@cosmicdrift/kumiko-samples 0.386.0 → 0.388.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 (530) hide show
  1. package/LICENSE +21 -57
  2. package/README.md +1 -1
  3. package/package.json +2 -2
  4. package/packages/bundled-features/package.json +9 -2
  5. package/packages/bundled-features/src/agent-tools/changes.json +6 -0
  6. package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
  7. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +9 -9
  8. package/packages/bundled-features/src/audit/changes.json +12 -0
  9. package/packages/bundled-features/src/audit/handlers/audit-events.ts +1 -1
  10. package/packages/bundled-features/src/audit/screens.ts +1 -1
  11. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
  12. package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
  13. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
  14. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
  16. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
  17. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
  18. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
  19. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
  20. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
  21. package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
  22. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
  23. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
  24. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
  25. package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
  26. package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
  27. package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
  28. package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
  29. package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
  30. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
  31. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
  32. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
  33. package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
  34. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
  35. package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
  36. package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
  37. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
  38. package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
  39. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
  40. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
  41. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
  42. package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
  43. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
  44. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
  45. package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
  46. package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
  47. package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
  48. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
  49. package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
  50. package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
  51. package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
  52. package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
  53. package/packages/bundled-features/src/auth-mfa/feature-exports.ts +2 -2
  54. package/packages/bundled-features/src/auth-mfa/feature.ts +6 -0
  55. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
  56. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
  57. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
  58. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  59. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
  60. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
  61. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
  62. package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
  63. package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
  64. package/packages/bundled-features/src/auth-mfa/web/i18n.ts +9 -4
  65. package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
  66. package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
  67. package/packages/bundled-features/src/auth-mfa/web/mfa-code-or-recovery-field.tsx +68 -0
  68. package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +23 -13
  69. package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +23 -13
  70. package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +15 -6
  71. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +71 -24
  72. package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
  73. package/packages/bundled-features/src/billing-foundation/changes.json +55 -0
  74. package/packages/bundled-features/src/billing-foundation/checkout-core.ts +32 -42
  75. package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +8 -2
  76. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +2 -0
  77. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +14 -3
  78. package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +3 -4
  79. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +12 -6
  80. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +35 -19
  81. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +12 -8
  82. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +14 -8
  83. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +5 -0
  84. package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +8 -10
  85. package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
  86. package/packages/bundled-features/src/billing-foundation/events.ts +23 -21
  87. package/packages/bundled-features/src/billing-foundation/feature.ts +45 -44
  88. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
  89. package/packages/bundled-features/src/billing-foundation/handlers/billing-plans.query.ts +71 -6
  90. package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +19 -17
  91. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
  92. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
  93. package/packages/bundled-features/src/billing-foundation/handlers/plan-switch-preview.query.ts +65 -0
  94. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +29 -22
  95. package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +44 -8
  96. package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +72 -82
  97. package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +14 -1
  98. package/packages/bundled-features/src/billing-foundation/i18n.ts +22 -0
  99. package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
  100. package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +62 -27
  101. package/packages/bundled-features/src/billing-foundation/plan-switch.ts +84 -0
  102. package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
  103. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
  104. package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
  105. package/packages/bundled-features/src/billing-foundation/types.ts +144 -66
  106. package/packages/bundled-features/src/billing-foundation/validate-options.ts +19 -0
  107. package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +123 -51
  108. package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +89 -42
  109. package/packages/bundled-features/src/billing-foundation/web/constants.ts +4 -0
  110. package/packages/bundled-features/src/billing-foundation/web/plan-price.ts +55 -0
  111. package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +224 -0
  112. package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
  113. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
  114. package/packages/bundled-features/src/cap-counter/cap-reservation.ts +5 -1
  115. package/packages/bundled-features/src/cap-counter/changes.json +18 -0
  116. package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
  117. package/packages/bundled-features/src/cap-counter/enforce-cap.ts +66 -68
  118. package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
  119. package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
  120. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
  121. package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
  122. package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -2
  123. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
  124. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
  125. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  126. package/packages/bundled-features/src/channel-email/changes.json +6 -0
  127. package/packages/bundled-features/src/channel-email/feature.ts +1 -1
  128. package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
  129. package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
  130. package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
  131. package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
  132. package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
  133. package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
  134. package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
  135. package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
  136. package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
  137. package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
  138. package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
  139. package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
  140. package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
  141. package/packages/bundled-features/src/config/changes.json +12 -0
  142. package/packages/bundled-features/src/config/handlers/cascade.query.ts +4 -2
  143. package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
  144. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
  145. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  146. package/packages/bundled-features/src/config/handlers/values.query.ts +4 -2
  147. package/packages/bundled-features/src/config/resolver.ts +4 -4
  148. package/packages/bundled-features/src/config/table.ts +10 -10
  149. package/packages/bundled-features/src/config/write-helpers.ts +28 -9
  150. package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
  151. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +23 -21
  152. package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
  153. package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
  154. package/packages/bundled-features/src/custom-fields/events.ts +7 -8
  155. package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
  156. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
  157. package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
  158. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
  159. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
  160. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
  161. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
  162. package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
  163. package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
  164. package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
  165. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
  166. package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
  167. package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
  168. package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
  169. package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
  170. package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
  171. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
  172. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
  173. package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
  174. package/packages/bundled-features/src/data-retention/feature.ts +11 -18
  175. package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
  176. package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
  177. package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
  178. package/packages/bundled-features/src/data-retention/presets.ts +18 -23
  179. package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
  180. package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
  181. package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
  182. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
  183. package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
  184. package/packages/bundled-features/src/delivery/changes.json +30 -0
  185. package/packages/bundled-features/src/delivery/channel-context.ts +9 -3
  186. package/packages/bundled-features/src/delivery/db/queries/preferences.ts +2 -2
  187. package/packages/bundled-features/src/delivery/delivery-service.ts +35 -5
  188. package/packages/bundled-features/src/delivery/feature.ts +3 -3
  189. package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
  190. package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +20 -1
  191. package/packages/bundled-features/src/delivery/handlers/unsubscribe-page-chrome.query.ts +1 -1
  192. package/packages/bundled-features/src/delivery/tables.ts +9 -8
  193. package/packages/bundled-features/src/delivery/unsubscribe-link.ts +3 -1
  194. package/packages/bundled-features/src/delivery/unsubscribe-page-strings.ts +1 -0
  195. package/packages/bundled-features/src/delivery/unsubscribe-page.ts +6 -0
  196. package/packages/bundled-features/src/delivery/unsubscribe.ts +40 -19
  197. package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
  198. package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
  199. package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
  200. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
  201. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
  202. package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
  203. package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
  204. package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
  205. package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
  206. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
  207. package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
  208. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  209. package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
  210. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  211. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  212. package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
  213. package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
  214. package/packages/bundled-features/src/file-foundation/index.ts +1 -0
  215. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -1
  216. package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
  217. package/packages/bundled-features/src/file-provider-s3/env-named-feature.ts +0 -2
  218. package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
  219. package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
  220. package/packages/bundled-features/src/files/README.md +1 -1
  221. package/packages/bundled-features/src/files/feature.ts +6 -6
  222. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
  223. package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
  224. package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
  225. package/packages/bundled-features/src/folders/feature.ts +1 -1
  226. package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
  227. package/packages/bundled-features/src/folders/web/tree.ts +5 -5
  228. package/packages/bundled-features/src/form-draft/changes.json +6 -0
  229. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +3 -3
  230. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
  231. package/packages/bundled-features/src/form-draft/decrypt-draft.ts +4 -1
  232. package/packages/bundled-features/src/form-draft/entity.ts +3 -3
  233. package/packages/bundled-features/src/form-draft/feature.ts +4 -5
  234. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
  235. package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
  236. package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
  237. package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
  238. package/packages/bundled-features/src/host-routing/README.md +1 -1
  239. package/packages/bundled-features/src/host-routing/changes.json +6 -0
  240. package/packages/bundled-features/src/host-routing/dispatch.ts +16 -17
  241. package/packages/bundled-features/src/host-routing/options.ts +2 -1
  242. package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
  243. package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
  244. package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
  245. package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
  246. package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
  247. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
  248. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
  249. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
  250. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
  251. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
  252. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
  253. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
  254. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
  255. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
  256. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +27 -15
  257. package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +0 -1
  258. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
  259. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
  260. package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
  261. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
  262. package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +0 -3
  263. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
  264. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +70 -70
  265. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +101 -84
  266. package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
  267. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
  268. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
  269. package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
  270. package/packages/bundled-features/src/jobs/changes.json +13 -0
  271. package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
  272. package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
  273. package/packages/bundled-features/src/jobs/events.ts +1 -1
  274. package/packages/bundled-features/src/jobs/feature.ts +20 -11
  275. package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
  276. package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
  277. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  278. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
  279. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  280. package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +45 -40
  281. package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +63 -59
  282. package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
  283. package/packages/bundled-features/src/jobs/job-run-logger.ts +19 -19
  284. package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
  285. package/packages/bundled-features/src/jobs/screens.ts +1 -1
  286. package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
  287. package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
  288. package/packages/bundled-features/src/ledger/entity.ts +5 -5
  289. package/packages/bundled-features/src/ledger/feature.ts +3 -3
  290. package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
  291. package/packages/bundled-features/src/ledger/recurring.ts +12 -12
  292. package/packages/bundled-features/src/ledger/schemas.ts +1 -1
  293. package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
  294. package/packages/bundled-features/src/ledger/web/index.ts +7 -7
  295. package/packages/bundled-features/src/legal-pages/README.md +1 -1
  296. package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
  297. package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
  298. package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
  299. package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
  300. package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
  301. package/packages/bundled-features/src/mail-foundation/feature.ts +31 -30
  302. package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
  303. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
  304. package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
  305. package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
  306. package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
  307. package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
  308. package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
  309. package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
  310. package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
  311. package/packages/bundled-features/src/managed-pages/table.ts +16 -15
  312. package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
  313. package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
  314. package/packages/bundled-features/src/notes-history/entity.ts +4 -4
  315. package/packages/bundled-features/src/notes-history/feature.ts +2 -2
  316. package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
  317. package/packages/bundled-features/src/page-render/layout.ts +9 -9
  318. package/packages/bundled-features/src/page-render/markdown.ts +11 -11
  319. package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
  320. package/packages/bundled-features/src/personal-access-tokens/feature.ts +9 -6
  321. package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
  322. package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
  323. package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
  324. package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
  325. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  326. package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
  327. package/packages/bundled-features/src/renderer-pdf/changes.json +6 -0
  328. package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +27 -10
  329. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  330. package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
  331. package/packages/bundled-features/src/seo/feature.ts +3 -3
  332. package/packages/bundled-features/src/sessions/constants.ts +1 -1
  333. package/packages/bundled-features/src/sessions/feature.ts +16 -11
  334. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
  335. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
  336. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  337. package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
  338. package/packages/bundled-features/src/sessions/screens.ts +2 -2
  339. package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
  340. package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
  341. package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
  342. package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +11 -1
  343. package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
  344. package/packages/bundled-features/src/shared/index.ts +1 -1
  345. package/packages/bundled-features/src/shared/mail-template-body.ts +10 -3
  346. package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
  347. package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
  348. package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
  349. package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
  350. package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
  351. package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
  352. package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -0
  353. package/packages/bundled-features/src/subscription-dodo/changes.json +14 -0
  354. package/packages/bundled-features/src/subscription-dodo/client.ts +45 -0
  355. package/packages/bundled-features/src/subscription-dodo/constants.ts +24 -0
  356. package/packages/bundled-features/src/subscription-dodo/feature.ts +207 -0
  357. package/packages/bundled-features/src/subscription-dodo/index.ts +16 -0
  358. package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +345 -0
  359. package/packages/bundled-features/src/subscription-dodo/runtime.ts +126 -0
  360. package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +262 -0
  361. package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
  362. package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
  363. package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
  364. package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
  365. package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
  366. package/packages/bundled-features/src/subscription-stripe/changes.json +12 -0
  367. package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
  368. package/packages/bundled-features/src/subscription-stripe/feature.ts +45 -43
  369. package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
  370. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
  371. package/packages/bundled-features/src/subscription-stripe/runtime.ts +9 -8
  372. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
  373. package/packages/bundled-features/src/tags/feature.ts +3 -3
  374. package/packages/bundled-features/src/tags/screens.ts +1 -1
  375. package/packages/bundled-features/src/template-resolver/api.ts +41 -14
  376. package/packages/bundled-features/src/template-resolver/changes.json +48 -0
  377. package/packages/bundled-features/src/template-resolver/compile-document-body.ts +1 -0
  378. package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
  379. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +3 -2
  380. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +27 -5
  381. package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +3 -2
  382. package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +3 -2
  383. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +8 -7
  384. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -20
  385. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +15 -5
  386. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
  387. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
  388. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-counts.query.ts +9 -5
  389. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +8 -6
  390. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +44 -5
  391. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
  392. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
  393. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +37 -26
  394. package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +7 -2
  395. package/packages/bundled-features/src/template-resolver/seeding.ts +36 -18
  396. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +22 -4
  397. package/packages/bundled-features/src/template-resolver/table.ts +13 -14
  398. package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +0 -11
  399. package/packages/bundled-features/src/template-resolver/template-meta.ts +12 -4
  400. package/packages/bundled-features/src/template-resolver/template-preview.ts +11 -3
  401. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
  402. package/packages/bundled-features/src/template-resolver/web/constants.ts +7 -5
  403. package/packages/bundled-features/src/template-resolver/web/index.ts +7 -0
  404. package/packages/bundled-features/src/template-resolver/web/mail-layout-constants.ts +8 -0
  405. package/packages/bundled-features/src/tenant/changes.json +6 -0
  406. package/packages/bundled-features/src/tenant/feature.ts +7 -7
  407. package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +10 -4
  408. package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
  409. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  410. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
  411. package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +8 -5
  412. package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
  413. package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
  414. package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
  415. package/packages/bundled-features/src/tenant/screens.ts +1 -1
  416. package/packages/bundled-features/src/tenant/seeding.ts +19 -18
  417. package/packages/bundled-features/src/tenant/testing.ts +5 -6
  418. package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
  419. package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
  420. package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
  421. package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
  422. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  423. package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
  424. package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
  425. package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
  426. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  427. package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
  428. package/packages/bundled-features/src/tenant-lifecycle/constants.ts +5 -0
  429. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
  430. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +7 -3
  431. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
  432. package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
  433. package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
  434. package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
  435. package/packages/bundled-features/src/tier-engine/changes.json +6 -0
  436. package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
  437. package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
  438. package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
  439. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
  440. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
  441. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +12 -9
  442. package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
  443. package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
  444. package/packages/bundled-features/src/tier-engine/web/constants.ts +3 -3
  445. package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
  446. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
  447. package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
  448. package/packages/bundled-features/src/user/schema/user.ts +45 -44
  449. package/packages/bundled-features/src/user/seeding.ts +40 -40
  450. package/packages/bundled-features/src/user/testing.ts +3 -3
  451. package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
  452. package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
  453. package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
  454. package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
  455. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
  456. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
  457. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
  458. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
  459. package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
  460. package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
  461. package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
  462. package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
  463. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
  464. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
  465. package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
  466. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
  467. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
  468. package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
  469. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
  470. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
  471. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +169 -280
  472. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +87 -108
  473. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
  474. package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
  475. package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
  476. package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
  477. package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
  478. package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
  479. package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
  480. package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
  481. package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
  482. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
  483. package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
  484. package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
  485. package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
  486. package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
  487. package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
  488. package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
  489. package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
  490. package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
  491. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
  492. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
  493. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
  494. package/packages/bundled-features/src/user-profile/index.ts +3 -3
  495. package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
  496. package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
  497. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
  498. package/packages/bundled-features/src/waitlist/changes.json +18 -0
  499. package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +2 -1
  500. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +5 -5
  501. package/packages/bundled-features/src/waitlist/import.ts +5 -0
  502. package/packages/bundled-features/src/waitlist/legacy-migrate.ts +22 -3
  503. package/packages/bundled-features/src/waitlist/link-on-signup.ts +20 -10
  504. package/packages/bundled-features/src/waitlist/options.ts +3 -0
  505. package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
  506. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
  507. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
  508. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  509. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
  510. package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
  511. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
  512. package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
  513. package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
  514. package/samples/apps/styleguide/src/features/widgets/i18n.ts +11 -0
  515. package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +24 -0
  516. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +5 -0
  517. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +90 -15
  518. package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +16 -6
  519. package/samples/apps/use-all-bundled/feature-manifest.json +120 -3
  520. package/samples/apps/use-all-bundled/src/run-config.ts +9 -3
  521. package/samples/recipes/auth-mfa/README.md +5 -0
  522. package/samples/recipes/auth-mfa/package.json +5 -2
  523. package/samples/recipes/auth-mfa/src/web/confirmation-code-step.tsx +49 -0
  524. package/samples/recipes/auth-mfa/tsconfig.json +8 -0
  525. package/samples/recipes/env-composition/README.md +3 -1
  526. package/samples/recipes/env-composition/src/env.ts +2 -0
  527. package/samples/recipes/legal-pages/README.md +1 -1
  528. package/samples/recipes/rate-limiting/README.md +18 -0
  529. package/samples/recipes/rate-limiting/src/feature.ts +12 -0
  530. package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +0 -12
