@cosmicdrift/kumiko-samples 0.386.0 → 0.388.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (530) hide show
  1. package/LICENSE +21 -57
  2. package/README.md +1 -1
  3. package/package.json +2 -2
  4. package/packages/bundled-features/package.json +9 -2
  5. package/packages/bundled-features/src/agent-tools/changes.json +6 -0
  6. package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
  7. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +9 -9
  8. package/packages/bundled-features/src/audit/changes.json +12 -0
  9. package/packages/bundled-features/src/audit/handlers/audit-events.ts +1 -1
  10. package/packages/bundled-features/src/audit/screens.ts +1 -1
  11. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
  12. package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
  13. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
  14. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
  16. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
  17. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
  18. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
  19. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
  20. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
  21. package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
  22. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
  23. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
  24. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
  25. package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
  26. package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
  27. package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
  28. package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
  29. package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
  30. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
  31. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
  32. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
  33. package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
  34. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
  35. package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
  36. package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
  37. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
  38. package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
  39. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
  40. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
  41. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
  42. package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
  43. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
  44. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
  45. package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
  46. package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
  47. package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
  48. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
  49. package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
  50. package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
  51. package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
  52. package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
  53. package/packages/bundled-features/src/auth-mfa/feature-exports.ts +2 -2
  54. package/packages/bundled-features/src/auth-mfa/feature.ts +6 -0
  55. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
  56. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
  57. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
  58. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  59. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
  60. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
  61. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
  62. package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
  63. package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
  64. package/packages/bundled-features/src/auth-mfa/web/i18n.ts +9 -4
  65. package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
  66. package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
  67. package/packages/bundled-features/src/auth-mfa/web/mfa-code-or-recovery-field.tsx +68 -0
  68. package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +23 -13
  69. package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +23 -13
  70. package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +15 -6
  71. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +71 -24
  72. package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
  73. package/packages/bundled-features/src/billing-foundation/changes.json +55 -0
  74. package/packages/bundled-features/src/billing-foundation/checkout-core.ts +32 -42
  75. package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +8 -2
  76. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +2 -0
  77. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +14 -3
  78. package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +3 -4
  79. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +12 -6
  80. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +35 -19
  81. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +12 -8
  82. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +14 -8
  83. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +5 -0
  84. package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +8 -10
  85. package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
  86. package/packages/bundled-features/src/billing-foundation/events.ts +23 -21
  87. package/packages/bundled-features/src/billing-foundation/feature.ts +45 -44
  88. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
  89. package/packages/bundled-features/src/billing-foundation/handlers/billing-plans.query.ts +71 -6
  90. package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +19 -17
  91. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
  92. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
  93. package/packages/bundled-features/src/billing-foundation/handlers/plan-switch-preview.query.ts +65 -0
  94. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +29 -22
  95. package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +44 -8
  96. package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +72 -82
  97. package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +14 -1
  98. package/packages/bundled-features/src/billing-foundation/i18n.ts +22 -0
  99. package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
  100. package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +62 -27
  101. package/packages/bundled-features/src/billing-foundation/plan-switch.ts +84 -0
  102. package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
  103. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
  104. package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
  105. package/packages/bundled-features/src/billing-foundation/types.ts +144 -66
  106. package/packages/bundled-features/src/billing-foundation/validate-options.ts +19 -0
  107. package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +123 -51
  108. package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +89 -42
  109. package/packages/bundled-features/src/billing-foundation/web/constants.ts +4 -0
  110. package/packages/bundled-features/src/billing-foundation/web/plan-price.ts +55 -0
  111. package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +224 -0
  112. package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
  113. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
  114. package/packages/bundled-features/src/cap-counter/cap-reservation.ts +5 -1
  115. package/packages/bundled-features/src/cap-counter/changes.json +18 -0
  116. package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
  117. package/packages/bundled-features/src/cap-counter/enforce-cap.ts +66 -68
  118. package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
  119. package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
  120. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
  121. package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
  122. package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -2
  123. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
  124. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
  125. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  126. package/packages/bundled-features/src/channel-email/changes.json +6 -0
  127. package/packages/bundled-features/src/channel-email/feature.ts +1 -1
  128. package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
  129. package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
  130. package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
  131. package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
  132. package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
  133. package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
  134. package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
  135. package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
  136. package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
  137. package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
  138. package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
  139. package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
  140. package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
  141. package/packages/bundled-features/src/config/changes.json +12 -0
  142. package/packages/bundled-features/src/config/handlers/cascade.query.ts +4 -2
  143. package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
  144. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
  145. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  146. package/packages/bundled-features/src/config/handlers/values.query.ts +4 -2
  147. package/packages/bundled-features/src/config/resolver.ts +4 -4
  148. package/packages/bundled-features/src/config/table.ts +10 -10
  149. package/packages/bundled-features/src/config/write-helpers.ts +28 -9
  150. package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
  151. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +23 -21
  152. package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
  153. package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
  154. package/packages/bundled-features/src/custom-fields/events.ts +7 -8
  155. package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
  156. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
  157. package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
  158. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
  159. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
  160. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
  161. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
  162. package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
  163. package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
  164. package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
  165. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
  166. package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
  167. package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
  168. package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
  169. package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
  170. package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
  171. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
  172. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
  173. package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
  174. package/packages/bundled-features/src/data-retention/feature.ts +11 -18
  175. package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
  176. package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
  177. package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
  178. package/packages/bundled-features/src/data-retention/presets.ts +18 -23
  179. package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
  180. package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
  181. package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
  182. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
  183. package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
  184. package/packages/bundled-features/src/delivery/changes.json +30 -0
  185. package/packages/bundled-features/src/delivery/channel-context.ts +9 -3
  186. package/packages/bundled-features/src/delivery/db/queries/preferences.ts +2 -2
  187. package/packages/bundled-features/src/delivery/delivery-service.ts +35 -5
  188. package/packages/bundled-features/src/delivery/feature.ts +3 -3
  189. package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
  190. package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +20 -1
  191. package/packages/bundled-features/src/delivery/handlers/unsubscribe-page-chrome.query.ts +1 -1
  192. package/packages/bundled-features/src/delivery/tables.ts +9 -8
  193. package/packages/bundled-features/src/delivery/unsubscribe-link.ts +3 -1
  194. package/packages/bundled-features/src/delivery/unsubscribe-page-strings.ts +1 -0
  195. package/packages/bundled-features/src/delivery/unsubscribe-page.ts +6 -0
  196. package/packages/bundled-features/src/delivery/unsubscribe.ts +40 -19
  197. package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
  198. package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
  199. package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
  200. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
  201. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
  202. package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
  203. package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
  204. package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
  205. package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
  206. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
  207. package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
  208. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  209. package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
  210. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  211. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  212. package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
  213. package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
  214. package/packages/bundled-features/src/file-foundation/index.ts +1 -0
  215. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -1
  216. package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
  217. package/packages/bundled-features/src/file-provider-s3/env-named-feature.ts +0 -2
  218. package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
  219. package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
  220. package/packages/bundled-features/src/files/README.md +1 -1
  221. package/packages/bundled-features/src/files/feature.ts +6 -6
  222. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
  223. package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
  224. package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
  225. package/packages/bundled-features/src/folders/feature.ts +1 -1
  226. package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
  227. package/packages/bundled-features/src/folders/web/tree.ts +5 -5
  228. package/packages/bundled-features/src/form-draft/changes.json +6 -0
  229. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +3 -3
  230. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
  231. package/packages/bundled-features/src/form-draft/decrypt-draft.ts +4 -1
  232. package/packages/bundled-features/src/form-draft/entity.ts +3 -3
  233. package/packages/bundled-features/src/form-draft/feature.ts +4 -5
  234. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
  235. package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
  236. package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
  237. package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
  238. package/packages/bundled-features/src/host-routing/README.md +1 -1
  239. package/packages/bundled-features/src/host-routing/changes.json +6 -0
  240. package/packages/bundled-features/src/host-routing/dispatch.ts +16 -17
  241. package/packages/bundled-features/src/host-routing/options.ts +2 -1
  242. package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
  243. package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
  244. package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
  245. package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
  246. package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
  247. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
  248. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
  249. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
  250. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
  251. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
  252. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
  253. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
  254. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
  255. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
  256. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +27 -15
  257. package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +0 -1
  258. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
  259. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
  260. package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
  261. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
  262. package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +0 -3
  263. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
  264. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +70 -70
  265. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +101 -84
  266. package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
  267. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
  268. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
  269. package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
  270. package/packages/bundled-features/src/jobs/changes.json +13 -0
  271. package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
  272. package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
  273. package/packages/bundled-features/src/jobs/events.ts +1 -1
  274. package/packages/bundled-features/src/jobs/feature.ts +20 -11
  275. package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
  276. package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
  277. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  278. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
  279. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  280. package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +45 -40
  281. package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +63 -59
  282. package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
  283. package/packages/bundled-features/src/jobs/job-run-logger.ts +19 -19
  284. package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
  285. package/packages/bundled-features/src/jobs/screens.ts +1 -1
  286. package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
  287. package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
  288. package/packages/bundled-features/src/ledger/entity.ts +5 -5
  289. package/packages/bundled-features/src/ledger/feature.ts +3 -3
  290. package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
  291. package/packages/bundled-features/src/ledger/recurring.ts +12 -12
  292. package/packages/bundled-features/src/ledger/schemas.ts +1 -1
  293. package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
  294. package/packages/bundled-features/src/ledger/web/index.ts +7 -7
  295. package/packages/bundled-features/src/legal-pages/README.md +1 -1
  296. package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
  297. package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
  298. package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
  299. package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
  300. package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
  301. package/packages/bundled-features/src/mail-foundation/feature.ts +31 -30
  302. package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
  303. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
  304. package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
  305. package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
  306. package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
  307. package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
  308. package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
  309. package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
  310. package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
  311. package/packages/bundled-features/src/managed-pages/table.ts +16 -15
  312. package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
  313. package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
  314. package/packages/bundled-features/src/notes-history/entity.ts +4 -4
  315. package/packages/bundled-features/src/notes-history/feature.ts +2 -2
  316. package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
  317. package/packages/bundled-features/src/page-render/layout.ts +9 -9
  318. package/packages/bundled-features/src/page-render/markdown.ts +11 -11
  319. package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
  320. package/packages/bundled-features/src/personal-access-tokens/feature.ts +9 -6
  321. package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
  322. package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
  323. package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
  324. package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
  325. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  326. package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
  327. package/packages/bundled-features/src/renderer-pdf/changes.json +6 -0
  328. package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +27 -10
  329. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  330. package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
  331. package/packages/bundled-features/src/seo/feature.ts +3 -3
  332. package/packages/bundled-features/src/sessions/constants.ts +1 -1
  333. package/packages/bundled-features/src/sessions/feature.ts +16 -11
  334. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
  335. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
  336. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  337. package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
  338. package/packages/bundled-features/src/sessions/screens.ts +2 -2
  339. package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
  340. package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
  341. package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
  342. package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +11 -1
  343. package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
  344. package/packages/bundled-features/src/shared/index.ts +1 -1
  345. package/packages/bundled-features/src/shared/mail-template-body.ts +10 -3
  346. package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
  347. package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
  348. package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
  349. package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
  350. package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
  351. package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
  352. package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -0
  353. package/packages/bundled-features/src/subscription-dodo/changes.json +14 -0
  354. package/packages/bundled-features/src/subscription-dodo/client.ts +45 -0
  355. package/packages/bundled-features/src/subscription-dodo/constants.ts +24 -0
  356. package/packages/bundled-features/src/subscription-dodo/feature.ts +207 -0
  357. package/packages/bundled-features/src/subscription-dodo/index.ts +16 -0
  358. package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +345 -0
  359. package/packages/bundled-features/src/subscription-dodo/runtime.ts +126 -0
  360. package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +262 -0
  361. package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
  362. package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
  363. package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
  364. package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
  365. package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
  366. package/packages/bundled-features/src/subscription-stripe/changes.json +12 -0
  367. package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
  368. package/packages/bundled-features/src/subscription-stripe/feature.ts +45 -43
  369. package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
  370. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
  371. package/packages/bundled-features/src/subscription-stripe/runtime.ts +9 -8
  372. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
  373. package/packages/bundled-features/src/tags/feature.ts +3 -3
  374. package/packages/bundled-features/src/tags/screens.ts +1 -1
  375. package/packages/bundled-features/src/template-resolver/api.ts +41 -14
  376. package/packages/bundled-features/src/template-resolver/changes.json +48 -0
  377. package/packages/bundled-features/src/template-resolver/compile-document-body.ts +1 -0
  378. package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
  379. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +3 -2
  380. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +27 -5
  381. package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +3 -2
  382. package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +3 -2
  383. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +8 -7
  384. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -20
  385. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +15 -5
  386. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
  387. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
  388. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-counts.query.ts +9 -5
  389. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +8 -6
  390. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +44 -5
  391. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
  392. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
  393. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +37 -26
  394. package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +7 -2
  395. package/packages/bundled-features/src/template-resolver/seeding.ts +36 -18
  396. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +22 -4
  397. package/packages/bundled-features/src/template-resolver/table.ts +13 -14
  398. package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +0 -11
  399. package/packages/bundled-features/src/template-resolver/template-meta.ts +12 -4
  400. package/packages/bundled-features/src/template-resolver/template-preview.ts +11 -3
  401. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
  402. package/packages/bundled-features/src/template-resolver/web/constants.ts +7 -5
  403. package/packages/bundled-features/src/template-resolver/web/index.ts +7 -0
  404. package/packages/bundled-features/src/template-resolver/web/mail-layout-constants.ts +8 -0
  405. package/packages/bundled-features/src/tenant/changes.json +6 -0
  406. package/packages/bundled-features/src/tenant/feature.ts +7 -7
  407. package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +10 -4
  408. package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
  409. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  410. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
  411. package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +8 -5
  412. package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
  413. package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
  414. package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
  415. package/packages/bundled-features/src/tenant/screens.ts +1 -1
  416. package/packages/bundled-features/src/tenant/seeding.ts +19 -18
  417. package/packages/bundled-features/src/tenant/testing.ts +5 -6
  418. package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
  419. package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
  420. package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
  421. package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
  422. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  423. package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
  424. package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
  425. package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
  426. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  427. package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
  428. package/packages/bundled-features/src/tenant-lifecycle/constants.ts +5 -0
  429. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
  430. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +7 -3
  431. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
  432. package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
  433. package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
  434. package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
  435. package/packages/bundled-features/src/tier-engine/changes.json +6 -0
  436. package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
  437. package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
  438. package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
  439. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
  440. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
  441. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +12 -9
  442. package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
  443. package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
  444. package/packages/bundled-features/src/tier-engine/web/constants.ts +3 -3
  445. package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
  446. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
  447. package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
  448. package/packages/bundled-features/src/user/schema/user.ts +45 -44
  449. package/packages/bundled-features/src/user/seeding.ts +40 -40
  450. package/packages/bundled-features/src/user/testing.ts +3 -3
  451. package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
  452. package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
  453. package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
  454. package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
  455. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
  456. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
  457. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
  458. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
  459. package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
  460. package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
  461. package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
  462. package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
  463. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
  464. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
  465. package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
  466. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
  467. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
  468. package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
  469. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
  470. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
  471. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +169 -280
  472. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +87 -108
  473. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
  474. package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
  475. package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
  476. package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
  477. package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
  478. package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
  479. package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
  480. package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
  481. package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
  482. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
  483. package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
  484. package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
  485. package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
  486. package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
  487. package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
  488. package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
  489. package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
  490. package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
  491. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
  492. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
  493. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
  494. package/packages/bundled-features/src/user-profile/index.ts +3 -3
  495. package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
  496. package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
  497. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
  498. package/packages/bundled-features/src/waitlist/changes.json +18 -0
  499. package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +2 -1
  500. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +5 -5
  501. package/packages/bundled-features/src/waitlist/import.ts +5 -0
  502. package/packages/bundled-features/src/waitlist/legacy-migrate.ts +22 -3
  503. package/packages/bundled-features/src/waitlist/link-on-signup.ts +20 -10
  504. package/packages/bundled-features/src/waitlist/options.ts +3 -0
  505. package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
  506. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
  507. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
  508. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  509. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
  510. package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
  511. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
  512. package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
  513. package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
  514. package/samples/apps/styleguide/src/features/widgets/i18n.ts +11 -0
  515. package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +24 -0
  516. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +5 -0
  517. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +90 -15
  518. package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +16 -6
  519. package/samples/apps/use-all-bundled/feature-manifest.json +120 -3
  520. package/samples/apps/use-all-bundled/src/run-config.ts +9 -3
  521. package/samples/recipes/auth-mfa/README.md +5 -0
  522. package/samples/recipes/auth-mfa/package.json +5 -2
  523. package/samples/recipes/auth-mfa/src/web/confirmation-code-step.tsx +49 -0
  524. package/samples/recipes/auth-mfa/tsconfig.json +8 -0
  525. package/samples/recipes/env-composition/README.md +3 -1
  526. package/samples/recipes/env-composition/src/env.ts +2 -0
  527. package/samples/recipes/legal-pages/README.md +1 -1
  528. package/samples/recipes/rate-limiting/README.md +18 -0
  529. package/samples/recipes/rate-limiting/src/feature.ts +12 -0
  530. package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +0 -12
