@cosmicdrift/kumiko-bundled-features 0.367.0 → 0.369.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 (359) hide show
  1. package/dist/audit/handlers/details.query.js +1 -1
  2. package/dist/audit/handlers/list.query.js +1 -1
  3. package/dist/audit/run-escape-hatch-retention.js +1 -1
  4. package/dist/auth-email-password/bootstrap.js +1 -1
  5. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +6 -3
  6. package/dist/auth-email-password/handlers/invite-accept.write.js +6 -3
  7. package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
  8. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +6 -3
  9. package/dist/auth-email-password/handlers/signup-confirm.write.js +1 -1
  10. package/dist/auth-email-password/seeding.js +2 -2
  11. package/dist/auth-foundation/feature.d.ts +4 -0
  12. package/dist/auth-foundation/feature.js +37 -33
  13. package/dist/auth-foundation/index.d.ts +1 -1
  14. package/dist/auth-foundation/index.js +1 -1
  15. package/dist/auth-mfa/db/queries.js +1 -2
  16. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +1 -1
  17. package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
  18. package/dist/auth-mfa/handlers/reencrypt.job.js +2 -3
  19. package/dist/auth-mfa/handlers/status.query.js +1 -1
  20. package/dist/auth-mfa/handlers/verify.write.js +1 -1
  21. package/dist/auth-mfa/mfa-code-verifier.js +1 -1
  22. package/dist/auth-mfa/mfa-status-checker.js +1 -1
  23. package/dist/auth-mfa-user-data/index.d.ts +4 -0
  24. package/dist/auth-mfa-user-data/index.js +13 -9
  25. package/dist/billing-foundation/billing-info-query.js +1 -1
  26. package/dist/billing-foundation/feature.d.ts +1 -0
  27. package/dist/billing-foundation/feature.js +5 -6
  28. package/dist/billing-foundation/handlers/create-portal-session.write.js +1 -1
  29. package/dist/billing-foundation/projection.js +1 -2
  30. package/dist/cap-counter/calendar-cap-usage-reservation.d.ts +7 -0
  31. package/dist/cap-counter/calendar-cap-usage-reservation.js +28 -2
  32. package/dist/cap-counter/cap-reservation.d.ts +16 -0
  33. package/dist/cap-counter/cap-reservation.js +144 -14
  34. package/dist/cap-counter/feature.d.ts +4 -0
  35. package/dist/cap-counter/feature.js +74 -68
  36. package/dist/cap-counter/handlers/increment-rolling.write.js +3 -4
  37. package/dist/cap-counter/index.d.ts +3 -2
  38. package/dist/cap-counter/index.js +3 -2
  39. package/dist/cap-counter/tables.js +10 -3
  40. package/dist/cap-overview/handlers/caps-usage.query.js +1 -2
  41. package/dist/cap-overview/handlers/tenant-caps-list.query.js +1 -2
  42. package/dist/cap-overview/handlers/tenant-options.query.js +1 -1
  43. package/dist/channel-email/email-channel.d.ts +13 -5
  44. package/dist/channel-email/email-channel.js +17 -3
  45. package/dist/channel-email/feature.js +1 -1
  46. package/dist/channel-email/index.d.ts +1 -1
  47. package/dist/channel-email/index.js +1 -1
  48. package/dist/channel-in-app/handlers/inbox.query.js +1 -1
  49. package/dist/channel-in-app/handlers/mark-all-read.write.js +1 -1
  50. package/dist/channel-in-app/handlers/mark-read.write.js +1 -1
  51. package/dist/channel-in-app/handlers/unread-count.query.js +1 -1
  52. package/dist/channel-in-app/tables.js +1 -2
  53. package/dist/compliance-profiles/handlers/for-tenant.query.js +1 -1
  54. package/dist/compliance-profiles/handlers/needs-profile.query.js +1 -1
  55. package/dist/compliance-profiles/handlers/set-profile.write.js +1 -2
  56. package/dist/compliance-profiles/resolve-for-tenant.js +1 -1
  57. package/dist/compliance-profiles/seeding.js +3 -5
  58. package/dist/compliance-profiles-ops/index.d.ts +8 -0
  59. package/dist/compliance-profiles-ops/index.js +19 -15
  60. package/dist/config/db/queries/resolver.js +1 -1
  61. package/dist/config/handlers/reencrypt.job.js +2 -2
  62. package/dist/config/resolver.js +1 -1
  63. package/dist/config/web/i18n.js +1 -2
  64. package/dist/config/write-helpers.js +1 -1
  65. package/dist/crypto-shredding/handlers/forget-subject.write.js +6 -3
  66. package/dist/custom-fields/db/queries/field-access.js +1 -1
  67. package/dist/custom-fields/db/queries/projection.js +1 -1
  68. package/dist/custom-fields/db/queries/quota.js +1 -1
  69. package/dist/custom-fields/db/queries/retention.js +1 -2
  70. package/dist/custom-fields/db/queries/user-data-rights.js +1 -1
  71. package/dist/custom-fields/feature.d.ts +20 -2
  72. package/dist/custom-fields/feature.js +9 -13
  73. package/dist/custom-fields/handlers/clear-custom-field.write.js +2 -2
  74. package/dist/custom-fields/handlers/delete-system-field.write.js +2 -2
  75. package/dist/custom-fields/handlers/delete-tenant-field.write.js +2 -2
  76. package/dist/custom-fields/handlers/set-custom-field.write.js +2 -2
  77. package/dist/custom-fields/wire-for-entity.js +4 -4
  78. package/dist/data-retention/resolve-for-tenant.js +1 -1
  79. package/dist/data-retention/run-retention-cleanup.js +17 -13
  80. package/dist/delivery/address-opt-out.js +1 -2
  81. package/dist/delivery/attempt-log-retention.js +1 -1
  82. package/dist/delivery/channel-context.js +1 -1
  83. package/dist/delivery/db/queries/address-opt-outs.js +1 -1
  84. package/dist/delivery/db/queries/preferences.js +1 -1
  85. package/dist/delivery/delivery-service.js +16 -7
  86. package/dist/delivery/feature.js +2 -2
  87. package/dist/delivery/handlers/log.query.js +2 -3
  88. package/dist/delivery/handlers/preferences.query.js +1 -1
  89. package/dist/delivery/index.d.ts +3 -2
  90. package/dist/delivery/index.js +3 -2
  91. package/dist/delivery/unsubscribe.d.ts +13 -0
  92. package/dist/delivery/unsubscribe.js +32 -27
  93. package/dist/delivery/upsert-preference.js +1 -2
  94. package/dist/derivatives-sharp/feature.d.ts +4 -0
  95. package/dist/derivatives-sharp/feature.js +15 -11
  96. package/dist/derivatives-sharp/index.d.ts +1 -1
  97. package/dist/derivatives-sharp/index.js +1 -1
  98. package/dist/document-ingest-foundation/feature.d.ts +7 -0
  99. package/dist/document-ingest-foundation/feature.js +94 -90
  100. package/dist/document-ingest-foundation/forget-extract-with-file-ref.js +3 -3
  101. package/dist/document-ingest-foundation/index.d.ts +1 -1
  102. package/dist/document-ingest-foundation/index.js +1 -1
  103. package/dist/feature-toggles/global-feature-state-table.js +1 -2
  104. package/dist/feature-toggles/handlers/set.write.js +3 -5
  105. package/dist/feature-toggles/toggle-runtime.js +1 -1
  106. package/dist/file-derivatives/feature.d.ts +1 -0
  107. package/dist/file-derivatives/feature.js +2 -1
  108. package/dist/file-derivatives/handlers/public-variant-by-file-ref.query.js +1 -1
  109. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  110. package/dist/file-foundation/feature.d.ts +6 -0
  111. package/dist/file-foundation/feature.js +29 -25
  112. package/dist/file-foundation/index.d.ts +1 -1
  113. package/dist/file-foundation/index.js +1 -1
  114. package/dist/file-foundation/read-file-ref-content.js +1 -1
  115. package/dist/file-provider-inmemory/feature.d.ts +4 -0
  116. package/dist/file-provider-inmemory/feature.js +24 -20
  117. package/dist/file-provider-inmemory/index.d.ts +1 -1
  118. package/dist/file-provider-inmemory/index.js +1 -1
  119. package/dist/file-provider-s3/env-feature.d.ts +3 -0
  120. package/dist/file-provider-s3/env-feature.js +46 -0
  121. package/dist/file-provider-s3/feature.d.ts +26 -0
  122. package/dist/file-provider-s3/feature.js +84 -72
  123. package/dist/file-provider-s3/index.d.ts +1 -1
  124. package/dist/file-provider-s3/index.js +1 -1
  125. package/dist/file-provider-s3/lib/index.d.ts +3 -0
  126. package/dist/file-provider-s3/lib/index.js +2 -0
  127. package/dist/{files-provider-s3 → file-provider-s3/lib}/s3-provider.js +19 -19
  128. package/dist/file-provider-s3-env/index.d.ts +8 -1
  129. package/dist/file-provider-s3-env/index.js +7 -2
  130. package/dist/files-provider-s3/index.d.ts +5 -3
  131. package/dist/files-provider-s3/index.js +5 -2
  132. package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +1 -1
  133. package/dist/files-tenant-data/hooks.js +1 -2
  134. package/dist/folders/feature.d.ts +6 -2
  135. package/dist/folders/feature.js +4 -2
  136. package/dist/folders-user-data/index.d.ts +4 -0
  137. package/dist/folders-user-data/index.js +21 -17
  138. package/dist/form-draft/db/queries/cleanup.js +1 -1
  139. package/dist/form-draft/db/queries/draft-count.js +1 -1
  140. package/dist/form-draft/db/queries/owned-file-refs.js +1 -2
  141. package/dist/form-draft/feature.d.ts +4 -0
  142. package/dist/form-draft/feature.js +5 -1
  143. package/dist/form-draft/handlers/cleanup.job.js +2 -2
  144. package/dist/form-draft/index.d.ts +1 -1
  145. package/dist/form-draft/index.js +1 -1
  146. package/dist/form-draft/lookup.js +1 -1
  147. package/dist/form-draft-user-data/index.d.ts +4 -0
  148. package/dist/form-draft-user-data/index.js +13 -9
  149. package/dist/inbound-mail-foundation/feature.d.ts +4 -0
  150. package/dist/inbound-mail-foundation/feature.js +151 -147
  151. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +1 -2
  152. package/dist/inbound-mail-foundation/index.d.ts +1 -1
  153. package/dist/inbound-mail-foundation/index.js +1 -1
  154. package/dist/inbound-mail-foundation/oauth-access-token.js +1 -2
  155. package/dist/inbound-mail-foundation/projection.js +1 -2
  156. package/dist/inbound-mail-foundation/watch-supervisor.js +1 -1
  157. package/dist/inbound-provider-imap/feature.d.ts +4 -0
  158. package/dist/inbound-provider-imap/feature.js +16 -12
  159. package/dist/inbound-provider-imap/index.d.ts +1 -1
  160. package/dist/inbound-provider-imap/index.js +1 -1
  161. package/dist/inbound-provider-inmemory/feature.d.ts +4 -0
  162. package/dist/inbound-provider-inmemory/feature.js +14 -10
  163. package/dist/inbound-provider-inmemory/index.d.ts +1 -1
  164. package/dist/inbound-provider-inmemory/index.js +1 -1
  165. package/dist/jobs/db/queries/retention.js +1 -1
  166. package/dist/jobs/db/queries/stale-run-sweep.js +1 -1
  167. package/dist/jobs/handlers/detail.query.js +1 -1
  168. package/dist/jobs/handlers/list.query.js +1 -2
  169. package/dist/jobs/handlers/retry.write.js +1 -1
  170. package/dist/jobs/handlers/tenant-failures.query.js +1 -1
  171. package/dist/jobs/handlers/tenant-runs.query.js +1 -1
  172. package/dist/jobs/job-run-logger.js +1 -2
  173. package/dist/jobs/tenant-job-failure-table.js +1 -2
  174. package/dist/jobs/tenant-job-run-table.js +1 -2
  175. package/dist/ledger/feature.d.ts +6 -2
  176. package/dist/ledger/feature.js +4 -2
  177. package/dist/mail-foundation/feature.d.ts +11 -0
  178. package/dist/mail-foundation/feature.js +52 -42
  179. package/dist/mail-foundation/i18n.d.ts +14 -0
  180. package/dist/mail-foundation/i18n.js +8 -0
  181. package/dist/mail-foundation/index.d.ts +1 -1
  182. package/dist/mail-foundation/index.js +1 -1
  183. package/dist/mail-transport-inmemory/feature.d.ts +4 -0
  184. package/dist/mail-transport-inmemory/feature.js +26 -16
  185. package/dist/mail-transport-inmemory/index.d.ts +1 -1
  186. package/dist/mail-transport-inmemory/index.js +1 -1
  187. package/dist/mail-transport-smtp/config-keys.d.ts +7 -0
  188. package/dist/mail-transport-smtp/config-keys.js +65 -0
  189. package/dist/mail-transport-smtp/feature.d.ts +17 -1
  190. package/dist/mail-transport-smtp/feature.js +58 -80
  191. package/dist/mail-transport-smtp/i18n.d.ts +41 -0
  192. package/dist/mail-transport-smtp/i18n.js +28 -0
  193. package/dist/mail-transport-smtp/index.d.ts +1 -1
  194. package/dist/mail-transport-smtp/index.js +1 -1
  195. package/dist/managed-pages/handlers/by-slug.query.js +1 -1
  196. package/dist/managed-pages/handlers/by-tenant-published.query.js +1 -2
  197. package/dist/managed-pages/handlers/set.write.js +1 -2
  198. package/dist/managed-pages/seeding.js +3 -5
  199. package/dist/metrics/compute.d.ts +1 -1
  200. package/dist/metrics/filters.d.ts +1 -1
  201. package/dist/metrics/filters.js +1 -1
  202. package/dist/metrics/validate.js +1 -1
  203. package/dist/notes-history/feature.d.ts +7 -2
  204. package/dist/notes-history/feature.js +4 -2
  205. package/dist/notes-history/handlers/add-note.write.js +1 -1
  206. package/dist/notes-history-user-data/hooks.js +1 -1
  207. package/dist/notes-history-user-data/index.d.ts +4 -0
  208. package/dist/notes-history-user-data/index.js +21 -17
  209. package/dist/personal-access-tokens/feature.d.ts +4 -1
  210. package/dist/personal-access-tokens/feature.js +10 -2
  211. package/dist/personal-access-tokens/handlers/create.write.js +1 -1
  212. package/dist/personal-access-tokens/handlers/list.query.js +2 -2
  213. package/dist/personal-access-tokens/handlers/revoke.write.js +1 -1
  214. package/dist/personal-access-tokens/resolver.js +1 -1
  215. package/dist/personal-access-tokens/revoke-for-user.js +1 -1
  216. package/dist/personal-access-tokens/roles.js +3 -5
  217. package/dist/personal-access-tokens/screens.js +2 -0
  218. package/dist/readiness/feature.d.ts +8 -0
  219. package/dist/readiness/feature.js +18 -14
  220. package/dist/readiness/index.d.ts +1 -1
  221. package/dist/readiness/index.js +1 -1
  222. package/dist/secrets/db/queries/read.js +1 -1
  223. package/dist/secrets/feature.d.ts +4 -0
  224. package/dist/secrets/feature.js +19 -6
  225. package/dist/secrets/handlers/delete.write.js +3 -2
  226. package/dist/secrets/handlers/rotate.job.js +2 -3
  227. package/dist/secrets/handlers/set.write.js +3 -2
  228. package/dist/secrets/secrets-context.js +3 -4
  229. package/dist/sessions/db/queries/cleanup.js +1 -1
  230. package/dist/sessions/handlers/detail.query.js +1 -1
  231. package/dist/sessions/handlers/list.query.js +2 -2
  232. package/dist/sessions/handlers/mine.query.js +1 -1
  233. package/dist/sessions/handlers/revoke-all-for-user.write.js +1 -1
  234. package/dist/sessions/handlers/revoke-all-others.write.js +1 -1
  235. package/dist/sessions/handlers/revoke.write.js +1 -1
  236. package/dist/sessions/session-callbacks.js +4 -6
  237. package/dist/sessions/testing.d.ts +1 -1
  238. package/dist/tags/feature.d.ts +6 -2
  239. package/dist/tags/feature.js +4 -2
  240. package/dist/template-resolver/api.js +1 -1
  241. package/dist/template-resolver/constants.d.ts +0 -1
  242. package/dist/template-resolver/constants.js +0 -1
  243. package/dist/template-resolver/handlers/bundle-import.js +1 -1
  244. package/dist/template-resolver/handlers/by-slug.query.js +1 -1
  245. package/dist/template-resolver/handlers/by-tenant.query.js +1 -2
  246. package/dist/template-resolver/handlers/collection-item.query.js +1 -1
  247. package/dist/template-resolver/handlers/collection-list.query.js +1 -2
  248. package/dist/template-resolver/handlers/collection-set.write.js +1 -1
  249. package/dist/template-resolver/handlers/copy-locale.write.js +1 -1
  250. package/dist/template-resolver/handlers/diff-to-system.query.js +1 -1
  251. package/dist/template-resolver/handlers/export-bundle.query.js +1 -1
  252. package/dist/template-resolver/handlers/find-by-id.query.js +1 -1
  253. package/dist/template-resolver/handlers/layout-source.query.js +1 -1
  254. package/dist/template-resolver/handlers/list.query.js +1 -1
  255. package/dist/template-resolver/handlers/overview.query.js +1 -1
  256. package/dist/template-resolver/handlers/reset-to-system.write.js +1 -1
  257. package/dist/template-resolver/handlers/resource-remove.write.js +1 -1
  258. package/dist/template-resolver/handlers/send-test.write.js +4 -2
  259. package/dist/template-resolver/handlers/set.write.js +1 -1
  260. package/dist/template-resolver/handlers/template-write.js +1 -1
  261. package/dist/template-resolver/handlers/tenant-overrides.query.js +1 -1
  262. package/dist/template-resolver/handlers/toggle-status.write.js +1 -1
  263. package/dist/template-resolver/handlers/usage.query.js +1 -1
  264. package/dist/template-resolver/handlers/validate.query.js +1 -1
  265. package/dist/template-resolver/seeding.js +3 -4
  266. package/dist/template-resolver/store-template-resource.js +1 -1
  267. package/dist/template-resolver/template-preview.js +1 -1
  268. package/dist/template-resolver/template-resources.js +1 -1
  269. package/dist/template-resolver/testing.js +1 -1
  270. package/dist/template-resolver-user-data/index.d.ts +4 -0
  271. package/dist/template-resolver-user-data/index.js +17 -13
  272. package/dist/tenant/handlers/active-tenant-ids.query.js +1 -1
  273. package/dist/tenant/handlers/add-member.write.js +1 -2
  274. package/dist/tenant/handlers/cancel-invitation.write.js +1 -1
  275. package/dist/tenant/handlers/invitations.query.js +1 -1
  276. package/dist/tenant/handlers/me.query.js +1 -1
  277. package/dist/tenant/handlers/member-directory.query.js +1 -1
  278. package/dist/tenant/handlers/members.query.js +1 -1
  279. package/dist/tenant/handlers/memberships.query.js +1 -1
  280. package/dist/tenant/handlers/remove-member.write.js +1 -2
  281. package/dist/tenant/handlers/resolve-user-ids.query.js +1 -1
  282. package/dist/tenant/handlers/team-list.query.js +1 -1
  283. package/dist/tenant/handlers/tenant-directory.query.js +1 -1
  284. package/dist/tenant/handlers/update-member-roles.write.js +1 -2
  285. package/dist/tenant/invited-membership.js +2 -3
  286. package/dist/tenant/last-tenant-admin.js +1 -2
  287. package/dist/tenant/seeding.js +12 -6
  288. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +1 -2
  289. package/dist/tenant-lifecycle/handlers/request-destruction.write.js +1 -2
  290. package/dist/tenant-lifecycle/lib/revoke-tenant-sessions.js +1 -1
  291. package/dist/tenant-lifecycle/lifecycle-gate.js +1 -1
  292. package/dist/tenant-lifecycle/run-tenant-destroy.js +3 -5
  293. package/dist/tenant-lifecycle/stages.js +10 -6
  294. package/dist/tenant-lifecycle/testing.d.ts +1 -1
  295. package/dist/tenant-lifecycle/testing.js +2 -2
  296. package/dist/tenant-settings/feature.d.ts +1 -0
  297. package/dist/tenant-settings/feature.js +2 -1
  298. package/dist/tier-engine/feature.d.ts +1 -8
  299. package/dist/tier-engine/feature.js +71 -78
  300. package/dist/tier-engine/handlers/get-tenant-tier.query.js +2 -3
  301. package/dist/tier-engine/handlers/set-tenant-tier.write.js +1 -2
  302. package/dist/user/db/queries/stream-tenant-backfill.js +1 -1
  303. package/dist/user/handlers/create.write.js +1 -2
  304. package/dist/user/handlers/find-for-auth.query.js +1 -1
  305. package/dist/user/handlers/list.query.js +1 -1
  306. package/dist/user/handlers/update-roles.js +1 -2
  307. package/dist/user/index.d.ts +1 -1
  308. package/dist/user/index.js +1 -1
  309. package/dist/user/invited-global-roles.js +2 -3
  310. package/dist/user/principal-status.d.ts +4 -0
  311. package/dist/user/principal-status.js +9 -3
  312. package/dist/user/resolve-display-names.js +1 -1
  313. package/dist/user/seeding.js +5 -6
  314. package/dist/user-data-rights/audit-download.js +2 -2
  315. package/dist/user-data-rights/db/queries/export-jobs.js +1 -1
  316. package/dist/user-data-rights/db/queries/forget-cleanup.js +1 -1
  317. package/dist/user-data-rights/feature.js +7 -4
  318. package/dist/user-data-rights/handlers/download-by-job.query.js +1 -1
  319. package/dist/user-data-rights/handlers/download-by-token.query.js +1 -1
  320. package/dist/user-data-rights/handlers/export-status.query.js +1 -1
  321. package/dist/user-data-rights/handlers/list-download-attempts.query.js +1 -1
  322. package/dist/user-data-rights/handlers/my-audit-log.query.js +1 -1
  323. package/dist/user-data-rights/handlers/request-export.write.js +1 -2
  324. package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +3 -3
  325. package/dist/user-data-rights/lib/default-mailers.d.ts +3 -0
  326. package/dist/user-data-rights/lib/default-mailers.js +25 -3
  327. package/dist/user-data-rights/lib/deny-if-target-outside-admin-tenant.d.ts +1 -1
  328. package/dist/user-data-rights/lib/deny-if-target-outside-admin-tenant.js +1 -1
  329. package/dist/user-data-rights/lib/update-user-lifecycle.d.ts +1 -0
  330. package/dist/user-data-rights/lib/update-user-lifecycle.js +3 -3
  331. package/dist/user-data-rights/run-export-jobs.js +2 -3
  332. package/dist/user-data-rights/run-forget-cleanup.js +9 -5
  333. package/dist/user-data-rights/run-user-export.js +9 -5
  334. package/dist/waitlist/handlers/invite.write.js +1 -2
  335. package/dist/waitlist/handlers/reject.write.js +1 -1
  336. package/dist/waitlist/lib.js +1 -2
  337. package/dist/workflow-runner/event-subscriber.js +1 -1
  338. package/dist/workflow-runner/feature.d.ts +4 -0
  339. package/dist/workflow-runner/feature.js +44 -40
  340. package/dist/workflow-runner/handlers/resume-run.write.js +1 -1
  341. package/dist/workflow-runner/index.d.ts +1 -1
  342. package/dist/workflow-runner/index.js +1 -1
  343. package/dist/workflow-runner/pending-projection.js +1 -1
  344. package/package.json +28 -11
  345. package/src/cap-counter/changes.json +13 -0
  346. package/src/channel-email/changes.json +7 -0
  347. package/src/crypto-shredding/changes.json +6 -0
  348. package/src/delivery/changes.json +6 -0
  349. package/src/file-provider-s3/changes.json +10 -1
  350. package/src/mail-transport-smtp/changes.json +7 -0
  351. package/src/personal-access-tokens/changes.json +6 -0
  352. package/src/secrets/changes.json +6 -0
  353. package/src/user/changes.json +6 -0
  354. package/src/user-data-rights/changes.json +6 -0
  355. package/dist/file-provider-s3-env/feature.d.ts +0 -3
  356. package/dist/file-provider-s3-env/feature.js +0 -46
  357. /package/dist/{files-provider-s3 → file-provider-s3/lib}/env-helper.d.ts +0 -0
  358. /package/dist/{files-provider-s3 → file-provider-s3/lib}/env-helper.js +0 -0
  359. /package/dist/{files-provider-s3 → file-provider-s3/lib}/s3-provider.d.ts +0 -0