@@ -1,17 +1,16 @@
1
- // createInboundMailSupervisor — der langlebige Sync-Prozess der
2
- // Foundation: startet pro aktivem Account `plugin.watch()` (IMAP IDLE,
3
- // Push in Sekunden) mit Reconnect-Backoff und fährt zusätzlich den
4
- // periodischen Reconciliation-Poll (Default 5 min) über `plugin.fetch()`.
5
- // Dedup im ingest-Handler macht Watch/Poll-Überschneidung idempotent —
6
- // der Poll ist Korrektheits-Anker, watch nur Latenz-Optimierung.
1
+ // createInboundMailSupervisor — the long-lived Foundation sync process:
2
+ // starts `plugin.watch()` (IMAP IDLE, seconds-level push) for each active
3
+ // account with reconnect backoff, and also runs the periodic reconciliation
4
+ // poll (default: 5 min) through `plugin.fetch()`.
5
+ // Deduplication in the ingest handler makes watch/poll overlap idempotent:
6
+ // the poll is the correctness anchor, while watch only reduces latency.
7
7
  //
8
- // **Plan deviation (documented):** the plan had the poll as an `r.job`
9
- // cron trigger. At the time this was written, JobContext had no
10
- // dispatcher (verified against run-export-jobs.ts). It now does
11
- // (`ctx.write`/`ctx.queryAs`, job-runner.ts) — but converting the poll to
12
- // a cron job is a separate migration (job concurrency instead of this
13
- // dispatcher contract), not part of #1719, and stays untouched here. The
14
- // app owner still wires the supervisor in bin/server.ts:
8
+ // **Documented deviation:** the poll was originally intended as an `r.job`
9
+ // cron trigger. JobContext previously had no dispatcher (see
10
+ // run-export-jobs.ts), but now has one (`ctx.write`/`ctx.queryAs`,
11
+ // job-runner.ts). Converting the poll to a cron job is a separate migration
12
+ // because it changes the concurrency model, so this dispatcher contract
13
+ // remains in place. The app owner wires the supervisor in bin/server.ts:
15
14
  //