@@ -1,17 +1,16 @@
1
- // Zod-Schema fuer RetentionOverride mit .strict() — Sprint 2.D2.5 (M2+M3).
1
+ // Strict Zod schema for RetentionOverride.
2
2
  //
3
- // Symmetrisch zu compliance-profiles override-schema.ts (S1.9 Z3).
4
- // Sub-Sprint-Pattern (advisor-pinned): bei jedem User-konfigurierbarem
5
- // JSON-Override → strict-Zod + enum-validated.
3
+ // Symmetric with compliance-profiles override-schema.ts.
4
+ // Every user-configurable JSON override uses strict Zod validation
5
+ // with enum validation.
6
6
  //
7
- // Schuetzt vor:
8
- // - Top-Level-Tippfehler ("keepfor" statt "keepFor") — strict()-Reject
9
- // - Strategy-Enum-Drift ("delete" statt "hardDelete") — z.enum-Reject
10
- // - keepFor-Format-Drift ("30days" statt "30d") — regex-Reject
7
+ // Protects against:
8
+ // - Top-level typos ("keepfor" instead of "keepFor") — strict() rejects them
9
+ // - Strategy enum drift ("delete" instead of "hardDelete") — z.enum rejects it
10
+ // - keepFor format drift ("30days" instead of "30d") — regex rejects it
11
11
  //