@@ -45,8 +45,7 @@
45
45
  // matches would still delete it out from under the tenant that claimed
46
46
  // its original. Batched: one existence query per chunk of listed keys,
47
47
  // not one query per key.
48
- import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
49
- import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
48
+ import { createEventStoreExecutor, selectMany } from "@cosmicdrift/kumiko-framework/db";
50
49
  import { createSystemUser, parseTenantId, } from "@cosmicdrift/kumiko-framework/engine";
51
50
  import { assertSafeStorageKey, fileRefEntity, fileRefsTable, storageKeyStemPrefix, tenantStoragePrefixes, } from "@cosmicdrift/kumiko-framework/files";
52
51
  import { runChunkedMigration } from "../shared/index.js";
@@ -2,7 +2,7 @@ import { type AccessRule, type EntityDefinition } from "@cosmicdrift/kumiko-fram
2
2
  type FoldersToggleable = {
3
3
  readonly default: boolean;
4
4
  };
5
- export declare const foldersFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
5
+ declare const defaultFoldersFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
6
6
  readonly exports: void;
7
7
  };
8
8
  export type FoldersFeatureOptions = {
@@ -41,5 +41,9 @@ export type FoldersFeatureOptions = {
41
41
  * otherwise considers every registered entity as a candidate host. */
42
42
  readonly parents?: readonly string[];
43
43
  };
44
- export declare function createFoldersFeature(opts?: FoldersFeatureOptions): typeof foldersFeature;
44
+ export declare function createFoldersFeature(opts?: FoldersFeatureOptions): typeof defaultFoldersFeature;
45
+ /** @deprecated Use createFoldersFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
46
+ export declare const foldersFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
47
+ readonly exports: void;
48
+ };
45
49
  export {};
@@ -62,7 +62,7 @@ function registerFolders(r, access, toggleable, ownership, parents) {
62
62
  description: "Lists the folder-membership rows of the caller's tenant; filter on entityId to learn which folder one entity sits in, or on folderId to list everything filed into a folder.",
63
63
  }));
64
64
  }
65
- export const foldersFeature = defineFeature(FOLDERS_FEATURE_NAME, (r) => registerFolders(r, DEFAULT_FOLDER_ACCESS, undefined, undefined, undefined));
65
+ const defaultFoldersFeature = defineFeature(FOLDERS_FEATURE_NAME, (r) => registerFolders(r, DEFAULT_FOLDER_ACCESS, undefined, undefined, undefined));
66
66
  function resolveAccess(opts) {
67
67
  if (opts.access !== undefined)
68
68
  return opts.access;
@@ -78,7 +78,7 @@ export function createFoldersFeature(opts = {}) {
78
78
  opts.toggleable === undefined &&
79
79
  opts.ownership === undefined &&
80
80
  opts.parents === undefined) {
81
- return foldersFeature;
81
+ return defaultFoldersFeature;
82
82
  }
83
83
  assertNoWhereRuleInOwnershipWrite("createFoldersFeature", opts.ownership?.write);
84
84
  if (opts.parents !== undefined && opts.parents.length === 0) {
@@ -90,3 +90,5 @@ export function createFoldersFeature(opts = {}) {
90
90
  const access = resolveAccess(opts);
91
91
  return defineFeature(FOLDERS_FEATURE_NAME, (r) => registerFolders(r, access, opts.toggleable, opts.ownership, opts.parents));
92
92
  }
93
+ /** @deprecated Use createFoldersFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
94
+ export const foldersFeature = createFoldersFeature();
@@ -1,3 +1,7 @@
1
+ export declare function createFoldersUserDataFeature(): import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
+ readonly exports: void;
3
+ };
4
+ /** @deprecated Use createFoldersUserDataFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
1
5
  export declare const foldersUserDataFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
6
  readonly exports: void;
3
7
  };
@@ -5,20 +5,24 @@
5
5
  // folders stays usable without the user-data-rights stack. Mirrors credit-user-data.
6
6
  import { defineFeature, EXT_USER_DATA } from "@cosmicdrift/kumiko-framework/engine";
7
7
  import { folderAssignmentDeleteHook, folderAssignmentExportHook, folderDeleteHook, folderExportHook, } from "./hooks.js";
8
- export const foldersUserDataFeature = defineFeature("folders-user-data", (r) => {
9
- r.describe("GDPR (Art. 20 export / Art. 17 erasure) coverage for the `folders` feature's `folder` + `folder-assignment` entities. Mounts the EXT_USER_DATA export + delete hooks so a tenant's folder tree and its entity-to-folder assignments are included in the user-data export bundle and erased on a tenant-scoped forget (single-user tenants only; multi-user + anonymize are no-ops since folder rows carry no per-user PII). Kept separate from `folders` so folder consumers without the user-data-rights pipeline don't pull a hard dependency — requires `user-data-rights`, optionalRequires `folders`.");
10
- // user-data-rights is the hard dependency (EXT_USER_DATA host). `folders` is
11
- // OPTIONAL: if it's mounted toggleable(default=false) (e.g. per-tenant via
12
- // tier), a hard r.requires would throw an "effectively disabled" boot
13
- // warning even though the folder entities exist and the hooks work fine.
14
- r.requires({ features: ["user-data-rights"] });
15
- r.optionalRequires("folders");
16
- r.useExtension(EXT_USER_DATA, "folder", {
17
- export: folderExportHook,
18
- delete: folderDeleteHook,
19
- });
20
- r.useExtension(EXT_USER_DATA, "folder-assignment", {
21
- export: folderAssignmentExportHook,
22
- delete: folderAssignmentDeleteHook,
23
- });
24
- });
8
+ export function createFoldersUserDataFeature() {
9
+ return defineFeature("folders-user-data", (r) => {
10
+ r.describe("GDPR (Art. 20 export / Art. 17 erasure) coverage for the `folders` feature's `folder` + `folder-assignment` entities. Mounts the EXT_USER_DATA export + delete hooks so a tenant's folder tree and its entity-to-folder assignments are included in the user-data export bundle and erased on a tenant-scoped forget (single-user tenants only; multi-user + anonymize are no-ops since folder rows carry no per-user PII). Kept separate from `folders` so folder consumers without the user-data-rights pipeline don't pull a hard dependency — requires `user-data-rights`, optionalRequires `folders`.");
11
+ // user-data-rights is the hard dependency (EXT_USER_DATA host). `folders` is
12
+ // OPTIONAL: if it's mounted toggleable(default=false) (e.g. per-tenant via
13
+ // tier), a hard r.requires would throw an "effectively disabled" boot
14
+ // warning even though the folder entities exist and the hooks work fine.
15
+ r.requires({ features: ["user-data-rights"] });
16
+ r.optionalRequires("folders");
17
+ r.useExtension(EXT_USER_DATA, "folder", {
18
+ export: folderExportHook,
19
+ delete: folderDeleteHook,
20
+ });
21
+ r.useExtension(EXT_USER_DATA, "folder-assignment", {
22
+ export: folderAssignmentExportHook,
23
+ delete: folderAssignmentDeleteHook,
24
+ });
25
+ }, { dedupeOptions: {} });
26
+ }
27
+ /** @deprecated Use createFoldersUserDataFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
28
+ export const foldersUserDataFeature = createFoldersUserDataFeature();
@@ -1,4 +1,4 @@
1
- import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/bun-db";
1
+ import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/db";
2
2
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
3
3
  // COALESCE(modified_at, inserted_at) — a draft's "last touched" timestamp is
4
4
  // modified_at once the upsert path (save.write.ts) has updated it at least
@@ -1,4 +1,4 @@
1
- import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/bun-db";
1
+ import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/db";
2
2
  // COUNT(*), not selectMany(...).length — save.write.ts checks this on every
3
3
  // create, and a draft's `values` blob can be up to FORM_DRAFT_VALUES_MAX_BYTES
4
4
  // (64KB); loading up to FORM_DRAFT_MAX_PER_OWNER full rows just to count them
@@ -1,5 +1,4 @@
1
- import { instantToTimestamptzParam, unsafeReadRetrying, } from "@cosmicdrift/kumiko-framework/bun-db";
2
- import { toPostgresArrayLiteral } from "@cosmicdrift/kumiko-framework/db";
1
+ import { instantToTimestamptzParam, toPostgresArrayLiteral, unsafeReadRetrying, } from "@cosmicdrift/kumiko-framework/db";
3
2
  // Narrows a draft blob's raw FileRef-shaped storageKeys (collectDraftFileRefKeys
4
3
  // output — extracted from free-form, client-supplied JSON, issue #1889) down to
5
4
  // the ones with a real, non-deleted file_refs row owned by this exact
@@ -1,3 +1,7 @@
1
+ export declare function createFormDraftFeature(): import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
+ readonly exports: void;
3
+ };
4
+ /** @deprecated Use createFormDraftFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
1
5
  export declare const formDraftFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
6
  readonly exports: void;
3
7
  };
@@ -41,4 +41,8 @@ function registerFormDraft(r) {
41
41
  handler: (payload, ctx) => cleanupDraftsJob(payload, ctx, ctx.db.unsafeRaw()),
42
42
  });
43
43
  }
44
- export const formDraftFeature = defineFeature(FORM_DRAFT_FEATURE_NAME, registerFormDraft);
44
+ export function createFormDraftFeature() {
45
+ return defineFeature(FORM_DRAFT_FEATURE_NAME, registerFormDraft, { dedupeOptions: {} });
46
+ }
47
+ /** @deprecated Use createFormDraftFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
48
+ export const formDraftFeature = createFormDraftFeature();
@@ -37,7 +37,7 @@ async function releaseRowFileRefs(row, db, fileProviderResolver, log) {
37
37
  const isCreateMode = row.draftKey.includes(":new:");
38
38
  const ownedRefs = await filterOwnedFileRefs(db, row.tenantId, row.ownerId, keys, row.insertedAt, isCreateMode);
39
39
  const systemUser = createSystemUser(row.tenantId);
40
- const tenantDb = createTenantDb(db, row.tenantId, "system");
40
+ const tenantDb = createTenantDb({ db, tenantId: row.tenantId, mode: "system" });
41
41
  for (const ref of ownedRefs) {
42
42
  const forgetResult = await fileRefExecutor.forget({ id: ref.id }, systemUser, tenantDb);
43
43
  if (!forgetResult.isSuccess) {
@@ -78,7 +78,7 @@ export async function deleteStaleDraftsBatch(batch, db, retentionDays, log) {
78
78
  const byId = new Map(batch.map((row) => [row.id, row]));
79
79
  for (const [tenantId, ids] of groupStaleDraftIdsByTenant(batch)) {
80
80
  const systemUser = createSystemUser(tenantId);
81
- const tenantDb = createTenantDb(db, tenantId, "system");
81
+ const tenantDb = createTenantDb({ db, tenantId, mode: "system" });
82
82
  for (const id of ids) {
83
83
  // Race: user may have saved between select and here — skip if no longer stale.
84
84
  if (!(await isDraftStillStale(db, tenantId, id, retentionDays))) {
@@ -1,7 +1,7 @@
1
1
  export { FORM_DRAFT_ACCESS, FORM_DRAFT_FEATURE_NAME, FORM_DRAFT_KEY_MAX_LENGTH, FORM_DRAFT_UNIQUE_KEY_CONSTRAINT, FormDraftHandlers, FormDraftQueries, } from "./constants.js";
2
2
  export { formDraftEntity } from "./entity.js";
3
3
  export { formDraftExecutor, formDraftTable } from "./executor.js";
4
- export { formDraftFeature } from "./feature.js";
4
+ export { createFormDraftFeature, formDraftFeature } from "./feature.js";
5
5
  export { discardDraftWrite } from "./handlers/discard.write.js";
6
6
  export { type GetDraftResult, getDraftQuery } from "./handlers/get.query.js";
7
7
  export { type ListDraftsResult, listDraftsQuery } from "./handlers/list.query.js";
@@ -1,7 +1,7 @@
1
1
  export { FORM_DRAFT_ACCESS, FORM_DRAFT_FEATURE_NAME, FORM_DRAFT_KEY_MAX_LENGTH, FORM_DRAFT_UNIQUE_KEY_CONSTRAINT, FormDraftHandlers, FormDraftQueries, } from "./constants.js";
2
2
  export { formDraftEntity } from "./entity.js";
3
3
  export { formDraftExecutor, formDraftTable } from "./executor.js";
4
- export { formDraftFeature } from "./feature.js";
4
+ export { createFormDraftFeature, formDraftFeature } from "./feature.js";
5
5
  export { discardDraftWrite } from "./handlers/discard.write.js";
6
6
  export { getDraftQuery } from "./handlers/get.query.js";
7
7
  export { listDraftsQuery } from "./handlers/list.query.js";
@@ -1,4 +1,4 @@
1
- import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
1
+ import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/db";
2
2
  import { formDraftTable } from "./executor.js";
3
3
  // Shared by save (upsert lookup + race re-lookup), discard, and get — always
4
4
  // scoped by tenantId + ownerId + draftKey, tenantId included explicitly
@@ -1,3 +1,7 @@
1
+ export declare function createFormDraftUserDataFeature(): import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
+ readonly exports: void;
3
+ };
4
+ /** @deprecated Use createFormDraftUserDataFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
1
5
  export declare const formDraftUserDataFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
6
  readonly exports: void;
3
7
  };
@@ -6,12 +6,16 @@
6
6
  // pull a hard dependency. Mirrors notes-history-user-data.
7
7
  import { defineFeature, EXT_USER_DATA } from "@cosmicdrift/kumiko-framework/engine";
8
8
  import { formDraftDeleteHook, formDraftExportHook } from "./hooks.js";
9
- export const formDraftUserDataFeature = defineFeature("form-draft-user-data", (r) => {
10
- r.describe("GDPR (Art. 20 export / Art. 17 erasure) coverage for the `form-draft` feature's `form-draft` entity. Mounts the EXT_USER_DATA export hook so a user's saved drafts are included in the user-data export bundle, and a delete hook that physically removes the user's own draft rows on forget. Kept separate from `form-draft` so form-draft consumers without the user-data-rights pipeline don't pull a hard dependency — requires `user-data-rights`, optionalRequires `form-draft`.");
11
- r.requires({ features: ["user-data-rights"] });
12
- r.optionalRequires("form-draft");
13
- r.useExtension(EXT_USER_DATA, "form-draft", {
14
- export: formDraftExportHook,
15
- delete: formDraftDeleteHook,
16
- });
17
- });
9
+ export function createFormDraftUserDataFeature() {
10
+ return defineFeature("form-draft-user-data", (r) => {
11
+ r.describe("GDPR (Art. 20 export / Art. 17 erasure) coverage for the `form-draft` feature's `form-draft` entity. Mounts the EXT_USER_DATA export hook so a user's saved drafts are included in the user-data export bundle, and a delete hook that physically removes the user's own draft rows on forget. Kept separate from `form-draft` so form-draft consumers without the user-data-rights pipeline don't pull a hard dependency — requires `user-data-rights`, optionalRequires `form-draft`.");
12
+ r.requires({ features: ["user-data-rights"] });
13
+ r.optionalRequires("form-draft");
14
+ r.useExtension(EXT_USER_DATA, "form-draft", {
15
+ export: formDraftExportHook,
16
+ delete: formDraftDeleteHook,
17
+ });
18
+ }, { dedupeOptions: {} });
19
+ }
20
+ /** @deprecated Use createFormDraftUserDataFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
21
+ export const formDraftUserDataFeature = createFormDraftUserDataFeature();
@@ -1,3 +1,7 @@
1
+ export declare function createInboundMailFoundationFeature(): import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
+ readonly exports: void;
3
+ };
4
+ /** @deprecated Use createInboundMailFoundationFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
1
5
  export declare const inboundMailFoundationFeature: import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
2
6
  readonly exports: void;
3
7
  };
@@ -54,151 +54,155 @@ import { updateAccountHandler } from "./handlers/update-account.write.js";
54
54
  import { applyInboundMessageReceived, applyMailAccountConnected, applyMailAccountDisconnected, applyMailAccountUpdated, applyMailThreadUpdated, inboundMessagesProjectionTable, mailAccountsProjectionTable, mailThreadsProjectionTable, } from "./projection.js";
55
55
  import { runInboundMailRetention } from "./retention-sweep.js";
56
56
  import { INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON, inboundMessageTenantDestroyHook, mailAccountTenantDestroyHook, mailThreadTenantDestroyHook, } from "./tenant-destroy-hook.js";
57
- export const inboundMailFoundationFeature = defineFeature(INBOUND_MAIL_FOUNDATION_FEATURE, (r) => {
58
- r.describe("Plugin host for inbound e-mail (inbox federation) — provider plugins (inbound-provider-imap, later M365 Graph / Gmail REST) register at the `inboundMailProvider` extension point with verify/fetch and optional oauth/watch (live push). The foundation owns three event-sourced streams (mail-account lifecycle, inbound-message with exactly-once ingest via deterministic aggregate ids, mail-thread rollup) and their projections `read_mail_accounts`/`read_inbound_messages`/`read_mail_threads`; PII fields are envelope-encrypted per tenant subject key before every append (crypto-shredding on tenant destroy). Wire `createInboundMailConnectRoutes` (OAuth connect + anonymous callback outside /api) and `createInboundMailSupervisor` (IDLE watch with backoff + reconciliation poll) in bin/server.ts. Consume `inbound-mail-foundation:event:inbound-message-received` from app features to attach business processes.");
59
- r.uiHints({
60
- displayLabel: "Inbound Mail · Foundation",
61
- category: "notifications",
62
- recommended: false,
63
- });
64
- // EXT_TENANT_DATA-Hook braucht den tenant-lifecycle-Host.
65
- r.requires({ features: ["tenant-lifecycle"] });
66
- // 5 fine-grained domain-events (Payload-Schemas: events.ts).
67
- // piiFields: "none" — address/subject/snippet are already ciphertext under the tenant subject key before append (see events.ts).
68
- // "displayName" stays plaintext (often the mailbox owner's real name for
69
- // personal accounts); encrypting it would break the entity-projection read
70
- // path that carries it as `personal: false`.
71
- r.defineEvent(MAIL_ACCOUNT_CONNECTED_EVENT_SHORT, mailAccountEventPayloadSchema, {
72
- piiFields: "none",
73
- });
74
- r.defineEvent(MAIL_ACCOUNT_UPDATED_EVENT_SHORT, mailAccountEventPayloadSchema, {
75
- piiFields: "none",
76
- });
77
- r.defineEvent(MAIL_ACCOUNT_DISCONNECTED_EVENT_SHORT, mailAccountEventPayloadSchema, {
78
- piiFields: "none",
79
- });
80
- r.defineEvent(INBOUND_MESSAGE_RECEIVED_EVENT_SHORT, inboundMessageEventPayloadSchema, {
81
- piiFields: "none",
82
- });
83
- r.defineEvent(MAIL_THREAD_UPDATED_EVENT_SHORT, mailThreadEventPayloadSchema, {
84
- piiFields: "none",
85
- });
86
- // Inline projections — apply in derselben TX wie der Append.
87
- r.projection({
88
- name: "mail-account",
89
- source: MAIL_ACCOUNT_AGGREGATE_TYPE,
90
- table: mailAccountsProjectionTable,
91
- entity: mailAccountEntity,
92
- apply: {
93
- [MAIL_ACCOUNT_CONNECTED_EVENT_QN]: applyMailAccountConnected,
94
- [MAIL_ACCOUNT_UPDATED_EVENT_QN]: applyMailAccountUpdated,
95
- [MAIL_ACCOUNT_DISCONNECTED_EVENT_QN]: applyMailAccountDisconnected,
96
- },
97
- });
98
- r.projection({
99
- name: "inbound-message",
100
- source: INBOUND_MESSAGE_AGGREGATE_TYPE,
101
- table: inboundMessagesProjectionTable,
102
- entity: inboundMessageEntity,
103
- apply: {
104
- [INBOUND_MESSAGE_RECEIVED_EVENT_QN]: applyInboundMessageReceived,
105
- },
106
- });
107
- r.projection({
108
- name: "mail-thread",
109
- source: MAIL_THREAD_AGGREGATE_TYPE,
110
- table: mailThreadsProjectionTable,
111
- entity: mailThreadEntity,
112
- apply: {
113
- [MAIL_THREAD_UPDATED_EVENT_QN]: applyMailThreadUpdated,
114
- },
115
- });
116
- // Plugin extension-point — Provider registrieren sich via
117
- // r.useExtension("inboundMailProvider", "<key>", plugin).
118
- r.extendsRegistrar(INBOUND_MAIL_PROVIDER_EXTENSION, {
119
- onRegister: () => {
120
- // No side-effects at register-time — Lookup läuft zur Laufzeit
121
- // über resolveInboundProviderForKey/-Account.
122
- },
123
- });
124
- // Sync-Maschinerie: hochfrequenter Tick-State, bewusst NICHT
125
- // event-sourced — r.storeTable hält die Migration-DDL, nimmt die
126
- // Tabellen aber aus dem Projection-Rebuild (#494/#498-Klasse).
127
- r.storeTable(syncCursorTable, {
128
- reason: "read_side.mail_sync_cursors_direct_write",
129
- });
130
- r.storeTable(seenMessageTable, {
131
- reason: "read_side.mail_seen_messages_direct_write",
132
- });
133
- // Write-Handler:
134
- // - ingest-message: programmatic (Supervisor/Poll, SystemAdmin)
135
- // - connect/update/disconnect-account: Lifecycle
136
- r.writeHandler(ingestMessageHandler);
137
- r.writeHandler(connectAccountHandler);
138
- r.writeHandler(updateAccountHandler);
139
- r.writeHandler(disconnectAccountHandler);
140
- // List-Queries (PII-decrypted, scope-gefiltert).
141
- r.queryHandler(listAccountsQuery);
142
- r.queryHandler(listMessagesQuery);
143
- // Entity-genaue GDPR-Destroy-Hooks — der gdpr-storage-Boot-Validator
144
- // prueft die EXT_TENANT_DATA-Registrierung pro tenant-subject-Entity.
145
- r.useExtension(EXT_TENANT_DATA, "mail-account", {
146
- destroy: mailAccountTenantDestroyHook,
147
- escapeHatch: { reason: INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON },
148
- });
149
- r.useExtension(EXT_TENANT_DATA, "inbound-message", {
150
- destroy: inboundMessageTenantDestroyHook,
151
- escapeHatch: { reason: INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON },
152
- });
153
- r.useExtension(EXT_TENANT_DATA, "mail-thread", {
154
- destroy: mailThreadTenantDestroyHook,
155
- escapeHatch: { reason: INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON },
156
- });
157
- // Data-Retention (#957) — perTenant fan-out (one run per tenant, like
158
- // data-retention:retention-cleanup): runInboundMailRetention's raw query
159
- // helpers need the unfiltered runner, so the handler declares escapeHatch
160
- // and reaches for ctx.db.unsafeRaw(). Without this cron, read_inbound_messages
161
- // + the bodies grow unbounded (GDPR Art. 5).
162
- const INBOUND_MAIL_RETENTION_REASON = "inbound-mail retention runs raw deletes; every statement filters by this run's tenantId";
163
- r.job({
164
- name: "inbound-mail-retention",
165
- trigger: { cron: "0 3 * * *" },
166
- perTenant: true,
167
- concurrency: "skip",
168
- escapeHatch: { reason: INBOUND_MAIL_RETENTION_REASON },
169
- handler: async (_payload, ctx) => {
170
- if (!ctx.registry) {
171
- throw new Error("inbound-mail-retention: ctx.registry required (JobContext incomplete)");
172
- }
173
- const tenantId = ctx.systemUser?.tenantId ?? ctx._tenantId;
174
- if (tenantId === undefined) {
175
- // skip: cron fired without a perTenant fan-out tenant — nothing scoped
176
- return;
177
- }
178
- const T = (await import("@cosmicdrift/kumiko-framework/time")).getTemporal();
179
- const retentionDb = ctx.db.unsafeRaw(); // @cast-boundary db-operator — jobs never run inside a DbTx
180
- // Body-Objekte liegen in file-foundation. Provider lazy + memoized —
181
- // heute schreibt der Ingest kein bodyRef (storeBody-Hook ungebunden),
182
- // der Provider wird also nie gebaut. Sobald storeBody wired ist, raeumt
183
- // die Retention die Bodies mit ab.
184
- let providerPromise = null;
185
- const deleteBodyObject = async (bodyRef) => {
186
- if (!providerPromise) {
187
- providerPromise = createFileProviderForTenant({ config: ctx.config, registry: ctx.registry }, tenantId);
57
+ export function createInboundMailFoundationFeature() {
58
+ return defineFeature(INBOUND_MAIL_FOUNDATION_FEATURE, (r) => {
59
+ r.describe("Plugin host for inbound e-mail (inbox federation) — provider plugins (inbound-provider-imap, later M365 Graph / Gmail REST) register at the `inboundMailProvider` extension point with verify/fetch and optional oauth/watch (live push). The foundation owns three event-sourced streams (mail-account lifecycle, inbound-message with exactly-once ingest via deterministic aggregate ids, mail-thread rollup) and their projections `read_mail_accounts`/`read_inbound_messages`/`read_mail_threads`; PII fields are envelope-encrypted per tenant subject key before every append (crypto-shredding on tenant destroy). Wire `createInboundMailConnectRoutes` (OAuth connect + anonymous callback outside /api) and `createInboundMailSupervisor` (IDLE watch with backoff + reconciliation poll) in bin/server.ts. Consume `inbound-mail-foundation:event:inbound-message-received` from app features to attach business processes.");
60
+ r.uiHints({
61
+ displayLabel: "Inbound Mail · Foundation",
62
+ category: "notifications",
63
+ recommended: false,
64
+ });
65
+ // EXT_TENANT_DATA-Hook braucht den tenant-lifecycle-Host.
66
+ r.requires({ features: ["tenant-lifecycle"] });
67
+ // 5 fine-grained domain-events (Payload-Schemas: events.ts).
68
+ // piiFields: "none" — address/subject/snippet are already ciphertext under the tenant subject key before append (see events.ts).
69
+ // "displayName" stays plaintext (often the mailbox owner's real name for
70
+ // personal accounts); encrypting it would break the entity-projection read
71
+ // path that carries it as `personal: false`.
72
+ r.defineEvent(MAIL_ACCOUNT_CONNECTED_EVENT_SHORT, mailAccountEventPayloadSchema, {
73
+ piiFields: "none",
74
+ });
75
+ r.defineEvent(MAIL_ACCOUNT_UPDATED_EVENT_SHORT, mailAccountEventPayloadSchema, {
76
+ piiFields: "none",
77
+ });
78
+ r.defineEvent(MAIL_ACCOUNT_DISCONNECTED_EVENT_SHORT, mailAccountEventPayloadSchema, {
79
+ piiFields: "none",
80
+ });
81
+ r.defineEvent(INBOUND_MESSAGE_RECEIVED_EVENT_SHORT, inboundMessageEventPayloadSchema, {
82
+ piiFields: "none",
83
+ });
84
+ r.defineEvent(MAIL_THREAD_UPDATED_EVENT_SHORT, mailThreadEventPayloadSchema, {
85
+ piiFields: "none",
86
+ });
87
+ // Inline projections — apply in derselben TX wie der Append.
88
+ r.projection({
89
+ name: "mail-account",
90
+ source: MAIL_ACCOUNT_AGGREGATE_TYPE,
91
+ table: mailAccountsProjectionTable,
92
+ entity: mailAccountEntity,
93
+ apply: {
94
+ [MAIL_ACCOUNT_CONNECTED_EVENT_QN]: applyMailAccountConnected,
95
+ [MAIL_ACCOUNT_UPDATED_EVENT_QN]: applyMailAccountUpdated,
96
+ [MAIL_ACCOUNT_DISCONNECTED_EVENT_QN]: applyMailAccountDisconnected,
97
+ },
98
+ });
99
+ r.projection({
100
+ name: "inbound-message",
101
+ source: INBOUND_MESSAGE_AGGREGATE_TYPE,
102
+ table: inboundMessagesProjectionTable,
103
+ entity: inboundMessageEntity,
104
+ apply: {
105
+ [INBOUND_MESSAGE_RECEIVED_EVENT_QN]: applyInboundMessageReceived,
106
+ },
107
+ });
108
+ r.projection({
109
+ name: "mail-thread",
110
+ source: MAIL_THREAD_AGGREGATE_TYPE,
111
+ table: mailThreadsProjectionTable,
112
+ entity: mailThreadEntity,
113
+ apply: {
114
+ [MAIL_THREAD_UPDATED_EVENT_QN]: applyMailThreadUpdated,
115
+ },
116
+ });
117
+ // Plugin extension point — providers register via
118
+ // r.useExtension("inboundMailProvider", "<key>", plugin).
119
+ r.extendsRegistrar(INBOUND_MAIL_PROVIDER_EXTENSION, {
120
+ onRegister: () => {
121
+ // No side-effects at register-time — lookup happens at runtime
122
+ // via resolveInboundProviderForKey/-Account.
123
+ },
124
+ });
125
+ // Sync machinery: high-frequency tick state, deliberately NOT
126
+ // event-sourced — r.storeTable keeps the migration DDL but excludes the
127
+ // tables from the projection rebuild (#494/#498 class).
128
+ r.storeTable(syncCursorTable, {
129
+ reason: "read_side.mail_sync_cursors_direct_write",
130
+ });
131
+ r.storeTable(seenMessageTable, {
132
+ reason: "read_side.mail_seen_messages_direct_write",
133
+ });
134
+ // Write-Handler:
135
+ // - ingest-message: programmatic (Supervisor/Poll, SystemAdmin)
136
+ // - connect/update/disconnect-account: Lifecycle
137
+ r.writeHandler(ingestMessageHandler);
138
+ r.writeHandler(connectAccountHandler);
139
+ r.writeHandler(updateAccountHandler);
140
+ r.writeHandler(disconnectAccountHandler);
141
+ // List-Queries (PII-decrypted, scope-gefiltert).
142
+ r.queryHandler(listAccountsQuery);
143
+ r.queryHandler(listMessagesQuery);
144
+ // Entity-genaue GDPR-Destroy-Hooks — der gdpr-storage-Boot-Validator
145
+ // prueft die EXT_TENANT_DATA-Registrierung pro tenant-subject-Entity.
146
+ r.useExtension(EXT_TENANT_DATA, "mail-account", {
147
+ destroy: mailAccountTenantDestroyHook,
148
+ escapeHatch: { reason: INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON },
149
+ });
150
+ r.useExtension(EXT_TENANT_DATA, "inbound-message", {
151
+ destroy: inboundMessageTenantDestroyHook,
152
+ escapeHatch: { reason: INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON },
153
+ });
154
+ r.useExtension(EXT_TENANT_DATA, "mail-thread", {
155
+ destroy: mailThreadTenantDestroyHook,
156
+ escapeHatch: { reason: INBOUND_MAIL_TENANT_DESTROY_ARCHIVE_REASON },
157
+ });
158
+ // Data-Retention (#957) — perTenant fan-out (one run per tenant, like
159
+ // data-retention:retention-cleanup): runInboundMailRetention's raw query
160
+ // helpers need the unfiltered runner, so the handler declares escapeHatch
161
+ // and reaches for ctx.db.unsafeRaw(). Without this cron, read_inbound_messages
162
+ // + the bodies grow unbounded (GDPR Art. 5).
163
+ const INBOUND_MAIL_RETENTION_REASON = "inbound-mail retention runs raw deletes; every statement filters by this run's tenantId";
164
+ r.job({
165
+ name: "inbound-mail-retention",
166
+ trigger: { cron: "0 3 * * *" },
167
+ perTenant: true,
168
+ concurrency: "skip",
169
+ escapeHatch: { reason: INBOUND_MAIL_RETENTION_REASON },
170
+ handler: async (_payload, ctx) => {
171
+ if (!ctx.registry) {
172
+ throw new Error("inbound-mail-retention: ctx.registry required (JobContext incomplete)");
188
173
  }
189
- const provider = await providerPromise;
190
- await provider.delete(bodyRef);
191
- };
192
- const report = await runInboundMailRetention({
193
- db: retentionDb,
194
- tenantId,
195
- now: T.Now.instant(),
196
- deleteBodyObject,
197
- });
198
- if (report.bodyObjectErrors > 0) {
199
- // biome-ignore lint/suspicious/noConsole: operator-visibility for failed body deletes
200
- console.warn(`[inbound-mail-foundation:inbound-mail-retention] tenant=${tenantId} bodyObjectErrors=${report.bodyObjectErrors}`);
201
- }
202
- },
203
- });
204
- });
174
+ const tenantId = ctx.systemUser?.tenantId ?? ctx._tenantId;
175
+ if (tenantId === undefined) {
176
+ // skip: cron fired without a perTenant fan-out tenant — nothing scoped
177
+ return;
178
+ }
179
+ const T = (await import("@cosmicdrift/kumiko-framework/time")).getTemporal();
180
+ const retentionDb = ctx.db.unsafeRaw(); // @cast-boundary db-operator — jobs never run inside a DbTx
181
+ // Body-Objekte liegen in file-foundation. Provider lazy + memoized —
182
+ // heute schreibt der Ingest kein bodyRef (storeBody-Hook ungebunden),
183
+ // der Provider wird also nie gebaut. Sobald storeBody wired ist, raeumt
184
+ // die Retention die Bodies mit ab.
185
+ let providerPromise = null;
186
+ const deleteBodyObject = async (bodyRef) => {
187
+ if (!providerPromise) {
188
+ providerPromise = createFileProviderForTenant({ config: ctx.config, registry: ctx.registry }, tenantId);
189
+ }
190
+ const provider = await providerPromise;
191
+ await provider.delete(bodyRef);
192
+ };
193
+ const report = await runInboundMailRetention({
194
+ db: retentionDb,
195
+ tenantId,
196
+ now: T.Now.instant(),
197
+ deleteBodyObject,
198
+ });
199
+ if (report.bodyObjectErrors > 0) {
200
+ // biome-ignore lint/suspicious/noConsole: operator-visibility for failed body deletes
201
+ console.warn(`[inbound-mail-foundation:inbound-mail-retention] tenant=${tenantId} bodyObjectErrors=${report.bodyObjectErrors}`);
202
+ }
203
+ },
204
+ });
205
+ }, { dedupeOptions: {} });
206
+ }
207
+ /** @deprecated Use createInboundMailFoundationFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
208
+ export const inboundMailFoundationFeature = createInboundMailFoundationFeature();
@@ -19,9 +19,8 @@
19
19
  //
20
20
  // SystemAdmin-only: Aufrufer ist der Watch/Sync-Supervisor bzw. der
21
21
  // Poll-Cron mit programmatic SystemUser — nie ein Tenant-Request.
22
- import { countWhere } from "@cosmicdrift/kumiko-framework/bun-db";
23
22
  import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
24
- import { acquireNamespacedAdvisoryLock } from "@cosmicdrift/kumiko-framework/db";
23
+ import { acquireNamespacedAdvisoryLock, countWhere } from "@cosmicdrift/kumiko-framework/db";
25
24
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
26
25
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
27
26
  import * as z from "zod";
@@ -3,7 +3,7 @@ export { createInboundMailConnectRoutes, type InboundMailConnectRoutesOptions, }
3
3
  export { INBOUND_MAIL_FOUNDATION_FEATURE, INBOUND_MAIL_PROVIDER_EXTENSION, type InboundMailAccountStatus, InboundMailAccountStatuses, type InboundMailAuthMethod, InboundMailAuthMethods, InboundMailFoundationHandlers, InboundMailFoundationQueries, inboundCredentialSecretKey, } from "./constants.js";
4
4
  export { INBOUND_MESSAGE_PII_FIELDS, inboundMessageEntity, MAIL_ACCOUNT_PII_FIELDS, MAIL_THREAD_PII_FIELDS, mailAccountEntity, mailThreadEntity, seenMessageEntity, seenMessageTable, syncCursorEntity, syncCursorTable, } from "./entities.js";
5
5
  export { INBOUND_MESSAGE_AGGREGATE_TYPE, INBOUND_MESSAGE_RECEIVED_EVENT_QN, INBOUND_MESSAGE_RECEIVED_EVENT_SHORT, type InboundMessageEventHeaders, type InboundMessageEventPayload, inboundMessageEventPayloadSchema, MAIL_ACCOUNT_AGGREGATE_TYPE, MAIL_ACCOUNT_CONNECTED_EVENT_QN, MAIL_ACCOUNT_CONNECTED_EVENT_SHORT, MAIL_ACCOUNT_DISCONNECTED_EVENT_QN, MAIL_ACCOUNT_DISCONNECTED_EVENT_SHORT, MAIL_ACCOUNT_UPDATED_EVENT_QN, MAIL_ACCOUNT_UPDATED_EVENT_SHORT, MAIL_THREAD_AGGREGATE_TYPE, MAIL_THREAD_UPDATED_EVENT_QN, MAIL_THREAD_UPDATED_EVENT_SHORT, type MailAccountEventHeaders, type MailAccountEventPayload, type MailThreadEventPayload, mailAccountEventPayloadSchema, mailThreadEventPayloadSchema, } from "./events.js";
6
- export { inboundMailFoundationFeature } from "./feature.js";
6
+ export { createInboundMailFoundationFeature, inboundMailFoundationFeature } from "./feature.js";
7
7
  export { createOAuthAccessTokenManager, inboundRefreshTokenSecretOptions, type OAuthAccessTokenManager, type OAuthAccessTokenManagerDeps, usesFoundationManagedOAuth, } from "./oauth-access-token.js";
8
8
  export { type OAuthStatePayload, signOAuthState, type VerifyOAuthStateResult, verifyOAuthState, } from "./oauth-state.js";
9
9
  export { inboundMessagesProjectionTable, mailAccountsProjectionTable, mailThreadsProjectionTable, } from "./projection.js";
@@ -4,7 +4,7 @@ export { createInboundMailConnectRoutes, } from "./connect-routes.js";
4
4
  export { INBOUND_MAIL_FOUNDATION_FEATURE, INBOUND_MAIL_PROVIDER_EXTENSION, InboundMailAccountStatuses, InboundMailAuthMethods, InboundMailFoundationHandlers, InboundMailFoundationQueries, inboundCredentialSecretKey, } from "./constants.js";
5
5
  export { INBOUND_MESSAGE_PII_FIELDS, inboundMessageEntity, MAIL_ACCOUNT_PII_FIELDS, MAIL_THREAD_PII_FIELDS, mailAccountEntity, mailThreadEntity, seenMessageEntity, seenMessageTable, syncCursorEntity, syncCursorTable, } from "./entities.js";
6
6
  export { INBOUND_MESSAGE_AGGREGATE_TYPE, INBOUND_MESSAGE_RECEIVED_EVENT_QN, INBOUND_MESSAGE_RECEIVED_EVENT_SHORT, inboundMessageEventPayloadSchema, MAIL_ACCOUNT_AGGREGATE_TYPE, MAIL_ACCOUNT_CONNECTED_EVENT_QN, MAIL_ACCOUNT_CONNECTED_EVENT_SHORT, MAIL_ACCOUNT_DISCONNECTED_EVENT_QN, MAIL_ACCOUNT_DISCONNECTED_EVENT_SHORT, MAIL_ACCOUNT_UPDATED_EVENT_QN, MAIL_ACCOUNT_UPDATED_EVENT_SHORT, MAIL_THREAD_AGGREGATE_TYPE, MAIL_THREAD_UPDATED_EVENT_QN, MAIL_THREAD_UPDATED_EVENT_SHORT, mailAccountEventPayloadSchema, mailThreadEventPayloadSchema, } from "./events.js";
7
- export { inboundMailFoundationFeature } from "./feature.js";
7
+ export { createInboundMailFoundationFeature, inboundMailFoundationFeature } from "./feature.js";
8
8
  export { createOAuthAccessTokenManager, inboundRefreshTokenSecretOptions, usesFoundationManagedOAuth, } from "./oauth-access-token.js";
9
9
  export { signOAuthState, verifyOAuthState, } from "./oauth-state.js";
10
10
  export { inboundMessagesProjectionTable, mailAccountsProjectionTable, mailThreadsProjectionTable, } from "./projection.js";