@cosmicdrift/kumiko-samples 0.385.0 → 0.387.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 (395) 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 +2 -2
  5. package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
  6. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +0 -2
  7. package/packages/bundled-features/src/audit/screens.ts +1 -1
  8. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
  9. package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
  10. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
  11. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  12. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
  13. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
  14. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
  16. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
  17. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
  18. package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
  19. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
  20. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
  21. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
  22. package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
  23. package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
  24. package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
  25. package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
  26. package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
  27. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
  28. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
  29. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
  30. package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
  31. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
  32. package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
  33. package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
  34. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
  35. package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
  36. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
  37. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
  38. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
  39. package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
  40. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
  41. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
  42. package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
  43. package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
  44. package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
  45. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
  46. package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
  47. package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
  48. package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
  49. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
  50. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
  51. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
  52. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  53. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
  54. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
  55. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
  56. package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
  57. package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
  58. package/packages/bundled-features/src/auth-mfa/web/i18n.ts +3 -3
  59. package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
  60. package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
  61. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +6 -6
  62. package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
  63. package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
  64. package/packages/bundled-features/src/billing-foundation/events.ts +22 -21
  65. package/packages/bundled-features/src/billing-foundation/feature.ts +42 -43
  66. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
  67. package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +18 -17
  68. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
  69. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
  70. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +20 -20
  71. package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
  72. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
  73. package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
  74. package/packages/bundled-features/src/billing-foundation/types.ts +64 -65
  75. package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +4 -4
  76. package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
  77. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
  78. package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
  79. package/packages/bundled-features/src/cap-counter/enforce-cap.ts +64 -67
  80. package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
  81. package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
  82. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
  83. package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
  84. package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +1 -1
  85. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
  86. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
  87. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  88. package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
  89. package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
  90. package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
  91. package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
  92. package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
  93. package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
  94. package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
  95. package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
  96. package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
  97. package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
  98. package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
  99. package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
  100. package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
  101. package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
  102. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
  103. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  104. package/packages/bundled-features/src/config/resolver.ts +4 -4
  105. package/packages/bundled-features/src/config/table.ts +10 -10
  106. package/packages/bundled-features/src/config/write-helpers.ts +1 -1
  107. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +13 -12
  108. package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
  109. package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
  110. package/packages/bundled-features/src/custom-fields/events.ts +7 -8
  111. package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
  112. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
  113. package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
  114. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
  115. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
  116. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
  117. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
  118. package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
  119. package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
  120. package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
  121. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
  122. package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
  123. package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
  124. package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
  125. package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
  126. package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
  127. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
  128. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
  129. package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
  130. package/packages/bundled-features/src/data-retention/feature.ts +11 -18
  131. package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
  132. package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
  133. package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
  134. package/packages/bundled-features/src/data-retention/presets.ts +18 -23
  135. package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
  136. package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
  137. package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
  138. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
  139. package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
  140. package/packages/bundled-features/src/delivery/feature.ts +3 -3
  141. package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
  142. package/packages/bundled-features/src/delivery/tables.ts +9 -8
  143. package/packages/bundled-features/src/delivery/unsubscribe.ts +4 -4
  144. package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
  145. package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
  146. package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
  147. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
  148. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
  149. package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
  150. package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
  151. package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
  152. package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
  153. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
  154. package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
  155. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  156. package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
  157. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  158. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  159. package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
  160. package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
  161. package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
  162. package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
  163. package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
  164. package/packages/bundled-features/src/files/feature.ts +6 -6
  165. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
  166. package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
  167. package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
  168. package/packages/bundled-features/src/folders/feature.ts +1 -1
  169. package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
  170. package/packages/bundled-features/src/folders/web/tree.ts +5 -5
  171. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +1 -1
  172. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
  173. package/packages/bundled-features/src/form-draft/entity.ts +3 -3
  174. package/packages/bundled-features/src/form-draft/feature.ts +4 -5
  175. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
  176. package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
  177. package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
  178. package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
  179. package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
  180. package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
  181. package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
  182. package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
  183. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
  184. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
  185. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
  186. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
  187. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
  188. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
  189. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
  190. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
  191. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
  192. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +11 -11
  193. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
  194. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
  195. package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
  196. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
  197. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
  198. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +66 -67
  199. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +69 -68
  200. package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
  201. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
  202. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
  203. package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
  204. package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
  205. package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
  206. package/packages/bundled-features/src/jobs/events.ts +1 -1
  207. package/packages/bundled-features/src/jobs/feature.ts +6 -7
  208. package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
  209. package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
  210. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  211. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
  212. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  213. package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
  214. package/packages/bundled-features/src/jobs/job-run-logger.ts +14 -15
  215. package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
  216. package/packages/bundled-features/src/jobs/screens.ts +1 -1
  217. package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
  218. package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
  219. package/packages/bundled-features/src/ledger/entity.ts +5 -5
  220. package/packages/bundled-features/src/ledger/feature.ts +3 -3
  221. package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
  222. package/packages/bundled-features/src/ledger/recurring.ts +12 -12
  223. package/packages/bundled-features/src/ledger/schemas.ts +1 -1
  224. package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
  225. package/packages/bundled-features/src/ledger/web/index.ts +7 -7
  226. package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
  227. package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
  228. package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
  229. package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
  230. package/packages/bundled-features/src/mail-foundation/feature.ts +26 -27
  231. package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
  232. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
  233. package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
  234. package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
  235. package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
  236. package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
  237. package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
  238. package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
  239. package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
  240. package/packages/bundled-features/src/managed-pages/table.ts +16 -15
  241. package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
  242. package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
  243. package/packages/bundled-features/src/notes-history/entity.ts +4 -4
  244. package/packages/bundled-features/src/notes-history/feature.ts +2 -2
  245. package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
  246. package/packages/bundled-features/src/page-render/layout.ts +9 -9
  247. package/packages/bundled-features/src/page-render/markdown.ts +11 -11
  248. package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
  249. package/packages/bundled-features/src/personal-access-tokens/feature.ts +5 -5
  250. package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
  251. package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
  252. package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
  253. package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
  254. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  255. package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
  256. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  257. package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
  258. package/packages/bundled-features/src/seo/feature.ts +3 -3
  259. package/packages/bundled-features/src/sessions/constants.ts +1 -1
  260. package/packages/bundled-features/src/sessions/feature.ts +9 -9
  261. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
  262. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
  263. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  264. package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
  265. package/packages/bundled-features/src/sessions/screens.ts +2 -2
  266. package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
  267. package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
  268. package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
  269. package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +1 -1
  270. package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
  271. package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
  272. package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
  273. package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
  274. package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
  275. package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
  276. package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
  277. package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
  278. package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
  279. package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
  280. package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
  281. package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
  282. package/packages/bundled-features/src/subscription-stripe/feature.ts +44 -43
  283. package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
  284. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
  285. package/packages/bundled-features/src/subscription-stripe/runtime.ts +2 -2
  286. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
  287. package/packages/bundled-features/src/tags/feature.ts +3 -3
  288. package/packages/bundled-features/src/tags/screens.ts +1 -1
  289. package/packages/bundled-features/src/template-resolver/api.ts +10 -10
  290. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +5 -5
  291. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +1 -1
  292. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
  293. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
  294. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
  295. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
  296. package/packages/bundled-features/src/template-resolver/seeding.ts +4 -4
  297. package/packages/bundled-features/src/template-resolver/table.ts +13 -14
  298. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
  299. package/packages/bundled-features/src/tenant/feature.ts +7 -7
  300. package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +1 -1
  301. package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
  302. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  303. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
  304. package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +1 -1
  305. package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
  306. package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
  307. package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
  308. package/packages/bundled-features/src/tenant/screens.ts +1 -1
  309. package/packages/bundled-features/src/tenant/seeding.ts +19 -18
  310. package/packages/bundled-features/src/tenant/testing.ts +5 -6
  311. package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
  312. package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
  313. package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
  314. package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
  315. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  316. package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
  317. package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
  318. package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
  319. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  320. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
  321. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +2 -2
  322. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
  323. package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
  324. package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
  325. package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
  326. package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
  327. package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
  328. package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
  329. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
  330. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
  331. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -8
  332. package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
  333. package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
  334. package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
  335. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
  336. package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
  337. package/packages/bundled-features/src/user/schema/user.ts +45 -44
  338. package/packages/bundled-features/src/user/seeding.ts +40 -40
  339. package/packages/bundled-features/src/user/testing.ts +3 -3
  340. package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
  341. package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
  342. package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
  343. package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
  344. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
  345. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
  346. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
  347. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
  348. package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
  349. package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
  350. package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
  351. package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
  352. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
  353. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
  354. package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
  355. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
  356. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
  357. package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
  358. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
  359. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
  360. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +166 -276
  361. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +85 -107
  362. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
  363. package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
  364. package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
  365. package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
  366. package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
  367. package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
  368. package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
  369. package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
  370. package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
  371. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
  372. package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
  373. package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
  374. package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
  375. package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
  376. package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
  377. package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
  378. package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
  379. package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
  380. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
  381. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
  382. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
  383. package/packages/bundled-features/src/user-profile/index.ts +3 -3
  384. package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
  385. package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
  386. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
  387. package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
  388. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
  389. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
  390. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  391. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
  392. package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
  393. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
  394. package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
  395. package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