12
- // Tenant-Override darf alle drei Properties weglassen (Resolver
13
- // fallback auf Preset/Entity-Default), aber WAS gesetzt ist muss
14
- // gueltig sein.
12
+ // A tenant override may omit all three properties (the resolver falls back
13
+ // to the preset/entity default), but every property that is set must be valid.
15
14
 
16
15
  import * as z from "zod";
17
16
 
@@ -20,10 +19,9 @@ const KEEP_FOR_PATTERN = /^\d+[hdwmy]$/;
20
19
  const retentionStrategySchema = z.enum(["hardDelete", "softDelete", "anonymize", "blockDelete"]);
21
20
 
22
21
  /**
23
- * RetentionOverride-Zod-Schema mit .strict() — fuer (a) set-override-
24
- * Handler-Validation (S2.D3) und (b) DB-Loader im Cleanup-Job (S2.D2b)
25
- * der invalides JSON aus der config-Spalte loggt + skipt statt mit
26
- * undefined behavior weiterzumachen.
22
+ * Strict Zod schema for RetentionOverride, used by the set-override handler
23
+ * and the cleanup job's DB loader. The loader logs invalid JSON from the
24
+ * config column and skips it instead of continuing with undefined behavior.
27
25
  */
28
26
  export const retentionOverrideSchema = z