16
15
  // const supervisor = createInboundMailSupervisor({
17
16
  // providerCtx: { registry: deps.registry, secrets, metricsFor: deps.metricsFor },
@@ -20,29 +19,29 @@
20
19
  // deps.dispatchSystemWrite({ handlerQn, payload, tenantId: tenantId as TenantId }),
21
20
  // // Multi-worker deployments: share one DistributedLock (same Redis,
22
21
  // // same key prefix) across every worker process so only one of them
23
- // // holds the IMAP IDLE connection per account (#1719). Omit `lock`
22
+ // // holds the IMAP IDLE connection per account. Omit `lock`
24
23
  // // for a single-process deployment — every active account gets
25
- // // watched locally, same as before.
24
+ // // watched locally, as before.
26
25
  // lock: createDistributedLock(redis, `${RedisKeys.lock}inbound-mail:watch:`),
27
26
  // });
28
27
  // await supervisor.start();
29
28
  // // shutdown-hook: await supervisor.stop();
30
29
  //
31
- // **IDLE operational risk (plan §7.3):** long-lived sockets in-process.
32
- // Mitigated here via backoff-restart on onError, a clean stop() of every
33
- // watcher on shutdown, and the poll covering every gap.
30
+ // **IDLE operational risk:** long-lived sockets run in-process.
31
+ // Backoff restart on onError, clean shutdown of every watcher, and the poll
32
+ // covering every gap mitigate this risk.
34
33
  //