@@ -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,18 @@ 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
 
92
91
  export type InboundMailSupervisorDeps = {
93
- /** Slim-Context für Provider-Calls (registry Pflicht, secrets für
94
- * Credential-Reads der Provider). */
92
+ /** Slim context for provider calls (`registry` is required; `secrets` is
93
+ * used for provider credential reads). */
95
94
  readonly providerCtx: InboundMailContext;
96
- /** App-DB — direct reads auf read_mail_accounts (Account-Snapshot)
97
- * + direct writes auf store_mail_sync_cursors (unmanaged store). */
95
+ /** App database — direct reads from read_mail_accounts (account snapshot)
96
+ * and direct writes to store_mail_sync_cursors (unmanaged store). */
98
97
  readonly db: DbConnection;
99
- /** Standard-Dispatcher mit SystemUser — trägt ingest-message +
98
+ /** Standard dispatcher with SystemUser — carries ingest-message and
100
99
  * update-account. */
101
100
  readonly dispatchWrite: (args: {
102
101
  readonly handlerQn: string;
@@ -107,8 +106,8 @@ export type InboundMailSupervisorDeps = {
107
106
  readonly data?: unknown;
108
107
  readonly error?: unknown;
109
108
  }>;
110
- /** Persisted den raw MIME-Body (file-foundation) und liefert den
111
- * bodyRef fürs Event. Fehlt der Hook, läuft snippet-only-Mode
109
+ /** Persists the raw MIME body (file-foundation) and returns its bodyRef
110
+ * for the event. Without this hook, runs in snippet-only mode
112
111
  * (bodyRef = ""). */
113
112
  readonly storeBody?: (account: MailAccountRecord, msg: RawInboundMessage) => Promise<string>;
114
113
  readonly pollIntervalMs?: number;
@@ -120,7 +119,7 @@ export type InboundMailSupervisorDeps = {
120
119
  * before `plugin.watch()`, ensureWatcher claims a TTL lease for the
121
120
  * account via `lock.acquire` and renews it via `lock.renew` while the
122
121
  * watcher lives. Omit for single-process deployments — every active
123
- * account is watched locally, same as before #1719. */
122
+ * account is watched locally, same as before. */
124
123
  readonly lock?: DistributedLock;
125
124
  /** Access-token lifecycle for OAuth accounts. Defaults to a manager built
126
125
  * from `db` + `providerCtx.secrets`; pass the instance shared with
@@ -136,8 +135,8 @@ type WatcherState = {
136
135
  stop: (() => Promise<void>) | null;
137
136
  backoffMs: number;
138
137
  restartTimer: ReturnType<typeof setTimeout> | null;
139
- /** Bump beim stop() — verhindert dass ein nachzügelnder Restart einen
140
- * bereits gestoppten Watcher wiederbelebt. */
138
+ /** Incremented by stop() to prevent a delayed restart from reviving
139
+ * an already stopped watcher. */
141
140
  generation: number;
142
141
  /** Token from `deps.lock.acquire` while this worker owns the account's
143
142
  * watch lease; null when unclaimed (no `deps.lock`, or claim lost). */
@@ -153,8 +152,8 @@ type WatchLeaseResult = "acquired" | "no-token" | "stale";
153
152
 
154
153
  export type InboundMailSupervisor = {
155
154
  readonly start: () => Promise<void>;
156
- /** Ein Reconciliation-Durchlauf über alle aktiven Accounts — auch
157
- * standalone nutzbar (Tests, manueller Ops-Trigger). */
155
+ /** Reconciliation run across all active accounts; also usable standalone
156
+ * for tests and manual operational triggers. */
158
157
  readonly pollOnce: () => Promise<void>;
159
158
  readonly stop: () => Promise<void>;
160
159
  };
@@ -214,8 +213,8 @@ export function createInboundMailSupervisor(
214
213
  const watchers = new Map<string, WatcherState>();
215
214
 
216
215
  // ---------------------------------------------------------------
217
- // Account-Snapshot: aktive Accounts aller Tenants, address decrypted
218
- // (Provider arbeiten nie mit Ciphertext).
216
+ // Account snapshot: active accounts of all tenants with the address
217
+ // decrypted (providers never work with ciphertext).
219
218
  // ---------------------------------------------------------------
220
219
  async function listActiveAccounts(): Promise<readonly MailAccountRecord[]> {
221
220
  const rows = await selectMany<ActiveMailAccountRow>(deps.db, mailAccountsProjectionTable, {
@@ -264,7 +263,7 @@ export function createInboundMailSupervisor(
264
263
  const parsed: unknown = JSON.parse(row.cursor);
265
264
  return isPlainObject(parsed) ? parsed : null;
266
265
  } catch {
267
- return null; // korrupter Cursor = wie kein Cursor → Backfill, Dedup fängt Dubletten
266
+ return null; // a corrupt cursor counts as none → backfill; dedup catches duplicates
268
267
  }
269
268
  }
270
269
 
@@ -286,7 +285,7 @@ export function createInboundMailSupervisor(
286
285
  { cursor: serialized, updatedAt: now },
287
286
  { accountId, scope: CURSOR_SCOPE },
288
287
  );
289
- // skip: update-Pfad fertig — nicht in den insert-Zweig durchfallen.
288
+ // skip: the update path is complete; do not fall through to the insert branch.
290
289
  return;
291
290
  }
292
291
  await insertOne(deps.db, syncCursorTable, {
@@ -301,8 +300,8 @@ export function createInboundMailSupervisor(
301
300
  }
302
301
 
303
302
  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.
303
+ // No deleteMany import is needed: an empty cursor parses to null, so the
304
+ // next fetch runs as a backfill.
306
305
  await updateMany(
307
306
  deps.db,
308
307
  syncCursorTable,
@@ -312,7 +311,7 @@ export function createInboundMailSupervisor(
312
311
  }
313
312
 
314
313
  // ---------------------------------------------------------------
315
- // Ingest — jede Message durch den Standard-Write-Handler.
314
+ // Ingest: every message goes through the standard write handler.
316
315
  // ---------------------------------------------------------------
317
316
  async function ingestBatch(
318
317
  account: MailAccountRecord,
@@ -344,9 +343,9 @@ export function createInboundMailSupervisor(
344
343
  },
345
344
  });
346
345
  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.
346
+ // A single-message failure aborts the batch without persisting the cursor.
347
+ // The next tick refetches from the old cursor, and deduplication skips
348
+ // messages that were already processed.
350
349
  throw new Error(
351
350
  `ingest-message failed for account ${account.id}: ${JSON.stringify(result.error ?? {})}`,
352
351
  );
@@ -367,8 +366,8 @@ export function createInboundMailSupervisor(
367
366
  }
368
367
 
369
368
  // ---------------------------------------------------------------
370
- // Fehler-Semantik (Plan §2-Tabelle) — geteilt von Poll und Watch.
371
- // Liefert true wenn der Account weiterlaufen darf.
369
+ // Error semantics, shared by poll and watch. Returns true when the
370
+ // account may keep running.
372
371
  // ---------------------------------------------------------------
373
372
  async function handleSyncError(account: MailAccountRecord, err: unknown): Promise<boolean> {
374
373
  if (isInboundAuthError(err)) {
@@ -383,14 +382,14 @@ export function createInboundMailSupervisor(
383
382
  }
384
383
  if (isInboundRateLimitError(err)) {
385
384
  log(`inbound-mail: account ${account.id} rate-limited (retryAfter ${err.retryAfterMs}ms)`);
386
- return true; // nächster Tick versucht es erneut
385
+ return true; // The next tick retries it
387
386
  }
388
387
  if (isInboundCursorInvalidError(err)) {
389
388
  log(`inbound-mail: account ${account.id} cursor invalid — full resync in backfill window`);
390
389
  await resetCursor(account.id);
391
390
  return true;
392
391
  }
393
- // Transient/unbekannt: loggen, nächster Tick retried.
392
+ // Transient or unknown error: log it and retry on the next tick.
394
393
  log(`inbound-mail: account ${account.id} sync error: ${describeError(err)}`);
395
394
  return true;
396
395
  }
@@ -423,7 +422,8 @@ export function createInboundMailSupervisor(
423
422
  let cursor = await loadCursor(account.id);
424
423
  let budget = maxMessagesPerPoll;
425
424
  try {
426
- // hasMore-Schleife: Pagination innerhalb eines Ticks bis Budget.
425
+ // hasMore loop: paginate within this tick until the message budget is
426
+ // exhausted.
427
427
  for (;;) {
428
428
  const accessToken = await resolveAccessToken(account, plugin);
429
429
  const result = await plugin.fetch(deps.providerCtx, account, cursor, {
@@ -456,7 +456,7 @@ export function createInboundMailSupervisor(
456
456
  await pollAccount(account, plugin);
457
457
  if (running) await ensureWatcher(account, plugin);
458
458
  }
459
- // Accounts die nicht mehr aktiv sind: Watcher abbauen.
459
+ // Remove watchers for accounts that are no longer active.
460
460
  const activeIds = new Set(accounts.map((a) => a.id));
461
461
  for (const accountId of watchers.keys()) {
462
462
  if (!activeIds.has(accountId)) await stopWatcher(accountId);
@@ -464,7 +464,7 @@ export function createInboundMailSupervisor(
464
464
  }
465
465
 
466
466
  // ---------------------------------------------------------------
467
- // Watch-Lifecycle mit Backoff-Restart.
467
+ // Watch lifecycle with backoff restart.
468
468
  // ---------------------------------------------------------------
469
469
  // Heartbeat for a held watch lease. Independent of the connection's own
470
470
  // lifecycle — it keeps renewing across reconnect backoff, not just while
@@ -538,7 +538,7 @@ export function createInboundMailSupervisor(
538
538
  }
539
539
  }
540
540
 
541
- // kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional (#2460)
541
+ // kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional
542
542
  async function acquireWatchLease(
543
543
  account: MailAccountRecord,
544
544
  state: WatcherState,
@@ -565,10 +565,10 @@ export function createInboundMailSupervisor(
565
565
  account: MailAccountRecord,
566
566
  plugin: InboundMailProviderPlugin,
567
567
  ): Promise<void> {
568
- // skip: Provider ohne Live-Push — der Reconciliation-Poll deckt den Account ab.
568
+ // skip: provider without live push; the reconciliation poll covers the account.
569
569
  if (!plugin.watch) return;
570
570
  const existing = watchers.get(account.id);
571
- // skip: Watcher läuft bereits, startet gerade, oder Restart ist geplant.
571
+ // skip: the watcher is already running, starting, or scheduled for restart.
572
572
  if (existing?.stop || existing?.restartTimer || existing?.starting) return;
573
573
 
574
574
  const state: WatcherState = existing ?? {
@@ -601,7 +601,8 @@ export function createInboundMailSupervisor(
601
601
  const generation = state.generation;
602
602
 
603
603
  const scheduleRestart = (err: unknown) => {
604
- // skip: Supervisor gestoppt oder Watcher-Generation gewechselt — Restart wäre ein Zombie.
604
+ // skip: the supervisor stopped or the watcher generation changed; a restart
605
+ // would create a zombie watcher.
605
606
  if (!running || state.generation !== generation) return;
606
607
  state.stop = null;
607
608
  const delay = state.backoffMs;
@@ -647,7 +648,7 @@ export function createInboundMailSupervisor(
647
648
  );
648
649
  if (state.generation !== generation || !running) {
649
650
  await stop();
650
- // skip: stop() kam während des Connects — Watcher wurde sofort wieder abgebaut.
651
+ // skip: stop() ran while connecting; the watcher was torn down right away.
651
652
  return;
652
653
  }
653
654
  state.stop = stop;
@@ -674,7 +675,7 @@ export function createInboundMailSupervisor(
674
675
 
675
676
  async function stopWatcher(accountId: string): Promise<void> {
676
677
  const state = watchers.get(accountId);
677
- // skip: kein Watcher-State für diesen Account — bereits abgebaut.
678
+ // skip: no watcher state for this account; it is already torn down.
678
679
  if (!state) return;
679
680
  state.generation += 1;
680
681
  if (state.restartTimer) {
@@ -715,7 +716,7 @@ export function createInboundMailSupervisor(
715
716
  // Lifecycle.
716
717
  // ---------------------------------------------------------------
717
718
  function scheduleNextPoll(): void {
718
- // skip: Supervisor gestoppt — keinen weiteren Poll-Tick planen.
719
+ // skip: the supervisor is stopped; do not schedule another poll tick.
719
720
  if (!running) return;
720
721
  pollTimer = setTimeout(() => {
721
722
  pollTimer = null;
@@ -730,7 +731,7 @@ export function createInboundMailSupervisor(
730
731
 
731
732
  return {
732
733
  async start() {
733
- // skip: bereits gestartet — start() ist idempotent.
734
+ // skip: already started; start() is idempotent.
734
735
  if (running) return;
735
736
  running = true;
736
737
  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() {
@@ -1,6 +1,6 @@
1
- // imapflow-Wiring + pure Helpers des IMAP-Providers.
2
- // Referenz-Impl: /Users/marc/code/doc-o-mat (HEINZ) — aber inkrementeller
3
- // UIDVALIDITY:lastUid-Cursor statt '1:*'-Vollscan.
1
+ // imapflow wiring and pure helpers for IMAP providers.
2
+ // Reference implementation: /Users/marc/code/doc-o-mat (HEINZ), but uses an
3
+ // incremental UIDVALIDITY:lastUid cursor instead of a '1:*' full scan.
4
4
 
5
5
  import {
6
6
  BlockedHostError,
@@ -95,9 +95,9 @@ export async function createImapClient(
95
95
 
96
96
  /** Server-Fehler → typisierte Foundation-Fehlerklassen (Plan §2). */
97
97
  export function mapImapError(err: unknown, host: string): Error {
98
- // imapflow-Fehler tragen die Server-Antwort in responseText, die
99
- // message ist nur "Command failed" — beides in den Match einbeziehen;
100
- // authenticationFailed ist das verlässliche Flag.
98
+ // imapflow errors carry the server response in responseText; the
99
+ // message is only "Command failed", so both are included in the match;
100
+ // authenticationFailed is the reliable flag.
101
101
  const carrier = isPlainObject(err) ? err : undefined;
102
102
  const responseText = typeof carrier?.["responseText"] === "string" ? carrier["responseText"] : "";
103
103
  const msg = `${err instanceof Error ? err.message : String(err)} ${responseText}`.trim();
@@ -129,9 +129,9 @@ export function parseImapCursor(cursor: SyncCursorPayload | null): ImapCursor |
129
129
  return { uidValidity, lastUid };
130
130
  }
131
131
 
132
- /** Wirft InboundCursorInvalidError bei UIDVALIDITY-Wechsel — die
133
- * Foundation resettet den Cursor und macht einen Voll-Resync im
134
- * Backfill-Fenster (Dedup fängt Dubletten). */
132
+ /** Throws InboundCursorInvalidError when UIDVALIDITY changes; the
133
+ * foundation resets the cursor and performs a full resync within the
134
+ * backfill window (deduplication catches duplicates). */
135
135
  export function assertUidValidity(cursor: ImapCursor | null, serverUidValidity: string): void {
136
136
  if (cursor && cursor.uidValidity !== serverUidValidity) {
137
137
  throw new InboundCursorInvalidError(
@@ -148,8 +148,9 @@ function stripAngles(id: string): string {
148
148
  return id.replace(/^<|>$/g, "").trim();
149
149
  }
150
150
 
151
- /** References-Chain normalisieren: mailparser liefert string | string[]
152
- * | undefined; fehlt References, trägt In-Reply-To den Thread-Anker. */
151
+ /** Normalizes the References chain: mailparser returns string | string[]
152
+ * | undefined; when References is missing, In-Reply-To provides the thread
153
+ * anchor. */
153
154
  export function normalizeReferences(parsed: {
154
155
  references?: string | string[];
155
156
  inReplyTo?: string;
@@ -170,16 +171,16 @@ function addressList(a: AddressObject | AddressObject[] | undefined): string[] {
170
171
  return arr.map((x) => x.text).filter((t): t is string => Boolean(t));
171
172
  }
172
173
 
173
- /** imapflow liefert internalDate je nach Codepfad als Date ODER string.
174
- * String-Form verwerfen wir bewusst (kein Date-Parsing im Feature-Code,
175
- * no-date-api) — toRawInboundMessage fällt dann auf mailparsers
176
- * Date-Header zurück, der immer als echtes Date kommt. */
174
+ /** imapflow provides internalDate as either a Date or string depending on
175
+ * the code path. The string form is deliberately discarded (no date parsing
176
+ * in feature code, no-date-api); toRawInboundMessage then falls back to the
177
+ * mailparser Date header, which is always a real Date. */
177
178
  export function coerceDate(d: Date | string | undefined): Date | undefined {
178
179
  return d instanceof Date ? d : undefined;
179
180
  }
180
181
 
181
- /** providerMessageId: UID ist nur innerhalb einer UIDVALIDITY eindeutig
182
- * — beides zusammen ist der stabile Idempotency-Anchor. */
182
+ /** providerMessageId: a UID is unique only within a UIDVALIDITY; together,
183
+ * they form the stable idempotency anchor. */
183
184
  export function buildProviderMessageId(uidValidity: string, uid: number): string {
184
185
  return `${uidValidity}:${uid}`;
185
186
  }
@@ -204,8 +205,8 @@ export async function toRawInboundMessage(args: {
204
205
  cc: addressList(parsed.cc),
205
206
  subject: parsed.subject ?? "",
206
207
  snippet: text.slice(0, SNIPPET_MAX),
207
- // Lib-Boundary-Bridge (Date von imapflow/mailparser) → Temporal;
208
- // fehlt jedes Datum, ist Epoch der ehrliche Marker.
208
+ // Lib-boundary bridge (Date from imapflow/mailparser) → Temporal;
209
+ // when no date exists, Epoch is the honest marker.
209
210
  receivedAtIso: (receivedAt
210
211
  ? legacyDateToInstant(receivedAt)
211
212
  : Temporal.Instant.fromEpochMilliseconds(0)