29
27
  .object({
@@ -1,14 +1,10 @@
1
- // Retention-Presets — vorgefertigte Bündel von per-Entity-Aufbewahrungs-
2
- // Konfigurationen pro Compliance-Regime. Tenant-Admin wählt EINES (analog
3
- // zu compliance-profiles); Entity-Default + Tenant-Override liegen als
4
- // Schichten 1+3 darüber (siehe resolver.ts).
1
+ // Retention presets — predefined bundles of per-entity retention
2
+ // configurations for each compliance regime. The tenant admin selects ONE
3
+ // (analogous to compliance profiles); the entity default and tenant override
4
+ // are layered above it as layers 1 and 3 (see resolver.ts).
5
5
  //
6
- // Plan-Roadmap docs/plans/datenschutz/core-data-retention.md hat 8 Presets
7
- // vorgeschlagen. Sprint-2-MVP: 3 Production-Presets + 1 dev-Default,
8
- // rest folgt on-demand wenn Customer fragt.
9
- //
10
- // Presets sind pure Daten — keine Logik. Erweitern = Constant erweitern,
11
- // kein Code-Eingriff.
6
+ // The presets are pure data with no logic. Extending them means extending the
7
+ // constant; no code changes are required.
12
8
 
13
9
  import type { RetentionDef } from "@cosmicdrift/kumiko-framework/engine";
14
10
 
@@ -23,25 +19,25 @@ export type RetentionPreset = Readonly<Record<string, RetentionDef>>;
23
19
  export type RetentionPresetKey = "default" | "dsgvo-basic" | "dsgvo-hgb" | "swiss-dsg";
24
20
 
25
21
  /**
26
- * MVP-Set für Sprint 2. Erweiterungen (hipaa, ccpa, aggressive-gdpr,
27
- * pipeda-default, ca-quebec-l25) kommen on-demand.
22
+ * Available presets. Additional options (hipaa, ccpa, aggressive-gdpr,
23
+ * pipeda-default, ca-quebec-l25) can be added on demand.
28
24
  */
29
25
  export const RETENTION_PRESETS: Readonly<Record<RetentionPresetKey, RetentionPreset>> = {
30
- // Default — keine Auto-Aktion. Nur sinnvoll für Dev/Staging.
31
- // Production-Tenant der "default" stehen lässt → Cleanup-Job
32
- // schreibt Audit-Eintrag "skipped: no preset selected".
26
+ // Default — no automatic action; intended only for Dev/Staging.
27
+ // A production tenant that leaves `default` selected causes the cleanup job
28
+ // to write the audit entry "skipped: no preset selected".
33
29
  default: {},
34
30
 
35
- // DSGVO Basic — Datenminimierung ohne Buchhaltungspflichten.
31
+ // DSGVO Basic — data minimization without accounting retention obligations.
36
32
  "dsgvo-basic": {
37
33
  "audit-log": { keepFor: "1y", strategy: "hardDelete", reference: "createdAt" },
38
34
  session: { keepFor: "30d", strategy: "hardDelete", reference: "lastSeenAt" },
39
35
  "http-log": { keepFor: "90d", strategy: "hardDelete", reference: "createdAt" },
40
36
  },
41
37
 
42
- // DSGVO + HGB — deutsche Aufbewahrungspflichten überlagert. Order
43
- // wird anonymisiert (PII raus, Geschäftsdaten bleiben), Invoice +
44
- // Booking sind blockDelete bis 10 Jahre, dann Anonymize.
38
+ // DSGVO + HGB — German statutory retention obligations take precedence.
39
+ // Orders are anonymized (PII removed while business data remains); invoices
40
+ // and bookings are blockDelete for up to 10 years, then anonymized.
45
41
  "dsgvo-hgb": {
46
42
  "audit-log": { keepFor: "1y", strategy: "hardDelete", reference: "createdAt" },
47
43
  session: { keepFor: "30d", strategy: "hardDelete", reference: "lastSeenAt" },
@@ -52,7 +48,7 @@ export const RETENTION_PRESETS: Readonly<Record<RetentionPresetKey, RetentionPre
52
48
  order: { keepFor: "6y", strategy: "anonymize", reference: "completedAt" },
53
49
  },
54
50
 
55
- // Schweizer DSG — ähnlich DSGVO mit OR Art. 958f Aufbewahrung.
51
+ // Swiss DSG — similar to DSGVO, with retention under OR Art. 958f.
56
52
  "swiss-dsg": {
57
53
  "audit-log": { keepFor: "1y", strategy: "hardDelete", reference: "createdAt" },
58
54
  session: { keepFor: "30d", strategy: "hardDelete", reference: "lastSeenAt" },
@@ -61,9 +57,8 @@ export const RETENTION_PRESETS: Readonly<Record<RetentionPresetKey, RetentionPre
61
57
  } satisfies Readonly<Record<RetentionPresetKey, RetentionPreset>>;
62
58
 
63
59
  /**
64
- * Auswählbare Presets für den Onboarding-Banner. "default" ist Migration-
65
- * Edge-Case und wird nicht angezeigt — Production-Tenants wählen ein
66
- * echtes Preset.
60
+ * Presets available in the onboarding banner. `default` is a migration edge
61
+ * case and is not displayed; production tenants must choose an actual preset.
67
62
  */
68
63
  export const SELECTABLE_RETENTION_PRESETS: readonly RetentionPresetKey[] = [
69
64
  "dsgvo-basic",
@@ -13,7 +13,7 @@ import { type EffectiveRetentionPolicy, resolveRetentionPolicy } from "./resolve
13
13
  import { tenantRetentionOverrideTable } from "./schema/tenant-retention-override.ts";
14
14
 
15
15
  export interface ResolveForTenantArgs {
16
- // fw#2914 — a EXT_USER_DATA hook's ctx.db is a TenantDb (method-form);
16
+ // A EXT_USER_DATA hook's ctx.db is a TenantDb (method-form);
17
17
  // the cleanup cron's own db is still a raw DbRunner. Both are valid here —
18
18
  // this lookup is a plain tenant-scoped read either way.
19
19
  readonly db: DbRunner | TenantDb;
@@ -1,15 +1,16 @@
1
- // Leitet den Layer-2 Retention-Preset eines Tenants aus seinem Compliance-
2
- // Profile ab. Der retention-cleanup-Cron ruft das pro fan-out-Tenant.
1
+ // Derives a tenant's Layer-2 retention preset from its compliance profile.
2
+ // The retention-cleanup cron calls this once per fan-out tenant.
3
3
  //
4
- // **Soft-Dependency:** data-retention bleibt standalone-mountbar. Nur wenn
5
- // compliance-profiles mit-gemountet ist (seine Entity registriert), wird ein
6
- // Preset abgeleitet — sonst null, dann greifen ausschliesslich Entity-Defaults
7
- // (Layer 1) + per-Entity Tenant-Overrides (Layer 3). Kein r.requires, damit
8
- // eine App data-retention auch ohne Compliance-Profiles nutzen kann.
4
+ // **Soft dependency:** data-retention remains mountable standalone. A preset is
5
+ // derived only when compliance-profiles is mounted as well (its entity is
6
+ // registered); otherwise this returns null, leaving only entity defaults
7
+ // (Layer 1) and per-entity tenant overrides (Layer 3). No r.requires is used,
8
+ // so an app can use data-retention without compliance-profiles.
9
9
  //
10
- // Die Map ist intentional: Compliance-Profile (Region) und Retention-Preset
11
- // sind beide um dieselben drei Regimes gebaut (EU/CH/DE-HGB). swiss-dsg ist
12
- // sogar namensgleich. minimal-no-region → "default" (No-Op-Preset).
10
+ // The mapping is intentional: compliance profiles (regions) and retention
11
+ // presets are both built around the same three regimes (EU/CH/DE-HGB).
12
+ // swiss-dsg even has the same name. minimal-no-region maps to "default"
13
+ // (a no-op preset).
13
14
 
14
15
  import type { ComplianceProfileKey } from "@cosmicdrift/kumiko-framework/compliance";
15
16
  import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
@@ -17,8 +18,8 @@ import type { Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
17
18
  import { resolveProfileForTenant } from "../compliance-profiles/index.ts";
18
19
  import type { RetentionPresetKey } from "./presets.ts";
19
20
 
20
- // r.entity-Name aus compliance-profiles/feature.ts — Probe ob das Feature
21
- // gemountet ist, bevor wir seine Tabelle lesen (sonst wirft fetchOne).
21
+ // Entity name from compliance-profiles/feature.ts — check that the feature is
22
+ // mounted before reading its table; otherwise fetchOne throws.
22
23
  const COMPLIANCE_PROFILE_ENTITY = "tenant-compliance-profile";
23
24
 
24
25
  const PROFILE_TO_PRESET: Readonly<Record<ComplianceProfileKey, RetentionPresetKey>> = {
@@ -43,12 +44,12 @@ export async function resolveTenantRetentionPreset(
43
44
  if (!args.registry.getEntity(COMPLIANCE_PROFILE_ENTITY)) {
44
45
  return null;
45
46
  }
46
- // resolveProfileForTenant liest die 1:1-Profile-Row des Tenants (boot-DB,
47
- // by tenantId gefiltert) und faellt auf minimal-no-region zurueck wenn der
48
- // Tenant noch kein Profile gewaehlt hat.
47
+ // resolveProfileForTenant reads the tenant's 1:1 profile row from the boot DB,
48
+ // filtered by tenantId, and falls back to minimal-no-region when the tenant
49
+ // has not selected a profile yet.
49
50
  const effective = await resolveProfileForTenant({ db: args.db, tenantId: args.tenantId });
50
51
  return PROFILE_TO_PRESET[effective.profile.key] ?? null;
51
52
  }
52
53
 
53
- // Exportiert fuer den Unit-Test (Map-Vollstaendigkeit gegen ComplianceProfileKey).
54
+ // Exported for the unit test that checks completeness against ComplianceProfileKey.
54
55
  export { PROFILE_TO_PRESET };
@@ -1,30 +1,30 @@
1
- // 3-Schicht-Retention-Resolver.
1
+ // Three-layer retention resolver.
2
2
  //
3
- // Layer 1: Entity-Default — Feature-Author setzt r.entity({retention})
4
- // Layer 2: Tenant-Preset — Tenant-Admin wählt Bundle (RETENTION_PRESETS)
5
- // Layer 3: Tenant-Override — per (tenantId, entityName) JSON-Override
3
+ // Layer 1: Entity default — Feature author sets r.entity({retention})
4
+ // Layer 2: Tenant preset — Tenant admin selects a bundle (RETENTION_PRESETS)
5
+ // Layer 3: Tenant override — per (tenantId, entityName) JSON override
6
6
  //
7
- // Resolver-Reihenfolge:
7
+ // Resolution order:
8
8
  // effective = override(tenantId, entityName)
9
9
  // ?? preset[tenant.retentionPreset][entityName]
10
- // ?? entity.retention (Code-Default aus EntityDefinition)
11
- // ?? null (keine Auto-Aktion)
10
+ // ?? entity.retention (code default from EntityDefinition)
11
+ // ?? null (no automatic action)
12
12
  //
13
- // Cleanup-Job in S2.D2 ruft das pro (tenantId, entityName) und
14
- // entscheidet was zu tun ist (hardDelete / softDelete / anonymize /
15
- // blockDelete-mit-Frist-Check).
13
+ // The cleanup job resolves this for each (tenantId, entityName) and
14
+ // decides what to do (hardDelete / softDelete / anonymize /
15
+ // blockDelete with a grace-period check).
16
16
  //
17
- // Cross-Feature-API r.exposesApi("retention.policyFor") (S2.D3)
18
- // macht das aus user-data-rights heraus konsumierbar — Forget-Flow
19
- // fragt blockDelete-Felder ab + anonymisiert sie statt zu löschen.
17
+ // The cross-feature API r.exposesApi("retention.policyFor")
18
+ // makes this consumable by user-data-rights — the forget flow
19
+ // queries blockDelete fields and anonymizes them instead of deleting them.
20
20
 
21
21
  import type { EntityDefinition, RetentionDef } from "@cosmicdrift/kumiko-framework/engine";
22
22
  import { RETENTION_PRESETS, type RetentionPresetKey } from "./presets.ts";
23
23
 
24
24
  /**
25
- * Roh-Override aus der DB-Tabelle (config-Spalte als JSON-String).
26
- * Nicht das gleiche wie RetentionDef weil hier alles optional ist —
27
- * Override darf einzelne Properties überschreiben.
25
+ * Raw override from the database table (the config column as a JSON string).
26
+ * Unlike RetentionDef, every property is optional because an override may
27
+ * replace individual properties.
28
28
  */
29
29
  export interface RetentionOverride {
30
30
  readonly keepFor?: string;
@@ -33,13 +33,13 @@ export interface RetentionOverride {
33
33
  }
34
34
 
35
35
  /**
36
- * Effektive Policy nach Resolver-Lauf. Source dokumentiert WELCHE
37
- * Schicht den Wert geliefert hat — Audit-Trail für DPO.
36
+ * Effective policy after the resolver ran. `source` records WHICH layer
37
+ * supplied the value, as an audit trail for the DPO.
38
38
  *
39
- * `override-incomplete` bedeutet: Tenant-Override ist gesetzt, aber
40
- * füllt keepFor weder selbst noch via Preset-/Entity-Fallback.
41
- * Cleanup-Job logt eine Warning + skippt — anstatt mit Default-"0d"
42
- * sofort alles zu löschen.
39
+ * `override-incomplete` means a tenant override is set but fills keepFor
40
+ * neither itself nor through the preset/entity fallback. The cleanup job
41
+ * logs a warning and skips instead of deleting everything at once with a
42
+ * default "0d".
43
43
  */
44
44
  export interface EffectiveRetentionPolicy {
45
45
  readonly entityName: string;
@@ -55,15 +55,15 @@ export interface ResolveRetentionPolicyArgs {
55
55
  }
56
56
 
57
57
  /**
58
- * Auswertung der drei Schichten. Pure Function — kein DB-Access, alle
59
- * Inputs werden vom Caller besorgt (Cleanup-Job aggregiert pro Tenant
60
- * vorher).
58
+ * Resolves the three layers. This is a pure function with no database
59
+ * access; the caller supplies all inputs (the cleanup job aggregates them
60
+ * per tenant beforehand).
61
61
  */
62
62
  export function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): EffectiveRetentionPolicy {
63
63
  const { entityName, entityDef, tenantPreset, tenantOverride } = args;
64
64
 
65
- // Layer 3: Override wins, aber Override darf Felder weglassen — dann
66
- // fallen die einzelnen Properties auf Layer 2/1 zurück.
65
+ // Layer 3: Override wins, but it may omit fields, which then
66
+ // fall back to the individual properties from Layer 2 or 1.
67
67
  if (tenantOverride !== null) {
68
68
  const baseFromPreset =
69
69
  tenantPreset !== null ? (RETENTION_PRESETS[tenantPreset]?.[entityName] ?? null) : null;
@@ -73,10 +73,11 @@ export function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): Effect
73
73
  const keepFor = tenantOverride.keepFor ?? base?.keepFor;
74
74
  const strategy = tenantOverride.strategy ?? base?.strategy;
75
75
 
76
- // keepFor + strategy sind Pflicht für jede aktive Policy. Wenn
77
- // weder Override noch Base sie liefert, ist das Override semantisch
78
- // unvollständig — Cleanup-Job soll WARNEN statt mit Default-"0d"
79
- // sofort alles löschen. Source-Marker dokumentiert das.
76
+ // keepFor + strategy are required for every active policy. If
77
+ // neither the override nor the base supplies them, the override is
78
+ // semantically incomplete — the cleanup job must warn instead of
79
+ // immediately deleting everything with the default "0d". The source
80
+ // marker documents this.
80
81
  if (keepFor === undefined || strategy === undefined) {
81
82
  return { entityName, policy: null, source: "override-incomplete" };
82
83
  }
@@ -102,6 +103,6 @@ export function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): Effect
102
103
  return { entityName, policy: entityDef.retention, source: "entity-default" };
103
104
  }
104
105
 
105
- // Nichts da — Cleanup-Job überspringt diese Entity für diesen Tenant.
106
+ // No policy is defined, so the cleanup job skips this entity for this tenant.
106
107
  return { entityName, policy: null, source: "none" };
107
108
  }
@@ -1,42 +1,41 @@
1
- // Retention-Cleanup-Runner (S2.D2b) — pure Function, vom retention-cleanup-Cron
2
- // pro fan-out-Tenant aufgerufen.
1
+ // Retention-Cleanup-Runner — pure function, called by the retention-cleanup cron
2
+ // for each fan-out tenant.
3
3
  //
4
- // Iteriert alle implicit-Entity-Projektionen, loest pro Entity die effektive
5
- // Retention-Policy (3-Schicht-Resolver, siehe resolver.ts) und wendet die
6
- // Strategy auf Rows an deren reference-Timestamp aelter als der keepFor-Cutoff
7
- // ist:
4
+ // Iterates all implicit entity projections, resolves the effective retention
5
+ // policy per entity (3-layer resolver, see resolver.ts), and applies the
6
+ // strategy to rows whose reference timestamp is older than the keepFor cutoff:
8
7
  //
9
- // - hardDelete → executor.forget pro Row (Event → rebuild-safe hard purge).
8
+ // - hardDelete → executor.forget per row (event → rebuild-safe hard purge).
10
9
  // Entities with file/image/files/images fields: bytes first
11
10
  // (incl. derivatives), then the fileRef row(s), then the row;
12
11
  // shared fileRefs (other owner, other row of the same table)
13
12
  // stay untouched.
14
- // - softDelete → executor.delete pro Row (Event → rebuild-safe soft-delete),
15
- // nur auf noch-nicht-geloeschte (isDeleted:false-Filter)
16
- // - anonymize → executor.update pro Row mit den Werten der per-Feld
17
- // anonymize-Funktionen; Row bleibt (fields.ts-Contract)
18
- // - blockDelete → waehrend keepFor unangetastet (Aufbewahrungs-Pflicht;
19
- // user-forget loest anonymize aus). NACH Ablauf laufen die
20
- // anonymize-Funktionen — Geschaeftsdaten bleiben, Personen-
21
- // Bezug raus (fields.ts:44-47).
13
+ // - softDelete → executor.delete per row (event → rebuild-safe soft-delete),
14
+ // only on not-yet-deleted rows (isDeleted:false filter)
15
+ // - anonymize → executor.update per row with the values of the per-field
16
+ // anonymize functions; row remains (fields.ts contract)
17
+ // - blockDelete → untouched during keepFor (retention requirement;
18
+ // user-forget triggers anonymize). AFTER expiration, the
19
+ // anonymize functions run — business data remains, the person-
20
+ // identifying link is removed (fields.ts:44-47).
22
21
  //
23
- // Über den Executor statt Batch-deleteMany/updateMany: ein eventloser Batch-Write
24
- // auf die (erased) Projektions-Tabelle wird beim Projection-Rebuild
25
- // gewischt/resurrektiert — das #648-Loch. Kosten: N Events pro Cleanup statt ein
26
- // Batch-Statement (per-Row-Ceiling, Batch-Event-Variante als Follow-up).
22
+ // Via the executor instead of batch-deleteMany/updateMany: an eventless batch write
23
+ // to the (erased) projection table is wiped/resurrected during a projection rebuild.
24
+ // Cost: N events per cleanup instead of one batch statement (per-row ceiling, with
25
+ // a batch-event variant later).
27
26
  //
28
- // **Schaerfer als soft-delete-cleanup:** dieser Cron hardDeleted LIVE Rows
29
- // (keyed auf reference, Default createdAt), nicht bereits-soft-geloeschte.
30
- // Darum die Spalten-Existenz-Pruefung vor jedem WHERE — eine fehlende/vertippte
31
- // reference-Spalte wuerde sonst ein malformed/all-matching WHERE ergeben und
32
- // pauschal loeschen.
27
+ // **Stricter than soft-delete-cleanup:** this cron hard-deletes LIVE rows
28
+ // (keyed on reference, default createdAt), not already-soft-deleted ones.
29
+ // Therefore the column-existence check comes before every WHERE — a missing/mistyped
30
+ // reference column would otherwise produce a malformed/all-matching WHERE and
31
+ // delete everything.
33
32
  //
34
- // **anonymize-Idempotenz ohne Marker-Spalte:** die anonymize-Funktionen sind
35
- // row-unabhaengig (`() => unknown`) — einmal pro Entity ausgewertet ergeben
36
- // sie die Ziel-Werte. Eine Row die sie schon traegt wird uebersprungen, der
37
- // taegliche Re-Lauf appended also null Events. Der WHERE matcht anonymisierte
38
- // Rows weiter (reference bleibt alt), deshalb id-Cursor-Paging statt einer
39
- // einzelnen Page — sonst verstopfen erledigte Rows das batchLimit-Fenster.
33
+ // **anonymize idempotence without a marker column:** the anonymize functions are
34
+ // row-independent (`() => unknown`) — evaluated once per entity, they produce
35
+ // the target values. A row that already carries them is skipped, so the
36
+ // daily rerun appends zero events. The WHERE continues to match anonymized
37
+ // rows (reference remains old), hence id-cursor paging instead of a single
38
+ // page — otherwise completed rows clog the batchLimit window.
40
39
 
41
40
  import {
42
41
  collectPiiSubjectFields,
@@ -68,6 +67,7 @@ import {
68
67
  type SessionUser,
69
68
  type TenantId,
70
69
  } from "@cosmicdrift/kumiko-framework/engine";
70
+ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
71
71
  import {
72
72
  type FileContext,
73
73
  fileRefEntity,
@@ -89,13 +89,13 @@ import { tenantRetentionOverrideTable } from "./schema/tenant-retention-override
89
89
  const DEFAULT_BATCH_LIMIT = 1000;
90
90
  const DEFAULT_REFERENCE_FIELD = "createdAt";
91
91
 
92
- // Der Boot-Validator (boot-validator/pii-retention.ts FRAMEWORK_TIMESTAMP_FIELDS)
93
- // erlaubt diese Aliase als retention.reference, auch wenn sie nicht in
94
- // entity.fields deklariert sind — die physischen Spalten heissen aber anders
95
- // (table-builder.ts: inserted_at/modified_at). Hier zur Cleanup-Zeit auf das
96
- // echte Entity-Feld mappen, sonst trifft die Spalten-Existenz-Pruefung unten
97
- // und der Cron wuerde lautlos nichts tun. deletedAt/lastSeenAt sind echte
98
- // Felder (softDelete bzw. session) und brauchen keine Uebersetzung.
92
+ // The boot validator (boot-validator/pii-retention.ts FRAMEWORK_TIMESTAMP_FIELDS)
93
+ // permits these aliases as retention.reference even when they are not declared in
94
+ // entity.fields, but the physical columns use different names
95
+ // (table-builder.ts: inserted_at/modified_at). Map them to the actual entity field
96
+ // during cleanup; otherwise the column existence check below would cause the cron
97
+ // to do nothing silently. deletedAt/lastSeenAt are real fields (softDelete and
98
+ // session respectively) and need no translation.
99
99
  const FRAMEWORK_REFERENCE_ALIAS: Readonly<Record<string, string>> = {
100
100
  createdAt: "insertedAt",
101
101
  updatedAt: "modifiedAt",
@@ -108,14 +108,16 @@ export interface RunRetentionCleanupArgs {
108
108
  /** Layer-2 preset from resolveTenantRetentionPreset, resolved once per tenant
109
109
  * by the caller rather than per entity here. null = layers 1 and 3 only. */
110
110
  readonly preloadedTenantPreset: RetentionPresetKey | null;
111
- /** Now-Injection — Tests pinnen den Wert ohne Date-Mock (Pattern keep-for.ts). */
111
+ /**
112
+ * Injects the current time so tests can pin it without mocking the date (pattern keep-for.ts).
113
+ */
112
114
  readonly now: Instant;
113
115
  readonly batchLimit?: number;
114
116
  /** Storage access for hardDelete entities with file/image/files/images fields.
115
117
  * Missing while fileRefs are due → the row stays (skipped "missing_file_storage"),
116
118
  * fail-closed instead of orphaning bytes. */
117
119
  readonly files?: FileContext;
118
- /** KMS for erasing a hardDeleted row's own subject key (Refs #2057). Defaults
120
+ /** KMS for erasing a hardDeleted row's own subject key. Defaults
119
121
  * to configuredPiiSubjectKms() (same fallback as run-forget-cleanup.ts);
120
122
  * undefined/no-KMS-configured skips erasure entirely — the row is still
121
123
  * hard-deleted, only crypto-shredding of its DEK is not attempted. */
@@ -137,7 +139,10 @@ export interface RunRetentionCleanupResult {
137
139
  readonly softDeleted: number;
138
140
  /** Rows deren anonymize-Feld-Funktionen angewendet wurden (anonymize + abgelaufene blockDelete). */
139
141
  readonly anonymized: number;
140
- /** Anomalien: Policy referenziert eine Spalte die die Tabelle nicht hat, oder anonymize ohne anonymize-Felder. */
142
+ /**
143
+ * Anomalies where the policy references a missing table column or anonymize has no anonymize
144
+ * fields.
145
+ */
141
146
  readonly skipped: readonly RetentionCleanupSkip[];
142
147
  }
143
148
 
@@ -159,7 +164,7 @@ async function purgeMatchingRows(
159
164
  return count;
160
165
  }
161
166
 
162
- // --- hardDelete crypto-shredding (Refs #2057) ---
167
+ // --- hardDelete crypto-shredding ---
163
168
  //
164
169
  // recordOwned/self-pii resolution (subject-resolver.ts) only ever reads
165
170
  // `row.id` for these two kinds — resolveRecordSubject uses opts.entityName +
@@ -243,17 +248,14 @@ async function forgetRowAndEraseKeys(args: {
243
248
  readonly entityName: string;
244
249
  readonly tenantId: TenantId;
245
250
  readonly systemUser: SessionUser;
251
+ readonly rowId: EntityId;
246
252
  readonly row: Record<string, unknown>;
247
253
  readonly kms: KmsAdapter | undefined;
248
254
  readonly registry: Registry;
249
255
  }): Promise<{ readonly isSuccess: boolean }> {
250
256
  return runInSubTransaction(args.db, async (sp) => {
251
257
  const tdb = createTenantDb({ db: sp, tenantId: args.tenantId, mode: "system" });
252
- const result = await args.executor.forget(
253
- { id: args.row["id"] as EntityId },
254
- args.systemUser,
255
- tdb,
256
- );
258
+ const result = await args.executor.forget({ id: args.rowId }, args.systemUser, tdb);
257
259
  if (!result.isSuccess) return { isSuccess: false };
258
260
  await eraseHardDeletedRowKeys({
259
261
  kms: args.kms,
@@ -268,7 +270,7 @@ async function forgetRowAndEraseKeys(args: {
268
270
  });
269
271
  }
270
272
 
271
- // --- hardDelete + file-fields (kumiko-framework#3089) ---
273
+ // --- hardDelete + file-fields ---
272
274
 
273
275
  // Throws (unsafe key, missing list permission) must fail this row closed,
274
276
  // not abort the whole tenant run.
@@ -298,6 +300,7 @@ async function purgeHardDeleteRowWithFiles(args: {
298
300
  readonly tenantId: TenantId;
299
301
  readonly entityName: string;
300
302
  readonly entity: EntityDefinition;
303
+ readonly rowId: EntityId;
301
304
  readonly row: Record<string, unknown>;
302
305
  readonly files: FileContext | undefined;
303
306
  readonly entityExecutor: ReturnType<typeof createEventStoreExecutor>;
@@ -308,8 +311,7 @@ async function purgeHardDeleteRowWithFiles(args: {
308
311
  readonly registry: Registry;
309
312
  readonly onSkip: (reason: "missing_file_storage" | "file_delete_failed") => void;
310
313
  }): Promise<boolean> {
311
- const rowId = String(args.row["id"]);
312
- const logContext = `tenant=${args.tenantId} entity=${args.entityName} row=${rowId}`;
314
+ const logContext = `tenant=${args.tenantId} entity=${args.entityName} row=${String(args.rowId)}`;
313
315
  const files = args.files;
314
316
  const outcome = await purgeRowFileRefs({
315
317
  db: args.db,
@@ -341,6 +343,7 @@ async function purgeHardDeleteRowWithFiles(args: {
341
343
  entityName: args.entityName,
342
344
  tenantId: args.tenantId,
343
345
  systemUser: args.systemUser,
346
+ rowId: args.rowId,
344
347
  row: args.row,
345
348
  kms: args.kms,
346
349
  registry: args.registry,
@@ -392,7 +395,14 @@ async function purgeHardDeleteRowsWithFiles(args: {
392
395
  });
393
396
  if (rows.length === 0) break;
394
397
  for (const row of rows) {
395
- cursor = row["id"] as EntityId; // @cast-boundary db-row
398
+ const rawId = row["id"];
399
+ if (typeof rawId !== "string" && typeof rawId !== "number") {
400
+ throw new InternalError({
401
+ message: `data-retention: ${args.entityName} row has no usable id (got ${typeof rawId})`,
402
+ });
403
+ }
404
+ const rowId = rawId;
405
+ cursor = rowId;
396
406
  const ok = await purgeHardDeleteRowWithFiles({
397
407
  db: args.db,
398
408
  table: args.table,
@@ -400,6 +410,7 @@ async function purgeHardDeleteRowsWithFiles(args: {
400
410
  tenantId: args.tenantId,
401
411
  entityName: args.entityName,
402
412
  entity: args.entity,
413
+ rowId,
403
414
  row,
404
415
  files: args.files,
405
416
  entityExecutor: args.entityExecutor,
@@ -490,9 +501,9 @@ export async function runRetentionCleanup(
490
501
 
491
502
  // Retention writes go through the executor (events) so a projection rebuild
492
503
  // replays the cleanup — a batch deleteMany/updateMany on the (erased)
493
- // projection table is eventless and gets wiped/resurrected on rebuild (the
494
- // #648 hole this closes). The cron acts as the system actor; the WHERE already
495
- // tenant-scopes, so a system-mode TenantDb (no extra filter) is correct.
504
+ // projection table is eventless and gets wiped/resurrected on rebuild.
505
+ // The cron acts as the system actor; the WHERE already tenant-scopes, so a
506
+ // system-mode TenantDb (no extra filter) is correct.
496
507
  // ponytail: per-row events, one batchLimit-sized page per entity per run —
497
508
  // the daily cron converges (hardDelete removes rows, softDelete's isDeleted
498
509
  // filter shrinks the set). A single batched forget/delete event is a follow-up.
@@ -511,8 +522,9 @@ export async function runRetentionCleanup(
511
522
  const overrideByEntity = new Map(overrideRows.map((r) => [r.entityName, { config: r.config }]));
512
523
 
513
524
  for (const proj of registry.getAllProjections().values()) {
514
- // Nur implicit-Entity-Projektionen mit Tabelle — wie soft-delete-cleanup.
515
- // Custom-Projektionen + unmanaged-Tables (z.B. sessions) sind kein Target.
525
+ // Only implicit entity projections with a table are targets, as in
526
+ // soft-delete-cleanup. Custom projections and unmanaged tables (such as
527
+ // sessions) are excluded.
516
528
  if (proj.isImplicit !== true || typeof proj.source !== "string" || !proj.table) continue;
517
529
  const entityName = proj.source;
518
530
 
@@ -539,9 +551,9 @@ export async function runRetentionCleanup(
539
551
 
540
552
  const cutoff = computeCutoff(policy.keepFor, now);
541
553
  const where: WhereObject = { [referenceField]: { lt: cutoff } };
542
- // Tenant-Scope nur wenn die Tabelle eine tenantId-Spalte hat — identisch zu
543
- // soft-delete-cleanup. Ohne diesen Filter wuerde ein Tenant die Rows eines
544
- // anderen treffen.
554
+ // Apply tenant scoping only when the table has a tenantId column, as in
555
+ // soft-delete-cleanup. Without this filter, one tenant could affect another
556
+ // tenant's rows.
545
557
  if (propertyOf(proj.table, "tenantId") !== undefined) {
546
558
  where["tenantId"] = tenantId;
547
559
  }
@@ -559,6 +571,7 @@ export async function runRetentionCleanup(
559
571
  entityName,
560
572
  tenantId,
561
573
  systemUser,
574
+ rowId: id,
562
575
  row: { id },
563
576
  kms,
564
577
  registry,
@@ -5,21 +5,23 @@ import {
5
5
  createTextField,
6
6
  } from "@cosmicdrift/kumiko-framework/engine";
7
7
 
8
- // tenantRetentionOverride — Layer 3 im 3-Schicht-Resolver.
8
+ // tenantRetentionOverride — Layer 3 in the three-layer resolver.
9
9
  //
10
- // Per (tenantId, entityName) eine optionale Override-Config die das
11
- // Preset (Layer 2) für genau diese eine Entity überschreibt. Use-Cases:
12
- // - Anwaltskanzlei in DE: caseFile 6y blockDelete (nicht im Preset)
13
- // - Pilot-Tenant der länger speichert für Test
14
- // - Branchenspezifische verkürzte Fristen
10
+ // One optional override config per (tenantId, entityName) replaces the
11
+ // preset (Layer 2) for that entity only. Use cases:
12
+ // - Law firm in DE: caseFile 6y blockDelete (not in the preset)
13
+ // - Pilot tenant retaining data longer for testing
14
+ // - Industry-specific shorter retention periods
15
15
  //
16
- // reason ist Pflicht — Audit für DPO + Aufsichtsbehörde nachvollziehbar.
16
+ // reason is required so the decision is auditable by the DPO and supervisory
17
+ // authorities.
17
18
  //
18
- // config ist JSON-String mit `{ keepFor, strategy, reference? }`. Zod-
19
- // Schema validiert beim set-override-Call (S2.D2 ggf. erweitert).
19
+ // config is a JSON string with `{ keepFor, strategy, reference? }`. The Zod
20
+ // schema validates it when set-override is called.
20
21
  //
21
- // Tenant-1-zu-N: pro Tenant beliebig viele Entity-Overrides. UNIQUE-
22
- // Index auf (tenantId, entityName) damit pro Entity max ein Override.
22
+ // Tenant one-to-many: each tenant can have any number of entity overrides.
23
+ // A UNIQUE index on (tenantId, entityName) allows at most one override per
24
+ // entity.
23
25
  export const tenantRetentionOverrideEntity = createEntity({
24
26
  table: "read_tenant_retention_overrides",
25
27
  fields: {