35
- // **Multi-worker coordination (#1719):** the reconciliation poll stays
36
- // deliberately N-fold — every worker polls every active account, which
37
- // is idempotent and cheap. Only `plugin.watch()` holds a long-lived
38
- // connection, and only one worker may hold it per account. With
39
- // `deps.lock`, `ensureWatcher` claims a TTL lease (`lock.acquire`) for
40
- // the account before connecting; a worker that doesn't get it leaves the
41
- // account to its current holder — the poll still covers it. The holder
42
- // renews the claim via heartbeat (`lock.renew`, every ttl/3) for as long
43
- // as the watcher state lives, including across reconnect backoff, not
44
- // only while the connection is open. Losing the claim (Redis outage, TTL
45
- // exceeded) tears down the local watcher instead of racing a new holder.
34
+ // **Multi-worker coordination:** the reconciliation poll remains deliberately
35
+ // N-fold: every worker polls every active account, which is idempotent and
36
+ // cheap. Only `plugin.watch()` holds a long-lived connection, and only one
37
+ // worker may hold it per account. With `deps.lock`, `ensureWatcher` claims a
38
+ // TTL lease (`lock.acquire`) before connecting; a worker that fails to claim
39
+ // it leaves the account to its current holder, while the poll still covers
40
+ // it. The holder renews the lease via heartbeat (`lock.renew`, every ttl/3)
41
+ // for as long as the watcher state exists, including reconnect backoff, not
42
+ // only while the connection is open. Losing the claim (for example through
43
+ // a Redis outage or expired TTL) tears down the local watcher instead of
44
+ // racing a new holder.
46
45
 
47
46
  import {
48
47
  configuredPiiSubjectKms,
@@ -85,18 +84,20 @@ const DEFAULT_MAX_MESSAGES_PER_POLL = 200;
85
84
  const DEFAULT_WATCH_BACKOFF_INITIAL_MS = 5_000;
86
85
  const DEFAULT_WATCH_BACKOFF_MAX_MS = 5 * 60 * 1000;
87
86
  const DEFAULT_WATCH_LEASE_TTL_SECONDS = 90;
88
- /** V1: ein Cursor pro Account (eine Mailbox-Inbox). Multi-Folder später
89
- * über weitere scopes ohne Schema-Änderung. */
87
+ /** One cursor per account (one mailbox inbox) for now; multi-folder support
88
+ * can add further scopes later without a schema change. */
90
89
  const CURSOR_SCOPE = "default";
91
90
 
91
+ const SEEN_LOOKUP_CHUNK_SIZE = 1000;
92
+
92
93
  export type InboundMailSupervisorDeps = {
93
- /** Slim-Context für Provider-Calls (registry Pflicht, secrets für
94
- * Credential-Reads der Provider). */
94
+ /** Slim context for provider calls (`registry` is required; `secrets` is
95
+ * used for provider credential reads). */
95
96
  readonly providerCtx: InboundMailContext;
96
- /** App-DB — direct reads auf read_mail_accounts (Account-Snapshot)
97
- * + direct writes auf store_mail_sync_cursors (unmanaged store). */
97
+ /** App database — direct reads from read_mail_accounts (account snapshot)
98
+ * and direct writes to store_mail_sync_cursors (unmanaged store). */
98
99
  readonly db: DbConnection;
99
- /** Standard-Dispatcher mit SystemUser — trägt ingest-message +
100
+ /** Standard dispatcher with SystemUser — carries ingest-message and
100
101
  * update-account. */
101
102
  readonly dispatchWrite: (args: {
102
103
  readonly handlerQn: string;
@@ -107,8 +108,8 @@ export type InboundMailSupervisorDeps = {
107
108
  readonly data?: unknown;
108
109
  readonly error?: unknown;
109
110
  }>;
110
- /** Persisted den raw MIME-Body (file-foundation) und liefert den
111
- * bodyRef fürs Event. Fehlt der Hook, läuft snippet-only-Mode
111
+ /** Persists the raw MIME body (file-foundation) and returns its bodyRef
112
+ * for the event. Without this hook, runs in snippet-only mode
112
113
  * (bodyRef = ""). */
113
114
  readonly storeBody?: (account: MailAccountRecord, msg: RawInboundMessage) => Promise<string>;
114
115
  readonly pollIntervalMs?: number;
@@ -120,7 +121,7 @@ export type InboundMailSupervisorDeps = {
120
121
  * before `plugin.watch()`, ensureWatcher claims a TTL lease for the
121
122
  * account via `lock.acquire` and renews it via `lock.renew` while the
122
123
  * watcher lives. Omit for single-process deployments — every active
123
- * account is watched locally, same as before #1719. */
124
+ * account is watched locally, same as before. */
124
125
  readonly lock?: DistributedLock;
125
126
  /** Access-token lifecycle for OAuth accounts. Defaults to a manager built
126
127
  * from `db` + `providerCtx.secrets`; pass the instance shared with
@@ -136,8 +137,8 @@ type WatcherState = {
136
137
  stop: (() => Promise<void>) | null;
137
138
  backoffMs: number;
138
139
  restartTimer: ReturnType<typeof setTimeout> | null;
139
- /** Bump beim stop() — verhindert dass ein nachzügelnder Restart einen
140
- * bereits gestoppten Watcher wiederbelebt. */
140
+ /** Incremented by stop() to prevent a delayed restart from reviving
141
+ * an already stopped watcher. */
141
142
  generation: number;
142
143
  /** Token from `deps.lock.acquire` while this worker owns the account's
143
144
  * watch lease; null when unclaimed (no `deps.lock`, or claim lost). */
@@ -153,8 +154,8 @@ type WatchLeaseResult = "acquired" | "no-token" | "stale";
153
154
 
154
155
  export type InboundMailSupervisor = {
155
156
  readonly start: () => Promise<void>;
156
- /** Ein Reconciliation-Durchlauf über alle aktiven Accounts — auch
157
- * standalone nutzbar (Tests, manueller Ops-Trigger). */
157
+ /** Reconciliation run across all active accounts; also usable standalone
158
+ * for tests and manual operational triggers. */
158
159
  readonly pollOnce: () => Promise<void>;
159
160
  readonly stop: () => Promise<void>;
160
161
  };
@@ -214,8 +215,8 @@ export function createInboundMailSupervisor(
214
215
  const watchers = new Map<string, WatcherState>();
215
216
 
216
217
  // ---------------------------------------------------------------
217
- // Account-Snapshot: aktive Accounts aller Tenants, address decrypted
218
- // (Provider arbeiten nie mit Ciphertext).
218
+ // Account snapshot: active accounts of all tenants with the address
219
+ // decrypted (providers never work with ciphertext).
219
220
  // ---------------------------------------------------------------
220
221
  async function listActiveAccounts(): Promise<readonly MailAccountRecord[]> {
221
222
  const rows = await selectMany<ActiveMailAccountRow>(deps.db, mailAccountsProjectionTable, {
@@ -224,11 +225,19 @@ export function createInboundMailSupervisor(
224
225
  const piiKms = configuredPiiSubjectKms();
225
226
  const records: MailAccountRecord[] = [];
226
227
  for (const row of rows) {
227
- const decrypted = piiKms
228
- ? await decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
229
- requestId: "inbound-mail-foundation:supervisor:list-accounts",
230
- })
231
- : row;
228
+ let decrypted: Record<string, unknown>;
229
+ try {
230
+ decrypted = piiKms
231
+ ? await decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
232
+ requestId: "inbound-mail-foundation:supervisor:list-accounts",
233
+ })
234
+ : row;
235
+ } catch (error) {
236
+ // Class name only: the message may carry ciphertext or key details.
237
+ const errorName = error instanceof Error ? error.name : "unknown error";
238
+ log(`inbound-mail: account ${row.id} skipped — address did not decrypt (${errorName})`);
239
+ continue;
240
+ }
232
241
  const decryptedAddress = decrypted["address"];
233
242
  // Falling back to row.address would hand the ciphertext to a provider.
234
243
  // One undecryptable account must not stop polling of every other tenant's accounts.
@@ -264,7 +273,7 @@ export function createInboundMailSupervisor(
264
273
  const parsed: unknown = JSON.parse(row.cursor);
265
274
  return isPlainObject(parsed) ? parsed : null;
266
275
  } catch {
267
- return null; // korrupter Cursor = wie kein Cursor → Backfill, Dedup fängt Dubletten
276
+ return null; // a corrupt cursor counts as none → backfill; dedup catches duplicates
268
277
  }
269
278
  }
270
279
 
@@ -286,7 +295,7 @@ export function createInboundMailSupervisor(
286
295
  { cursor: serialized, updatedAt: now },
287
296
  { accountId, scope: CURSOR_SCOPE },
288
297
  );
289
- // skip: update-Pfad fertig — nicht in den insert-Zweig durchfallen.
298
+ // skip: the update path is complete; do not fall through to the insert branch.
290
299
  return;
291
300
  }
292
301
  await insertOne(deps.db, syncCursorTable, {
@@ -301,8 +310,8 @@ export function createInboundMailSupervisor(
301
310
  }
302
311
 
303
312
  async function resetCursor(accountId: string): Promise<void> {
304
- // Kein deleteMany-Import nötig: leerer Cursor-String parsed zu null
305
- // → nächster fetch läuft als Backfill.
313
+ // No deleteMany import is needed: an empty cursor parses to null, so the
314
+ // next fetch runs as a backfill.
306
315
  await updateMany(
307
316
  deps.db,
308
317
  syncCursorTable,
@@ -312,7 +321,7 @@ export function createInboundMailSupervisor(
312
321
  }
313
322
 
314
323
  // ---------------------------------------------------------------
315
- // Ingest — jede Message durch den Standard-Write-Handler.
324
+ // Ingest: every message goes through the standard write handler.
316
325
  // ---------------------------------------------------------------
317
326
  async function ingestBatch(
318
327
  account: MailAccountRecord,
@@ -344,9 +353,9 @@ export function createInboundMailSupervisor(
344
353
  },
345
354
  });
346
355
  if (!result.isSuccess) {
347
- // Einzel-Message-Fehler bricht den Batch: Cursor wird NICHT
348
- // persistiert → nächster Tick re-fetcht ab altem Cursor, Dedup
349
- // überspringt die bereits verarbeiteten.
356
+ // A single-message failure aborts the batch without persisting the cursor.
357
+ // The next tick refetches from the old cursor, and deduplication skips
358
+ // messages that were already processed.
350
359
  throw new Error(
351
360
  `ingest-message failed for account ${account.id}: ${JSON.stringify(result.error ?? {})}`,
352
361
  );
@@ -367,8 +376,8 @@ export function createInboundMailSupervisor(
367
376
  }
368
377
 
369
378
  // ---------------------------------------------------------------
370
- // Fehler-Semantik (Plan §2-Tabelle) — geteilt von Poll und Watch.
371
- // Liefert true wenn der Account weiterlaufen darf.
379
+ // Error semantics, shared by poll and watch. Returns true when the
380
+ // account may keep running.
372
381
  // ---------------------------------------------------------------
373
382
  async function handleSyncError(account: MailAccountRecord, err: unknown): Promise<boolean> {
374
383
  if (isInboundAuthError(err)) {
@@ -383,14 +392,14 @@ export function createInboundMailSupervisor(
383
392
  }
384
393
  if (isInboundRateLimitError(err)) {
385
394
  log(`inbound-mail: account ${account.id} rate-limited (retryAfter ${err.retryAfterMs}ms)`);
386
- return true; // nächster Tick versucht es erneut
395
+ return true; // The next tick retries it
387
396
  }
388
397
  if (isInboundCursorInvalidError(err)) {
389
398
  log(`inbound-mail: account ${account.id} cursor invalid — full resync in backfill window`);
390
399
  await resetCursor(account.id);
391
400
  return true;
392
401
  }
393
- // Transient/unbekannt: loggen, nächster Tick retried.
402
+ // Transient or unknown error: log it and retry on the next tick.
394
403
  log(`inbound-mail: account ${account.id} sync error: ${describeError(err)}`);
395
404
  return true;
396
405
  }
@@ -401,17 +410,23 @@ export function createInboundMailSupervisor(
401
410
  function unknownMessageIdsFilterFor(account: MailAccountRecord) {
402
411
  return async (providerMessageIds: readonly string[]): Promise<ReadonlySet<string>> => {
403
412
  if (providerMessageIds.length === 0) return new Set();
404
- const seen = await selectMany<{ providerMessageId: string }>(
405
- deps.db,
406
- seenMessageTable,
407
- {
408
- tenantId: account.tenantId,
409
- accountId: account.id,
410
- providerMessageId: { in: providerMessageIds },
411
- },
412
- { fields: ["providerMessageId"] },
413
- );
414
- const seenIds = new Set(seen.map((row) => row.providerMessageId));
413
+ // One bind parameter per id: a full-mailbox listing would exceed Postgres' 65535 limit.
414
+ const seenIds = new Set<string>();
415
+ for (let start = 0; start < providerMessageIds.length; start += SEEN_LOOKUP_CHUNK_SIZE) {
416
+ const seen = await selectMany<{ providerMessageId: string }>(
417
+ deps.db,
418
+ seenMessageTable,
419
+ {
420
+ tenantId: account.tenantId,
421
+ accountId: account.id,
422
+ providerMessageId: {
423
+ in: providerMessageIds.slice(start, start + SEEN_LOOKUP_CHUNK_SIZE),
424
+ },
425
+ },
426
+ { fields: ["providerMessageId"] },
427
+ );
428
+ for (const row of seen) seenIds.add(row.providerMessageId);
429
+ }
415
430
  return new Set(providerMessageIds.filter((id) => !seenIds.has(id)));
416
431
  };
417
432
  }
@@ -423,7 +438,8 @@ export function createInboundMailSupervisor(
423
438
  let cursor = await loadCursor(account.id);
424
439
  let budget = maxMessagesPerPoll;
425
440
  try {
426
- // hasMore-Schleife: Pagination innerhalb eines Ticks bis Budget.
441
+ // hasMore loop: paginate within this tick until the message budget is
442
+ // exhausted.
427
443
  for (;;) {
428
444
  const accessToken = await resolveAccessToken(account, plugin);
429
445
  const result = await plugin.fetch(deps.providerCtx, account, cursor, {
@@ -456,7 +472,7 @@ export function createInboundMailSupervisor(
456
472
  await pollAccount(account, plugin);
457
473
  if (running) await ensureWatcher(account, plugin);
458
474
  }
459
- // Accounts die nicht mehr aktiv sind: Watcher abbauen.
475
+ // Remove watchers for accounts that are no longer active.
460
476
  const activeIds = new Set(accounts.map((a) => a.id));
461
477
  for (const accountId of watchers.keys()) {
462
478
  if (!activeIds.has(accountId)) await stopWatcher(accountId);
@@ -464,7 +480,7 @@ export function createInboundMailSupervisor(
464
480
  }
465
481
 
466
482
  // ---------------------------------------------------------------
467
- // Watch-Lifecycle mit Backoff-Restart.
483
+ // Watch lifecycle with backoff restart.
468
484
  // ---------------------------------------------------------------
469
485
  // Heartbeat for a held watch lease. Independent of the connection's own
470
486
  // lifecycle — it keeps renewing across reconnect backoff, not just while
@@ -538,7 +554,7 @@ export function createInboundMailSupervisor(
538
554
  }
539
555
  }
540
556
 
541
- // kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional (#2460)
557
+ // kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional
542
558
  async function acquireWatchLease(
543
559
  account: MailAccountRecord,
544
560
  state: WatcherState,
@@ -565,10 +581,10 @@ export function createInboundMailSupervisor(
565
581
  account: MailAccountRecord,
566
582
  plugin: InboundMailProviderPlugin,
567
583
  ): Promise<void> {
568
- // skip: Provider ohne Live-Push — der Reconciliation-Poll deckt den Account ab.
584
+ // skip: provider without live push; the reconciliation poll covers the account.
569
585
  if (!plugin.watch) return;
570
586
  const existing = watchers.get(account.id);
571
- // skip: Watcher läuft bereits, startet gerade, oder Restart ist geplant.
587
+ // skip: the watcher is already running, starting, or scheduled for restart.
572
588
  if (existing?.stop || existing?.restartTimer || existing?.starting) return;
573
589
 
574
590
  const state: WatcherState = existing ?? {
@@ -601,7 +617,8 @@ export function createInboundMailSupervisor(
601
617
  const generation = state.generation;
602
618
 
603
619
  const scheduleRestart = (err: unknown) => {
604
- // skip: Supervisor gestoppt oder Watcher-Generation gewechselt — Restart wäre ein Zombie.
620
+ // skip: the supervisor stopped or the watcher generation changed; a restart
621
+ // would create a zombie watcher.
605
622
  if (!running || state.generation !== generation) return;
606
623
  state.stop = null;
607
624
  const delay = state.backoffMs;
@@ -647,7 +664,7 @@ export function createInboundMailSupervisor(
647
664
  );
648
665
  if (state.generation !== generation || !running) {
649
666
  await stop();
650
- // skip: stop() kam während des Connects — Watcher wurde sofort wieder abgebaut.
667
+ // skip: stop() ran while connecting; the watcher was torn down right away.
651
668
  return;
652
669
  }
653
670
  state.stop = stop;
@@ -674,7 +691,7 @@ export function createInboundMailSupervisor(
674
691
 
675
692
  async function stopWatcher(accountId: string): Promise<void> {
676
693
  const state = watchers.get(accountId);
677
- // skip: kein Watcher-State für diesen Account — bereits abgebaut.
694
+ // skip: no watcher state for this account; it is already torn down.
678
695
  if (!state) return;
679
696
  state.generation += 1;
680
697
  if (state.restartTimer) {
@@ -715,7 +732,7 @@ export function createInboundMailSupervisor(
715
732
  // Lifecycle.
716
733
  // ---------------------------------------------------------------
717
734
  function scheduleNextPoll(): void {
718
- // skip: Supervisor gestoppt — keinen weiteren Poll-Tick planen.
735
+ // skip: the supervisor is stopped; do not schedule another poll tick.
719
736
  if (!running) return;
720
737
  pollTimer = setTimeout(() => {
721
738
  pollTimer = null;
@@ -730,7 +747,7 @@ export function createInboundMailSupervisor(
730
747
 
731
748
  return {
732
749
  async start() {
733
- // skip: bereits gestartet — start() ist idempotent.
750
+ // skip: already started; start() is idempotent.
734
751
  if (running) return;
735
752
  running = true;
736
753
  await pollOnce();
@@ -1,19 +1,19 @@
1
- // Connection-Dokument im per-Account-Secret-Slot
2
- // (inboundCredentialSecretKey(accountId), Plan §4).
1
+ // Connection document stored in the per-account secret slot
2
+ // (inboundCredentialSecretKey(accountId)).
3
3
  //
4
- // **Warum JSON im Secret statt Tenant-Config-Keys:** anders als
5
- // mail-transport-smtp (EIN SMTP pro Tenant) hat Inbound MEHRERE
6
- // Accounts pro Tenant mit verschiedenen Hosts — per-Tenant-Config-Keys
7
- // können das nicht tragen. Host/Port/User sind zusammen mit dem
8
- // Passwort ein zusammengehöriges Verbindungs-Dokument; der Slot ist
9
- // ohnehin encrypted-at-rest.
4
+ // **Why JSON in the secret instead of tenant config keys:** unlike
5
+ // mail-transport-smtp (one SMTP configuration per tenant), inbound mail
6
+ // supports multiple accounts per tenant with different hosts, which
7
+ // per-tenant config keys cannot represent. Host, port, and user belong
8
+ // with the password in one connection document; the slot is encrypted
9
+ // at rest.
10
10
  //
11
- // authMethod des Accounts bestimmt die Interpretation:
11
+ // The account's authMethod determines the interpretation:
12
12
  // password → { host, port, secure, user, password }
13
- // xoauth2 → { host, port, secure, user, accessToken } — V1-Fallback:
14
- // Token-Beschaffung/-Refresh gehört den OAuth-Providern
15
- // (M365/Gmail, Phase 4/5); der imap-Provider konsumiert
16
- // nur einen gültigen Access-Token aus dem Slot.
13
+ // xoauth2 → { host, port, secure, user, accessToken } — V1 fallback:
14
+ // OAuth providers (M365/Gmail) handle token acquisition and
15
+ // refresh; the imap provider only consumes a valid access
16
+ // token from the slot.
17
17
 
18
18
  import * as z from "zod";
19
19
 
@@ -1,27 +1,27 @@
1
1
  // kumiko-feature-version: 1
2
2
  //
3
- // inbound-provider-imap — IMAP-Implementierung der inbound-mail-
4
- // foundation Plugin-API. Deckt den DACH-Long-Tail (IONOS, Telekom,
5
- // Strato, GMX/Web.de, mailbox.org, Hetzner, all-inkl) via Passwort/
6
- // App-Passwort ab — komplett OHNE OAuth. XOAUTH2 als Fallback für
7
- // M365-/Gmail-Konten (Access-Token aus dem Secret-Slot; Token-Refresh
8
- // gehört den OAuth-Providern, Phase 4/5).
3
+ // inbound-provider-imap — IMAP implementation of the inbound-mail-
4
+ // foundation plugin API. Covers the DACH long tail (IONOS, Telekom,
5
+ // Strato, GMX/Web.de, mailbox.org, Hetzner, all-inkl) via password/
6
+ // app-password authentication, without OAuth. XOAUTH2 is available as
7
+ // a fallback for M365/Gmail accounts (access token from the secret slot;
8
+ // token refresh is handled by the OAuth providers).
9
9
  //
10
- // **Was diese Feature liefert:**
11
- // 1. Plugin-Registration via r.useExtension("inboundMailProvider",
10
+ // **What this feature provides:**
11
+ // 1. Plugin registration via r.useExtension("inboundMailProvider",
12
12
  // "imap", { verify, fetch, watch }).
13
- // 2. verify: connect+logout mit typisiertem Fehler-Mapping.
14
- // 3. fetch: inkrementeller Sync über UIDVALIDITY:lastUid-Cursor —
15
- // UIDVALIDITY-Wechsel wirft InboundCursorInvalidError (Foundation
16
- // resettet + resynct im Backfill-Fenster). Erst-Befüllung via
17
- // SEARCH since=backfillWindowDays.
18
- // 4. watch: IMAP IDLE via imapflow — 'exists'-Event → neue UIDs
19
- // fetchen → onMessages; Verbindungs-Tod → onError (Supervisor
20
- // restartet mit Backoff, der Provider reconnected NICHT selbst).
13
+ // 2. verify: connect+logout with typed error mapping.
14
+ // 3. fetch: incremental sync using a UIDVALIDITY:lastUid cursor —
15
+ // a UIDVALIDITY change throws InboundCursorInvalidError (the
16
+ // foundation resets and resyncs within the backfill window). Initial
17
+ // population uses SEARCH since=backfillWindowDays.
18
+ // 4. watch: IMAP IDLE via imapflow — the 'exists' event triggers fetching
19
+ // new UIDs and then onMessages; connection loss triggers onError (the
20
+ // supervisor restarts with backoff; the provider does not reconnect).
21
21
  //
22
- // Credentials: JSON-Dokument im per-Account-Secret-Slot
23
- // (credential-document.ts) — kein Tenant-Config, weil mehrere Accounts
24
- // pro Tenant verschiedene Hosts haben.
22
+ // Credentials: JSON document in the per-account secret slot
23
+ // (credential-document.ts), not tenant configuration, because one tenant
24
+ // can have multiple accounts with different hosts.
25
25
 
26
26
  import { mailAllowedPrivateHostsEnvSchema } from "@cosmicdrift/kumiko-bundled-features/foundation-shared";
27
27
  import {
@@ -102,17 +102,17 @@ async function fetchMessages(
102
102
  const parsedCursor = parseImapCursor(cursor);
103
103
  assertUidValidity(parsedCursor, serverUidValidity);
104
104
 
105
- // UID-Menge bestimmen: inkrementell ab lastUid+1, sonst Backfill
106
- // über SEARCH since (Fenster begrenzt die Erst-Befüllung).
105
+ // Determine the UID set: incrementally from lastUid+1, or use a backfill
106
+ // via SEARCH since (the window limits the initial population).
107
107
  let uids: number[];
108
108
  if (parsedCursor) {
109
109
  const found = await client.search({ uid: `${parsedCursor.lastUid + 1}:*` }, { uid: true });
110
- // `N:*`-Gotcha: liegt N über der höchsten UID, liefert IMAP
111
- // trotzdem die letzte Message — rausfiltern.
110
+ // `N:*` gotcha: when N exceeds the highest UID, IMAP still returns the
111
+ // last message, so filter it out.
112
112
  uids = (found || []).filter((uid) => uid > parsedCursor.lastUid);
113
113
  } else {
114
114
  const since = Temporal.Now.instant().subtract({ hours: opts.backfillWindowDays * 24 });
115
- // imapflow-API-Boundary: search() verlangt ein JS-Date.
115
+ // imapflow API boundary: search() requires a JS Date.
116
116
  uids = (await client.search({ since: instantToLegacyDate(since) }, { uid: true })) || [];
117
117
  }
118
118
  uids.sort((a, b) => a - b);
@@ -155,8 +155,8 @@ async function fetchMessages(
155
155
  }
156
156
 
157
157
  // =============================================================================
158
- // watch — IMAP IDLE (imapflow idlet automatisch bei offener Mailbox und
159
- // emittet 'exists' bei neuen Messages).
158
+ // watch — IMAP IDLE (imapflow idles automatically while the mailbox is open
159
+ // and emits 'exists' for new messages).
160
160
  // =============================================================================
161
161
 
162
162
  async function watchMailbox(
@@ -175,7 +175,8 @@ async function watchMailbox(
175
175
  await client.connect();
176
176
  const mailbox = (await client.mailboxOpen(IMAP_MAILBOX)) as MailboxObject;
177
177
  const uidValidity = String(mailbox.uidValidity);
178
- // uidNext = nächste zu vergebende UID — alles ab hier ist "neu".
178
+ // uidNext is the next UID to be assigned, so everything from here is
179
+ // "new".
179
180
  let nextUid = Number(mailbox.uidNext ?? 1);
180
181
 
181
182
  const drainNew = async () => {
@@ -186,7 +187,7 @@ async function watchMailbox(
186
187
  { uid: true, internalDate: true, source: true },
187
188
  { uid: true },
188
189
  )) {
189
- // `N:*`-Gotcha (siehe fetch): alte letzte Message rausfiltern.
190
+ // `N:*` gotcha (see fetch): filter out the previous last message.
190
191
  if (msg.uid < nextUid || !msg.source) continue;
191
192
  messages.push(
192
193
  await toRawInboundMessage({
@@ -203,14 +204,14 @@ async function watchMailbox(
203
204
  };
204
205
 
205
206
  client.on("exists", () => {
206
- // skip: Watcher gestoppt — exists-Nachzügler ignorieren.
207
+ // skip: ignore delayed exists events after the watcher has stopped.
207
208
  if (stopped) return;
208
209
  void drainNew().catch((err) => {
209
210
  if (!stopped) handlers.onError(mapImapError(err, doc.host));
210
211
  });
211
212
  });
212
213
  const die = (err: unknown) => {
213
- // skip: bereits gestoppt/gestorben — onError nur einmal feuern.
214
+ // skip: already stopped or terminated; fire onError only once.
214
215
  if (stopped) return;
215
216
  stopped = true;
216
217
  handlers.onError(mapImapError(err, doc.host));
@@ -218,17 +219,18 @@ async function watchMailbox(
218
219
  client.on("error", die);
219
220
  client.on("close", () => die(new Error("connection closed")));
220
221
 
221
- // IDLE-Loop: 'exists' feuert NUR während client.idle() läuft
222
- // (empirisch gegen greenmail verifiziert). Jedes zwischengeschobene
223
- // Kommando (drainNew-fetch) beendet idle() — die Loop re-idlet,
224
- // bis stop() die Verbindung schließt.
222
+ // IDLE loop: 'exists' fires ONLY while client.idle() is running
223
+ // (verified empirically against greenmail). Each intervening command
224
+ // (drainNew-fetch) ends idle(), so the loop re-enters idle() until
225
+ // stop() closes the connection.
225
226
  void (async () => {
226
227
  while (!stopped) {
227
228
  try {
228
229
  await client.idle();
229
230
  } catch (err) {
230
231
  if (!stopped) die(err);
231
- // skip: idle-Loop endet — die() hat den Supervisor informiert (bzw. stop() war die Ursache).
232
+ // skip: the idle loop ends; die() has informed the supervisor (or stop()
233
+ // was the cause).
232
234
  return;
233
235
  }
234
236
  }
@@ -251,8 +253,8 @@ async function watchMailbox(
251
253
  // Plugin + Feature-definition
252
254
  // =============================================================================
253
255
 
254
- /** Exportiert für den Live-Test (imap-live.integration.test.ts) —
255
- * Apps nutzen das Feature, nie das Plugin direkt. */
256
+ /** Exported for the live test (imap-live.integration.test.ts) —
257
+ * applications use the feature, never the plugin directly. */
256
258
  export const imapInboundMailPlugin: InboundMailProviderPlugin = {
257
259
  verify: async (ctx, account) => {
258
260
  const doc = await readCredentialDocument(ctx, account);
@@ -267,9 +269,9 @@ export const imapInboundMailPlugin: InboundMailProviderPlugin = {
267
269
  },
268
270
  fetch: fetchMessages,
269
271
  watch: watchMailbox,
270
- // Kein oauth-Block: Passwort-Connect läuft über das Credential-
271
- // Formular (connect-account + Secret-Write); XOAUTH2-Token-Flows
272
- // gehören den OAuth-Providern (M365/Gmail).
272
+ // No oauth block: password authentication runs through the credential
273
+ // form (connect-account + Secret-Write); XOAUTH2 token flows belong to
274
+ // the OAuth providers (M365/Gmail).
273
275
  };
274
276
 
275
277
  export function createInboundProviderImapFeature() {