@cosmicdrift/kumiko-bundled-features 1.0.0 → 2.0.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 (669) hide show
  1. package/package.json +41 -7
  2. package/src/__tests__/access-invalidation-consumer.integration.test.ts +279 -0
  3. package/src/__tests__/access-invalidation-stream.integration.test.ts +217 -0
  4. package/src/admin-shell/__tests__/admin-shell-security.integration.test.ts +200 -0
  5. package/src/admin-shell/__tests__/admin-shell.boot.test.ts +147 -0
  6. package/src/admin-shell/__tests__/overview-allowlist.test.ts +43 -0
  7. package/src/admin-shell/__tests__/overview-screens.boot.test.ts +35 -0
  8. package/src/admin-shell/constants.ts +11 -0
  9. package/src/admin-shell/feature.ts +135 -0
  10. package/src/admin-shell/i18n.ts +36 -0
  11. package/src/admin-shell/index.ts +17 -0
  12. package/src/admin-shell/overview-allowlist.ts +37 -0
  13. package/src/admin-shell/web/client-plugin.tsx +26 -0
  14. package/src/admin-shell/web/i18n.ts +16 -0
  15. package/src/admin-shell/web/index.ts +2 -0
  16. package/src/admin-shell/web/overview-layout.tsx +69 -0
  17. package/src/admin-shell/web/overview-query.ts +15 -0
  18. package/src/admin-shell/web/platform-overview-screen.tsx +90 -0
  19. package/src/admin-shell/web/tenant-overview-screen.tsx +90 -0
  20. package/src/agent-tools/__tests__/tool-catalog.test.ts +179 -0
  21. package/src/agent-tools/__tests__/tool-dispatch.integration.test.ts +100 -0
  22. package/src/agent-tools/__tests__/tool-dispatch.test.ts +126 -0
  23. package/src/agent-tools/index.ts +9 -0
  24. package/src/agent-tools/tool-catalog.ts +130 -0
  25. package/src/agent-tools/tool-dispatch.ts +82 -0
  26. package/src/agent-tools/types.ts +38 -0
  27. package/src/audit/__tests__/audit-screens.boot.test.ts +51 -0
  28. package/src/audit/__tests__/audit-security.integration.test.ts +201 -0
  29. package/src/audit/__tests__/audit.integration.test.ts +50 -25
  30. package/src/audit/constants.ts +9 -2
  31. package/src/audit/feature.ts +33 -1
  32. package/src/audit/handlers/details.query.ts +47 -0
  33. package/src/audit/handlers/list.query.ts +3 -2
  34. package/src/audit/i18n.ts +31 -0
  35. package/src/audit/web/audit-log-detail-screen.tsx +131 -0
  36. package/src/audit/web/audit-log-screen.tsx +250 -0
  37. package/src/audit/web/client-plugin.tsx +22 -0
  38. package/src/audit/web/i18n.ts +12 -0
  39. package/src/audit/web/index.ts +5 -0
  40. package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +4 -4
  41. package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +4 -4
  42. package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
  43. package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +10 -11
  44. package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
  45. package/src/auth-email-password/__tests__/auth.integration.test.ts +33 -6
  46. package/src/auth-email-password/__tests__/email-verification.integration.test.ts +9 -6
  47. package/src/auth-email-password/__tests__/feature-options.test.ts +33 -0
  48. package/src/auth-email-password/__tests__/identity-v3-login.integration.test.ts +3 -3
  49. package/src/auth-email-password/__tests__/invite-flow-kms.integration.test.ts +283 -0
  50. package/src/auth-email-password/__tests__/invite-flow.integration.test.ts +35 -1
  51. package/src/auth-email-password/__tests__/login-mfa-setup-required.integration.test.ts +129 -0
  52. package/src/auth-email-password/__tests__/multi-roles.integration.test.ts +1 -1
  53. package/src/auth-email-password/__tests__/password-policy.test.ts +15 -0
  54. package/src/auth-email-password/__tests__/password-reset.integration.test.ts +19 -6
  55. package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.test.ts +3 -3
  56. package/src/auth-email-password/__tests__/seed-admin.integration.test.ts +1 -1
  57. package/src/auth-email-password/__tests__/self-registration-toggle.integration.test.ts +264 -0
  58. package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +20 -4
  59. package/src/auth-email-password/__tests__/session-strict-mode.integration.test.ts +7 -14
  60. package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +91 -0
  61. package/src/auth-email-password/constants.ts +25 -0
  62. package/src/auth-email-password/email-templates.ts +32 -0
  63. package/src/auth-email-password/errors.ts +9 -0
  64. package/src/auth-email-password/feature.ts +59 -11
  65. package/src/auth-email-password/handlers/change-password.write.ts +14 -13
  66. package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
  67. package/src/auth-email-password/handlers/confirm-token-flow.ts +34 -105
  68. package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +12 -5
  69. package/src/auth-email-password/handlers/invite-accept.write.ts +9 -2
  70. package/src/auth-email-password/handlers/invite-create.write.ts +2 -2
  71. package/src/auth-email-password/handlers/invite-signup-complete.write.ts +12 -4
  72. package/src/auth-email-password/handlers/login.write.ts +86 -16
  73. package/src/auth-email-password/handlers/logout.write.ts +4 -2
  74. package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
  75. package/src/auth-email-password/handlers/reset-password.write.ts +3 -2
  76. package/src/auth-email-password/handlers/self-registration-status.query.ts +23 -0
  77. package/src/auth-email-password/handlers/signup-confirm.write.ts +19 -11
  78. package/src/auth-email-password/handlers/signup-request.write.ts +22 -0
  79. package/src/auth-email-password/handlers/token-request-handler.ts +4 -2
  80. package/src/auth-email-password/i18n.ts +52 -0
  81. package/src/auth-email-password/index.ts +13 -2
  82. package/src/auth-email-password/lockout-store.ts +10 -5
  83. package/src/auth-email-password/password-policy.ts +2108 -0
  84. package/src/auth-email-password/seeding.ts +55 -27
  85. package/src/auth-email-password/self-registration-toggle.ts +25 -0
  86. package/src/auth-email-password/signed-token.ts +1 -0
  87. package/src/auth-email-password/unlock-token.ts +28 -0
  88. package/src/auth-email-password/web/__tests__/auth-client.test.ts +542 -0
  89. package/src/auth-email-password/web/__tests__/auth-form-logic.test.ts +44 -0
  90. package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +194 -6
  91. package/src/auth-email-password/web/__tests__/client-plugin.test.tsx +34 -0
  92. package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +64 -0
  93. package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +5 -1
  94. package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +129 -2
  95. package/src/auth-email-password/web/__tests__/login-screen.test.tsx +149 -5
  96. package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +137 -0
  97. package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +5 -1
  98. package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +134 -0
  99. package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +93 -0
  100. package/src/auth-email-password/web/__tests__/test-utils.tsx +12 -2
  101. package/src/auth-email-password/web/__tests__/use-url-token.test.tsx +29 -0
  102. package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +5 -1
  103. package/src/auth-email-password/web/auth-client.ts +96 -18
  104. package/src/auth-email-password/web/auth-form-logic.ts +30 -0
  105. package/src/auth-email-password/web/auth-form-primitives.tsx +22 -20
  106. package/src/auth-email-password/web/auth-gate.tsx +140 -19
  107. package/src/auth-email-password/web/client-plugin.ts +24 -2
  108. package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
  109. package/src/auth-email-password/web/forgot-password-screen.tsx +6 -6
  110. package/src/auth-email-password/web/index.ts +16 -4
  111. package/src/auth-email-password/web/invite-accept-screen.tsx +5 -5
  112. package/src/auth-email-password/web/login-screen.tsx +78 -22
  113. package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
  114. package/src/auth-email-password/web/reset-password-screen.tsx +15 -18
  115. package/src/auth-email-password/web/session.tsx +11 -6
  116. package/src/auth-email-password/web/signup-complete-screen.tsx +13 -15
  117. package/src/auth-email-password/web/signup-screen.tsx +8 -10
  118. package/src/auth-email-password/web/use-shell-user.ts +5 -2
  119. package/src/auth-email-password/web/user-menu.tsx +3 -1
  120. package/src/auth-email-password/web/verify-email-screen.tsx +10 -14
  121. package/src/auth-foundation/__tests__/feature.test.ts +46 -0
  122. package/src/auth-foundation/__tests__/session-store.test.ts +77 -0
  123. package/src/auth-foundation/__tests__/tenant-providers.test.ts +160 -0
  124. package/src/auth-foundation/__tests__/token-verifier.test.ts +150 -0
  125. package/src/auth-foundation/boot-checks.ts +150 -0
  126. package/src/auth-foundation/feature.ts +157 -0
  127. package/src/auth-foundation/index.ts +37 -0
  128. package/src/auth-foundation/resolve-anonymous-access.ts +62 -0
  129. package/src/auth-foundation/types.ts +170 -0
  130. package/src/auth-mfa/__tests__/base32.test.ts +28 -0
  131. package/src/auth-mfa/__tests__/disable-and-regenerate.integration.test.ts +265 -0
  132. package/src/auth-mfa/__tests__/enable-confirm-preauth.integration.test.ts +321 -0
  133. package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +322 -0
  134. package/src/auth-mfa/__tests__/enable-start-preauth.integration.test.ts +237 -0
  135. package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +39 -0
  136. package/src/auth-mfa/__tests__/mfa-preauth-setup-token.test.ts +113 -0
  137. package/src/auth-mfa/__tests__/no-redis.integration.test.ts +209 -0
  138. package/src/auth-mfa/__tests__/pii-subject-encryption.integration.test.ts +139 -0
  139. package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +247 -0
  140. package/src/auth-mfa/__tests__/session-auto-revoke.integration.test.ts +169 -0
  141. package/src/auth-mfa/__tests__/totp.test.ts +131 -0
  142. package/src/auth-mfa/__tests__/verify.integration.test.ts +319 -0
  143. package/src/auth-mfa/base32.ts +51 -0
  144. package/src/auth-mfa/config.ts +26 -0
  145. package/src/auth-mfa/constants.ts +45 -0
  146. package/src/auth-mfa/db/queries.ts +45 -0
  147. package/src/auth-mfa/errors.ts +66 -0
  148. package/src/auth-mfa/feature.ts +170 -0
  149. package/src/auth-mfa/handlers/disable.write.ts +67 -0
  150. package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +200 -0
  151. package/src/auth-mfa/handlers/enable-confirm.write.ts +106 -0
  152. package/src/auth-mfa/handlers/enable-start-preauth.write.ts +87 -0
  153. package/src/auth-mfa/handlers/enable-start.write.ts +66 -0
  154. package/src/auth-mfa/handlers/reencrypt.job.ts +234 -0
  155. package/src/auth-mfa/handlers/regenerate-recovery.write.ts +77 -0
  156. package/src/auth-mfa/handlers/status.query.ts +23 -0
  157. package/src/auth-mfa/handlers/verify.write.ts +179 -0
  158. package/src/auth-mfa/i18n.ts +12 -0
  159. package/src/auth-mfa/index.ts +19 -0
  160. package/src/auth-mfa/mfa-challenge-token.ts +90 -0
  161. package/src/auth-mfa/mfa-preauth-setup-token.ts +86 -0
  162. package/src/auth-mfa/mfa-setup-token.ts +96 -0
  163. package/src/auth-mfa/mfa-status-checker.ts +84 -0
  164. package/src/auth-mfa/mfa-verify-attempts.ts +90 -0
  165. package/src/auth-mfa/otpauth-uri.ts +21 -0
  166. package/src/auth-mfa/recovery-codes.ts +54 -0
  167. package/src/auth-mfa/schema/user-mfa.ts +60 -0
  168. package/src/auth-mfa/testing.ts +5 -0
  169. package/src/auth-mfa/totp.ts +67 -0
  170. package/src/auth-mfa/verify-factor.ts +58 -0
  171. package/src/auth-mfa/web/__tests__/mfa-client.test.ts +265 -0
  172. package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +182 -0
  173. package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +379 -0
  174. package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +104 -0
  175. package/src/auth-mfa/web/client-plugin.ts +37 -0
  176. package/src/auth-mfa/web/i18n.ts +136 -0
  177. package/src/auth-mfa/web/index.ts +27 -0
  178. package/src/auth-mfa/web/mfa-client.ts +184 -0
  179. package/src/auth-mfa/web/mfa-disable-dialog.tsx +69 -0
  180. package/src/auth-mfa/web/mfa-enable-screen.tsx +228 -0
  181. package/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +53 -0
  182. package/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +68 -0
  183. package/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +267 -0
  184. package/src/auth-mfa/web/mfa-verify-screen.tsx +117 -0
  185. package/src/auth-mfa/web/qrcode-browser.d.ts +12 -0
  186. package/src/auth-mfa-user-data/__tests__/hooks.integration.test.ts +134 -0
  187. package/src/auth-mfa-user-data/hooks.ts +53 -0
  188. package/src/auth-mfa-user-data/index.ts +21 -0
  189. package/src/billing-foundation/__tests__/billing-foundation.integration.test.ts +206 -2
  190. package/src/billing-foundation/__tests__/subscription-provider-contract.ts +93 -0
  191. package/src/billing-foundation/entities.ts +28 -4
  192. package/src/billing-foundation/events.ts +6 -2
  193. package/src/billing-foundation/feature.ts +9 -1
  194. package/src/billing-foundation/get-subscription-for-tenant.ts +16 -5
  195. package/src/billing-foundation/handlers/create-portal-session.write.ts +17 -1
  196. package/src/billing-foundation/handlers/list-subscriptions.query.ts +15 -1
  197. package/src/billing-foundation/handlers/process-event.write.ts +39 -3
  198. package/src/billing-foundation/tenant-destroy-hook.ts +46 -0
  199. package/src/cap-counter/__tests__/cap-list.boot.test.ts +27 -0
  200. package/src/cap-counter/__tests__/cap-list.security.integration.test.ts +53 -0
  201. package/src/cap-counter/__tests__/entity-ddl.test.ts +15 -0
  202. package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +8 -5
  203. package/src/cap-counter/constants.ts +2 -0
  204. package/src/cap-counter/enforce-cap.ts +15 -18
  205. package/src/cap-counter/entity.ts +3 -3
  206. package/src/cap-counter/feature.ts +28 -1
  207. package/src/cap-counter/i18n.ts +12 -0
  208. package/src/cap-counter/with-cap-enforcement.ts +2 -2
  209. package/src/channel-email/__tests__/pii-guard.test.ts +56 -0
  210. package/src/channel-email/email-channel.ts +2 -1
  211. package/src/channel-email/index.ts +1 -0
  212. package/src/channel-email/pii-guard.ts +39 -0
  213. package/src/compliance-profiles/__tests__/profile-picker.boot.test.ts +28 -0
  214. package/src/compliance-profiles/__tests__/profile-picker.security.integration.test.ts +91 -0
  215. package/src/compliance-profiles/constants.ts +15 -0
  216. package/src/compliance-profiles/feature.ts +22 -1
  217. package/src/compliance-profiles/handlers/set-profile.write.ts +2 -2
  218. package/src/compliance-profiles/i18n.ts +6 -0
  219. package/src/compliance-profiles/index.ts +6 -0
  220. package/src/compliance-profiles/web/client-plugin.tsx +22 -0
  221. package/src/compliance-profiles/web/compliance-profile-screen.tsx +152 -0
  222. package/src/compliance-profiles/web/i18n.ts +25 -0
  223. package/src/compliance-profiles/web/index.ts +6 -0
  224. package/src/config/__tests__/app-overrides.test.ts +11 -0
  225. package/src/config/__tests__/backing-secrets.integration.test.ts +22 -0
  226. package/src/config/__tests__/cascade.integration.test.ts +35 -0
  227. package/src/config/__tests__/config.integration.test.ts +71 -10
  228. package/src/config/__tests__/deserialize-value.test.ts +5 -8
  229. package/src/config/__tests__/inherited-redaction.integration.test.ts +4 -3
  230. package/src/config/__tests__/pii-encrypted.integration.test.ts +179 -0
  231. package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +234 -0
  232. package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +243 -0
  233. package/src/config/__tests__/write-helpers.test.ts +23 -0
  234. package/src/config/db/queries/resolver.ts +3 -3
  235. package/src/config/feature.ts +15 -8
  236. package/src/config/handlers/cascade.query.ts +2 -1
  237. package/src/config/handlers/reencrypt.job.ts +209 -0
  238. package/src/config/handlers/set.write.ts +55 -3
  239. package/src/config/handlers/values.query.ts +2 -1
  240. package/src/config/i18n.ts +9 -0
  241. package/src/config/index.ts +5 -4
  242. package/src/config/resolver.ts +75 -61
  243. package/src/config/table.ts +1 -1
  244. package/src/config/write-helpers.ts +22 -0
  245. package/src/crypto-shredding/__tests__/forget-subject.integration.test.ts +148 -0
  246. package/src/crypto-shredding/constants.ts +5 -0
  247. package/src/crypto-shredding/feature.ts +18 -0
  248. package/src/crypto-shredding/handlers/forget-subject.write.ts +91 -0
  249. package/src/crypto-shredding/index.ts +11 -0
  250. package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
  251. package/src/custom-fields/__tests__/custom-fields.integration.test.ts +92 -53
  252. package/src/custom-fields/__tests__/field-access.integration.test.ts +25 -0
  253. package/src/custom-fields/__tests__/parse-serialized-field.test.ts +11 -3
  254. package/src/custom-fields/__tests__/retention.integration.test.ts +40 -0
  255. package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +26 -79
  256. package/src/custom-fields/constants.ts +1 -0
  257. package/src/custom-fields/db/queries/user-data-rights.ts +0 -20
  258. package/src/custom-fields/entity.ts +1 -1
  259. package/src/custom-fields/events.ts +4 -3
  260. package/src/custom-fields/feature.ts +8 -0
  261. package/src/custom-fields/handlers/delete-system-field.write.ts +2 -1
  262. package/src/custom-fields/handlers/delete-tenant-field.write.ts +2 -1
  263. package/src/custom-fields/handlers/set-custom-field.write.ts +3 -44
  264. package/src/custom-fields/handlers/update-tenant-field.write.ts +0 -17
  265. package/src/custom-fields/lib/field-access.ts +8 -1
  266. package/src/custom-fields/lib/parse-serialized-field.ts +12 -4
  267. package/src/custom-fields/lib/value-schema.ts +1 -1
  268. package/src/custom-fields/run-retention.ts +9 -1
  269. package/src/custom-fields/schemas.ts +9 -9
  270. package/src/custom-fields/wire-for-entity.ts +110 -78
  271. package/src/custom-fields/wire-user-data-rights.ts +5 -54
  272. package/src/data-retention/__tests__/parse-override.test.ts +14 -13
  273. package/src/data-retention/__tests__/retention-cleanup.integration.test.ts +145 -8
  274. package/src/data-retention/feature.ts +3 -3
  275. package/src/data-retention/run-retention-cleanup.ts +102 -19
  276. package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +171 -0
  277. package/src/delivery/__tests__/delivery-screens.boot.test.ts +27 -0
  278. package/src/delivery/__tests__/delivery-security.integration.test.ts +106 -0
  279. package/src/delivery/constants.ts +13 -51
  280. package/src/delivery/feature.ts +29 -5
  281. package/src/delivery/handlers/log.query.ts +28 -7
  282. package/src/delivery/i18n.ts +6 -0
  283. package/src/delivery/index.ts +6 -1
  284. package/src/delivery/public-names.ts +36 -0
  285. package/src/delivery/tables.ts +3 -3
  286. package/src/delivery/web/client-plugin.tsx +20 -0
  287. package/src/delivery/web/delivery-log-screen.tsx +103 -0
  288. package/src/delivery/web/i18n.ts +25 -0
  289. package/src/delivery/web/index.ts +3 -0
  290. package/src/document-ingest-foundation/__tests__/feature.integration.test.ts +147 -0
  291. package/src/document-ingest-foundation/__tests__/feature.test.ts +105 -0
  292. package/src/document-ingest-foundation/__tests__/pages.test.ts +37 -0
  293. package/src/document-ingest-foundation/entity.ts +43 -0
  294. package/src/document-ingest-foundation/events.ts +25 -0
  295. package/src/document-ingest-foundation/feature.ts +126 -0
  296. package/src/document-ingest-foundation/index.ts +12 -0
  297. package/src/document-ingest-foundation/pages.ts +34 -0
  298. package/src/feature-toggles/__tests__/compose-tier-resolver.test.ts +304 -0
  299. package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +7 -6
  300. package/src/feature-toggles/__tests__/toggle-admin.boot.test.ts +31 -0
  301. package/src/feature-toggles/__tests__/toggle-admin.security.integration.test.ts +69 -0
  302. package/src/feature-toggles/compose-tier-resolver.ts +134 -0
  303. package/src/feature-toggles/constants.ts +14 -0
  304. package/src/feature-toggles/feature.ts +23 -3
  305. package/src/feature-toggles/global-feature-state-table.ts +20 -1
  306. package/src/feature-toggles/handlers/list.query.ts +1 -1
  307. package/src/feature-toggles/handlers/registered.query.ts +1 -1
  308. package/src/feature-toggles/i18n.ts +6 -0
  309. package/src/feature-toggles/index.ts +8 -0
  310. package/src/feature-toggles/toggle-runtime.ts +14 -0
  311. package/src/feature-toggles/web/client-plugin.tsx +22 -0
  312. package/src/feature-toggles/web/i18n.ts +29 -0
  313. package/src/feature-toggles/web/index.ts +6 -0
  314. package/src/feature-toggles/web/toggle-admin-screen.tsx +136 -0
  315. package/src/file-foundation/__tests__/feature.test.ts +5 -5
  316. package/src/file-foundation/__tests__/file-foundation.integration.test.ts +4 -3
  317. package/src/file-foundation/feature.ts +10 -11
  318. package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +27 -2
  319. package/src/files-provider-s3/s3-provider.ts +12 -1
  320. package/src/folders/__tests__/folders.integration.test.ts +3 -0
  321. package/src/folders/web/__tests__/folder-manager.test.tsx +43 -2
  322. package/src/folders/web/__tests__/tree.test.ts +8 -0
  323. package/src/folders/web/folder-manager.tsx +2 -3
  324. package/src/folders/web/i18n.ts +0 -2
  325. package/src/folders/web/tree.ts +13 -2
  326. package/src/folders-user-data/__tests__/hooks.integration.test.ts +1 -0
  327. package/src/folders-user-data/index.ts +4 -4
  328. package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
  329. package/src/inbound-mail-foundation/__tests__/connect-routes.integration.test.ts +307 -0
  330. package/src/inbound-mail-foundation/__tests__/feature.test.ts +169 -0
  331. package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +644 -0
  332. package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +111 -0
  333. package/src/inbound-mail-foundation/__tests__/retention.integration.test.ts +237 -0
  334. package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +433 -0
  335. package/src/inbound-mail-foundation/aggregate-id.ts +40 -0
  336. package/src/inbound-mail-foundation/connect-routes.ts +230 -0
  337. package/src/inbound-mail-foundation/constants.ts +80 -0
  338. package/src/inbound-mail-foundation/db/queries/inbound-projections.ts +38 -0
  339. package/src/inbound-mail-foundation/entities.ts +172 -0
  340. package/src/inbound-mail-foundation/events.ts +147 -0
  341. package/src/inbound-mail-foundation/feature.ts +243 -0
  342. package/src/inbound-mail-foundation/handlers/account-state.ts +23 -0
  343. package/src/inbound-mail-foundation/handlers/connect-account.write.ts +112 -0
  344. package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +69 -0
  345. package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +333 -0
  346. package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +44 -0
  347. package/src/inbound-mail-foundation/handlers/list-messages.query.ts +58 -0
  348. package/src/inbound-mail-foundation/handlers/scope-visibility.ts +14 -0
  349. package/src/inbound-mail-foundation/handlers/update-account.write.ts +78 -0
  350. package/src/inbound-mail-foundation/index.ts +97 -0
  351. package/src/inbound-mail-foundation/oauth-state.ts +103 -0
  352. package/src/inbound-mail-foundation/projection.ts +152 -0
  353. package/src/inbound-mail-foundation/provider-factory.ts +53 -0
  354. package/src/inbound-mail-foundation/retention-sweep.ts +168 -0
  355. package/src/inbound-mail-foundation/tenant-destroy-hook.ts +93 -0
  356. package/src/inbound-mail-foundation/types.ts +250 -0
  357. package/src/inbound-mail-foundation/watch-supervisor.ts +482 -0
  358. package/src/inbound-provider-imap/__tests__/feature.test.ts +115 -0
  359. package/src/inbound-provider-imap/__tests__/imap-foundation.integration.test.ts +302 -0
  360. package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +206 -0
  361. package/src/inbound-provider-imap/__tests__/plugin-mocked.test.ts +358 -0
  362. package/src/inbound-provider-imap/credential-document.ts +51 -0
  363. package/src/inbound-provider-imap/feature.ts +283 -0
  364. package/src/inbound-provider-imap/imap-client.ts +158 -0
  365. package/src/inbound-provider-imap/index.ts +17 -0
  366. package/src/inbound-provider-inmemory/__tests__/feature.test.ts +52 -0
  367. package/src/inbound-provider-inmemory/feature.ts +154 -0
  368. package/src/inbound-provider-inmemory/index.ts +12 -0
  369. package/src/jobs/__tests__/entity-ddl.test.ts +15 -0
  370. package/src/jobs/__tests__/job-instant.test.ts +15 -0
  371. package/src/jobs/__tests__/job-system-user.integration.test.ts +4 -8
  372. package/src/jobs/__tests__/jobs-events.integration.test.ts +37 -1
  373. package/src/jobs/__tests__/jobs-pii-kms.integration.test.ts +120 -0
  374. package/src/jobs/__tests__/jobs-screens.boot.test.ts +36 -0
  375. package/src/jobs/__tests__/jobs-security.integration.test.ts +108 -0
  376. package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +169 -0
  377. package/src/jobs/constants.ts +8 -1
  378. package/src/jobs/feature.ts +52 -8
  379. package/src/jobs/handlers/detail.query.ts +6 -0
  380. package/src/jobs/handlers/list.query.ts +8 -1
  381. package/src/jobs/handlers/projection-rebuild.job.ts +14 -4
  382. package/src/jobs/handlers/reindex-entity.job.ts +49 -0
  383. package/src/jobs/i18n.ts +37 -0
  384. package/src/jobs/job-instant.ts +9 -0
  385. package/src/jobs/job-run-table.ts +4 -4
  386. package/src/jobs/web/client-plugin.tsx +22 -0
  387. package/src/jobs/web/i18n.ts +12 -0
  388. package/src/jobs/web/index.ts +4 -0
  389. package/src/jobs/web/job-run-detail-screen.tsx +180 -0
  390. package/src/jobs/web/job-runs-screen.tsx +146 -0
  391. package/src/ledger/__tests__/ledger.integration.test.ts +8 -3
  392. package/src/ledger/handlers/confirm-schedule-period.write.ts +17 -4
  393. package/src/ledger/recurring.ts +17 -8
  394. package/src/ledger/web/index.ts +3 -2
  395. package/src/legal-pages/__tests__/legal-pages.integration.test.ts +55 -0
  396. package/src/legal-pages/feature.ts +24 -31
  397. package/src/legal-pages/index.ts +1 -0
  398. package/src/mail-foundation/__tests__/feature.test.ts +5 -6
  399. package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +4 -3
  400. package/src/mail-foundation/__tests__/mail-transport-contract.ts +58 -0
  401. package/src/mail-foundation/feature.ts +26 -24
  402. package/src/mail-transport-inmemory/__tests__/feature.test.ts +23 -0
  403. package/src/mail-transport-smtp/__tests__/feature.test.ts +65 -0
  404. package/src/managed-pages/__tests__/branding-coerce.test.ts +2 -6
  405. package/src/managed-pages/__tests__/i18n-surface.boot.test.ts +30 -0
  406. package/src/managed-pages/__tests__/managed-pages.integration.test.ts +137 -0
  407. package/src/managed-pages/feature.ts +45 -47
  408. package/src/managed-pages/handlers/branding.query.ts +5 -1
  409. package/src/managed-pages/handlers/by-tenant-published.query.ts +52 -0
  410. package/src/managed-pages/i18n.ts +66 -0
  411. package/src/managed-pages/screens/page-screens.ts +5 -1
  412. package/src/managed-pages/web/client-plugin.tsx +22 -0
  413. package/src/managed-pages/web/i18n.ts +24 -0
  414. package/src/managed-pages/web/index.ts +3 -0
  415. package/src/page-render/__tests__/layout.test.ts +63 -0
  416. package/src/page-render/__tests__/markdown.test.ts +29 -0
  417. package/src/page-render/branding.ts +4 -3
  418. package/src/page-render/index.ts +1 -1
  419. package/src/page-render/layout.ts +18 -3
  420. package/src/page-render/markdown.ts +1 -9
  421. package/src/page-render/web.ts +13 -0
  422. package/src/personal-access-tokens/__tests__/feature.test.ts +42 -0
  423. package/src/personal-access-tokens/__tests__/pat.integration.test.ts +239 -0
  424. package/src/personal-access-tokens/__tests__/scopes.test.ts +42 -0
  425. package/src/personal-access-tokens/constants.ts +33 -0
  426. package/src/personal-access-tokens/feature.ts +105 -0
  427. package/src/personal-access-tokens/handlers/available-scopes.query.ts +21 -0
  428. package/src/personal-access-tokens/handlers/create.write.ts +47 -0
  429. package/src/personal-access-tokens/handlers/list.query.ts +50 -0
  430. package/src/personal-access-tokens/handlers/revoke.write.ts +37 -0
  431. package/src/personal-access-tokens/hash.ts +17 -0
  432. package/src/personal-access-tokens/i18n.ts +5 -0
  433. package/src/personal-access-tokens/index.ts +31 -0
  434. package/src/personal-access-tokens/resolver.ts +58 -0
  435. package/src/personal-access-tokens/roles.ts +29 -0
  436. package/src/personal-access-tokens/schema/api-token.ts +63 -0
  437. package/src/personal-access-tokens/scopes.ts +43 -0
  438. package/src/personal-access-tokens/web/client-plugin.tsx +29 -0
  439. package/src/personal-access-tokens/web/i18n.ts +69 -0
  440. package/src/personal-access-tokens/web/index.ts +7 -0
  441. package/src/personal-access-tokens/web/pat-tokens-screen.tsx +265 -0
  442. package/src/readiness/__tests__/readiness.integration.test.ts +4 -3
  443. package/src/renderer-simple/__tests__/resolve-variables.test.ts +37 -0
  444. package/src/renderer-simple/resolve-variables.ts +3 -1
  445. package/src/secrets/__tests__/rotate.integration.test.ts +115 -18
  446. package/src/secrets/handlers/rotate.job.ts +77 -98
  447. package/src/secrets/secrets-context.ts +7 -38
  448. package/src/secrets/table.ts +7 -8
  449. package/src/seo/__tests__/llms-txt.test.ts +38 -0
  450. package/src/seo/__tests__/robots-txt.test.ts +18 -0
  451. package/src/seo/__tests__/schema-builders.test.ts +57 -0
  452. package/src/seo/__tests__/seo.integration.test.ts +350 -0
  453. package/src/seo/__tests__/sitemap.test.ts +38 -0
  454. package/src/seo/constants.ts +60 -0
  455. package/src/seo/feature.ts +316 -0
  456. package/src/seo/handlers/seo-config.query.ts +35 -0
  457. package/src/seo/index.ts +19 -0
  458. package/src/seo/llms-txt.ts +33 -0
  459. package/src/seo/robots-txt.ts +13 -0
  460. package/src/seo/schema-builders.ts +57 -0
  461. package/src/seo/sitemap.ts +33 -0
  462. package/src/sessions/__tests__/auto-revoke-binding.test.ts +71 -0
  463. package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
  464. package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +11 -7
  465. package/src/sessions/__tests__/rebuild-survival.integration.test.ts +18 -7
  466. package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
  467. package/src/sessions/__tests__/sessions-screens.boot.test.ts +55 -0
  468. package/src/sessions/__tests__/sessions.integration.test.ts +272 -20
  469. package/src/sessions/__tests__/test-helpers.ts +16 -3
  470. package/src/sessions/constants.ts +5 -0
  471. package/src/sessions/db/queries/cleanup.ts +2 -2
  472. package/src/sessions/feature.ts +175 -26
  473. package/src/sessions/handlers/detail.query.ts +36 -0
  474. package/src/sessions/handlers/list.query.ts +14 -9
  475. package/src/sessions/handlers/mine.query.ts +13 -8
  476. package/src/sessions/handlers/revoke-all-for-user.write.ts +51 -12
  477. package/src/sessions/handlers/revoke-all-others.write.ts +19 -1
  478. package/src/sessions/handlers/revoke.write.ts +13 -0
  479. package/src/sessions/i18n.ts +23 -0
  480. package/src/sessions/index.ts +9 -2
  481. package/src/sessions/schema/user-session.ts +8 -2
  482. package/src/sessions/session-callbacks.ts +99 -14
  483. package/src/sessions/session-checker-fail-open.test.ts +77 -0
  484. package/src/sessions/session-revoked-event.ts +29 -0
  485. package/src/sessions/testing.ts +14 -1
  486. package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
  487. package/src/shared/chunked-entity-migration.ts +89 -0
  488. package/src/shared/decrypt-stored-pii.ts +26 -0
  489. package/src/shared/encrypt-for-direct-write.ts +23 -0
  490. package/src/shared/entities-of.ts +17 -0
  491. package/src/shared/grace-period.test.ts +26 -0
  492. package/src/shared/grace-period.ts +20 -0
  493. package/src/{auth-email-password/__tests__ → shared}/identity-v3-hash.test.ts +2 -2
  494. package/src/shared/index.ts +16 -0
  495. package/src/shared/map-with-concurrency.test.ts +104 -0
  496. package/src/shared/map-with-concurrency.ts +38 -0
  497. package/src/shared/password-hashing.test.ts +35 -0
  498. package/src/{auth-email-password → shared}/password-hashing.ts +11 -0
  499. package/src/shared/system-query.ts +5 -0
  500. package/src/{auth-email-password → shared}/token-burn-store.ts +1 -1
  501. package/src/subscription-mollie/__tests__/feature.test.ts +97 -0
  502. package/src/subscription-mollie/__tests__/mollie-foundation.integration.test.ts +12 -1
  503. package/src/subscription-mollie/__tests__/verify-webhook.test.ts +21 -0
  504. package/src/subscription-mollie/verify-webhook.ts +1 -0
  505. package/src/subscription-stripe/__tests__/feature.test.ts +127 -1
  506. package/src/subscription-stripe/__tests__/runtime.test.ts +14 -24
  507. package/src/subscription-stripe/__tests__/stripe-foundation.integration.test.ts +15 -5
  508. package/src/subscription-stripe/__tests__/verify-webhook.test.ts +20 -0
  509. package/src/subscription-stripe/feature.ts +2 -0
  510. package/src/subscription-stripe/verify-webhook.ts +3 -2
  511. package/src/tags/__tests__/schemas.test.ts +34 -0
  512. package/src/tags/__tests__/tags.integration.test.ts +12 -1
  513. package/src/tags/entity.ts +6 -1
  514. package/src/tags/feature.ts +2 -0
  515. package/src/tags/i18n.ts +5 -0
  516. package/src/tags/schemas.ts +9 -2
  517. package/src/tags/web/__tests__/tag-manager.test.tsx +333 -0
  518. package/src/tags/web/__tests__/tag-section.test.tsx +47 -1
  519. package/src/tags/web/tag-manager.tsx +1 -1
  520. package/src/tags/web/tag-section.tsx +6 -3
  521. package/src/template-resolver/testing.ts +2 -1
  522. package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
  523. package/src/tenant/__tests__/members-screens.boot.test.ts +51 -0
  524. package/src/tenant/__tests__/multi-tenant.integration.test.ts +1 -1
  525. package/src/tenant/__tests__/tenant-security.integration.test.ts +310 -0
  526. package/src/tenant/__tests__/tenant.integration.test.ts +8 -4
  527. package/src/tenant/constants.ts +5 -0
  528. package/src/tenant/feature.ts +19 -0
  529. package/src/tenant/handlers/active-tenant-ids.query.ts +8 -2
  530. package/src/tenant/handlers/cancel-invitation.write.ts +2 -2
  531. package/src/tenant/handlers/invitations.query.ts +21 -4
  532. package/src/tenant/handlers/members.query.ts +41 -6
  533. package/src/tenant/i18n.ts +28 -0
  534. package/src/tenant/index.ts +7 -2
  535. package/src/tenant/invitation-table.ts +3 -2
  536. package/src/tenant/schema/tenant.ts +27 -2
  537. package/src/tenant/screens.ts +3 -2
  538. package/src/tenant/seeding.ts +136 -3
  539. package/src/tenant/web/client-plugin.tsx +20 -0
  540. package/src/tenant/web/i18n.ts +43 -0
  541. package/src/tenant/web/index.ts +4 -0
  542. package/src/tenant/web/members-screen.tsx +270 -0
  543. package/src/tenant-lifecycle/__tests__/stages.test.ts +22 -0
  544. package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +471 -0
  545. package/src/tenant-lifecycle/boot-checks.ts +45 -0
  546. package/src/tenant-lifecycle/constants.ts +56 -0
  547. package/src/tenant-lifecycle/events.ts +46 -0
  548. package/src/tenant-lifecycle/feature.ts +112 -0
  549. package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +79 -0
  550. package/src/tenant-lifecycle/handlers/request-destruction.write.ts +83 -0
  551. package/src/tenant-lifecycle/index.ts +7 -0
  552. package/src/tenant-lifecycle/lib/revoke-tenant-sessions.ts +16 -0
  553. package/src/tenant-lifecycle/lifecycle-gate.ts +65 -0
  554. package/src/tenant-lifecycle/run-tenant-destroy.ts +368 -0
  555. package/src/tenant-lifecycle/stages.ts +206 -0
  556. package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +115 -0
  557. package/src/tenant-settings/config.ts +44 -0
  558. package/src/tenant-settings/constants.ts +16 -0
  559. package/src/tenant-settings/feature.ts +49 -0
  560. package/src/tenant-settings/index.ts +8 -0
  561. package/src/tenant-settings/tenant-defaults.ts +67 -0
  562. package/src/text-content/web/__tests__/editor-read-only.test.tsx +33 -6
  563. package/src/text-content/web/__tests__/group-blocks.test.ts +28 -3
  564. package/src/text-content/web/client-plugin.tsx +126 -104
  565. package/src/tier-engine/__tests__/auto-default-tier.integration.test.ts +1 -1
  566. package/src/tier-engine/__tests__/resolver.integration.test.ts +1 -1
  567. package/src/tier-engine/__tests__/tier-engine.integration.test.ts +7 -4
  568. package/src/tier-engine/__tests__/trial-config-validation.test.ts +30 -0
  569. package/src/tier-engine/feature.ts +13 -6
  570. package/src/tier-engine/i18n.ts +8 -0
  571. package/src/tier-engine/web/tier-admin-screen.tsx +26 -19
  572. package/src/user/__tests__/stream-tenant-backfill.integration.test.ts +186 -0
  573. package/src/user/__tests__/user.integration.test.ts +26 -0
  574. package/src/user/constants.ts +1 -0
  575. package/src/user/db/queries/stream-tenant-backfill.ts +112 -0
  576. package/src/user/feature.ts +3 -0
  577. package/src/user/handlers/find-for-auth.query.ts +8 -6
  578. package/src/user/i18n.ts +20 -0
  579. package/src/user/index.ts +4 -0
  580. package/src/user/schema/user.ts +6 -2
  581. package/src/user/screens.ts +3 -15
  582. package/src/user/seeding.ts +8 -0
  583. package/src/user-data-rights/COMPLIANCE.md +21 -1
  584. package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +145 -0
  585. package/src/user-data-rights/__tests__/anonymous-deletion.integration.test.ts +3 -0
  586. package/src/user-data-rights/__tests__/audit-log.integration.test.ts +67 -0
  587. package/src/user-data-rights/__tests__/boot-checks.test.ts +170 -0
  588. package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +4 -1
  589. package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +169 -0
  590. package/src/user-data-rights/__tests__/download.integration.test.ts +101 -33
  591. package/src/user-data-rights/__tests__/email-templates.test.ts +15 -0
  592. package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +154 -0
  593. package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +2 -0
  594. package/src/user-data-rights/__tests__/export-job-list-cross-tenant.integration.test.ts +2 -0
  595. package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +4 -1
  596. package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +4 -1
  597. package/src/user-data-rights/__tests__/file-retention.integration.test.ts +4 -1
  598. package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +4 -1
  599. package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +41 -62
  600. package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +110 -0
  601. package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +14 -0
  602. package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +19 -2
  603. package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +25 -14
  604. package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +2 -0
  605. package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +4 -1
  606. package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
  607. package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +172 -19
  608. package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +2 -0
  609. package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +257 -26
  610. package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +339 -5
  611. package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +77 -1
  612. package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +2 -0
  613. package/src/user-data-rights/__tests__/user-data-rights.integration.test.ts +9 -1
  614. package/src/user-data-rights/boot-checks.ts +70 -0
  615. package/src/user-data-rights/db/queries/__tests__/forget-cleanup.integration.test.ts +69 -0
  616. package/src/user-data-rights/db/queries/forget-cleanup.ts +9 -2
  617. package/src/user-data-rights/email-templates.ts +6 -6
  618. package/src/user-data-rights/feature.ts +162 -43
  619. package/src/user-data-rights/handlers/cancel-deletion.write.ts +8 -11
  620. package/src/user-data-rights/handlers/deletion-grace-period.ts +4 -1
  621. package/src/user-data-rights/handlers/download-by-job.query.ts +26 -18
  622. package/src/user-data-rights/handlers/download-by-token.query.ts +23 -15
  623. package/src/user-data-rights/handlers/lift-restriction.write.ts +44 -19
  624. package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -0
  625. package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -0
  626. package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +3 -1
  627. package/src/user-data-rights/handlers/restrict-account.write.ts +58 -7
  628. package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +2 -0
  629. package/src/user-data-rights/i18n.ts +35 -32
  630. package/src/user-data-rights/lib/is-admin-actor.ts +19 -0
  631. package/src/user-data-rights/lib/storage-provider-resolver.ts +6 -5
  632. package/src/user-data-rights/lib/update-user-lifecycle.ts +8 -24
  633. package/src/user-data-rights/run-export-jobs.ts +55 -19
  634. package/src/user-data-rights/run-forget-cleanup.ts +112 -7
  635. package/src/user-data-rights/run-user-export.ts +70 -4
  636. package/src/user-data-rights/schema/download-token.ts +5 -3
  637. package/src/user-data-rights/schema/export-job.ts +2 -0
  638. package/src/user-data-rights/screens.ts +2 -1
  639. package/src/user-data-rights/web/__tests__/privacy-center-screen.test.tsx +1 -0
  640. package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +7 -1
  641. package/src/user-data-rights/web/client-plugin.tsx +6 -1
  642. package/src/user-data-rights/web/i18n.ts +30 -10
  643. package/src/user-data-rights/web/privacy-center-screen.tsx +5 -3
  644. package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +455 -0
  645. package/src/user-data-rights-defaults/__tests__/feature-mounted.test.ts +24 -0
  646. package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +33 -9
  647. package/src/user-data-rights-defaults/feature.ts +78 -1
  648. package/src/user-data-rights-defaults/hooks/api-token.userdata-hook.ts +37 -0
  649. package/src/user-data-rights-defaults/hooks/config-value.userdata-hook.ts +51 -0
  650. package/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +37 -0
  651. package/src/user-data-rights-defaults/hooks/feature-mounted.ts +10 -0
  652. package/src/user-data-rights-defaults/hooks/in-app-message.userdata-hook.ts +38 -0
  653. package/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +35 -0
  654. package/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +54 -0
  655. package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +112 -0
  656. package/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +42 -0
  657. package/src/user-data-rights-defaults/index.ts +26 -0
  658. package/src/user-profile/__tests__/change-email.integration.test.ts +7 -1
  659. package/src/user-profile/__tests__/profile-screen.test.tsx +5 -0
  660. package/src/user-profile/feature.ts +4 -1
  661. package/src/user-profile/handlers/change-email.write.ts +15 -10
  662. package/src/user-profile/web/profile-screen.tsx +15 -3
  663. package/src/auth-email-password/stream-tenant.ts +0 -31
  664. package/src/feature-toggles/db/queries/toggle-state.ts +0 -25
  665. package/src/legal-pages/security-headers.ts +0 -1
  666. package/src/sessions/schema/index.ts +0 -5
  667. package/src/tenant/schema/index.ts +0 -5
  668. package/src/user/schema/index.ts +0 -5
  669. /package/src/{auth-email-password → shared}/identity-v3-hash.ts +0 -0
@@ -67,6 +67,7 @@ export const userEntity = createEntity({
67
67
  format: "email",
68
68
  maxLength: 320,
69
69
  pii: true,
70
+ lookupable: true,
70
71
  access: { write: access.privileged },
71
72
  }),
72
73
 
@@ -79,8 +80,10 @@ export const userEntity = createEntity({
79
80
  }),
80
81
 
81
82
  // Profile — user-editable. Display-name is real-name in most apps,
82
- // so treat as PII for DSGVO export/forget pipelines.
83
- displayName: createTextField({ required: true, maxLength: 100, searchable: true, pii: true }),
83
+ // so treat as PII for DSGVO export/forget pipelines. NOT searchable:
84
+ // substring search on an encrypted field would require plaintext copies
85
+ // in the search index (boot-validator rejects the combination, #818).
86
+ displayName: createTextField({ required: true, maxLength: 100, pii: true }),
84
87
  locale: createTextField({ maxLength: 10, default: "de" }),
85
88
 
86
89
  // Which tenant should this user land in on next login. Set by the login
@@ -133,6 +136,7 @@ export const userEntity = createEntity({
133
136
  required: true,
134
137
  default: USER_STATUS.Active,
135
138
  options: USER_STATUS_OPTIONS,
139
+ sortable: true,
136
140
  access: { write: access.privileged },
137
141
  }),
138
142
 
@@ -7,10 +7,6 @@ import type {
7
7
  // feature runs with `r.systemScope()`, the entityList query returns every
8
8
  // user across all tenants — the SystemAdmin platform roster. Both screens are
9
9
  // SystemAdmin-gated and stay inert until an app navs them (no auto-nav).
10
- //
11
- // Field labels come from the renderer's humanizeSlug fallback (no i18n keys
12
- // registered) — "Display Name", "Email Verified" etc. Apps can override via
13
- // their own translations under the `user:entity:user:field:*` convention.
14
10
 
15
11
  export const userListScreen: EntityListScreenDefinition = {
16
12
  id: "user-list",
@@ -26,9 +22,9 @@ export const userListScreen: EntityListScreenDefinition = {
26
22
  entityId: "id",
27
23
  },
28
24
  ],
29
- // No SearchAdapter assumption: search is opt-in per app infra, not a
30
- // universal default for a bundled screen.
31
- searchable: false,
25
+ defaultSort: { field: "status", dir: "asc" },
26
+ // ponytail: screen searchable but email is encrypted PII — list search uses non-encrypted columns only.
27
+ searchable: true,
32
28
  access: { roles: ["SystemAdmin"] },
33
29
  };
34
30
 
@@ -44,14 +40,6 @@ export const userEditScreen: EntityEditScreenDefinition = {
44
40
  },
45
41
  ],
46
42
  },
47
- // `roles` is deliberately NOT editable here: it is a raw JSON text column
48
- // (`["SystemAdmin"]`) — a free-text input would let a typo corrupt the
49
- // privilege column on a live platform. Role management needs a dedicated
50
- // surface; the list still shows status for triage.
51
- //
52
- // Create dispatches user:write:user:create (email + displayName required —
53
- // both in the form). Delete is suppressed: there is no user:write:user:delete
54
- // — user removal is the GDPR status/forget flow, not a hard delete.
55
43
  allowDelete: false,
56
44
  access: { roles: ["SystemAdmin"] },
57
45
  };
@@ -18,6 +18,8 @@ import {
18
18
  } from "@cosmicdrift/kumiko-framework/db";
19
19
  import type { SessionUser } from "@cosmicdrift/kumiko-framework/engine";
20
20
  import { TestUsers } from "@cosmicdrift/kumiko-framework/stack";
21
+ // kumiko-lint-ignore cross-feature-import shares the postSave-hooks fixture contract with tenant/seeding (#1478)
22
+ import { fireEntityPostSave, type SeedTenantHooks } from "../tenant/seeding";
21
23
  import { userEntity, userTable } from "./schema/user";
22
24
 
23
25
  const userExecutor = createEventStoreExecutor(userTable, userEntity, { entityName: "user" });
@@ -52,6 +54,11 @@ export type SeedUserOptions = {
52
54
  export async function seedUser(
53
55
  db: DbConnection,
54
56
  options: SeedUserOptions,
57
+ // Optional, append-only — same rationale as tenant/seeding's seedTenant
58
+ // hooks param (#1478): fires user's postSave hooks (e.g. a Welcome-
59
+ // notification hook keyed off `allOf: "user"`) for fixtures/self-signup
60
+ // that opt in. Omit to keep today's hook-less behavior.
61
+ hooks?: SeedTenantHooks,
55
62
  ): Promise<{ id: string }> {
56
63
  const by = options.by ?? TestUsers.systemAdmin;
57
64
  // executor.create erwartet eine TenantDb (mit .insert()-API). User
@@ -81,6 +88,7 @@ export async function seedUser(
81
88
  `seedUser failed: ${result.error.code} — ${JSON.stringify(result.error.details ?? {})}`,
82
89
  );
83
90
  }
91
+ await fireEntityPostSave(hooks, "user:seed", result.data);
84
92
  return { id: extractId(result.data, "seedUser") };
85
93
  }
86
94
 
@@ -20,7 +20,7 @@ Dieses Dokument bündelt die **technischen Fakten** zur DSGVO-Pipeline
20
20
  |---------|-------------------|--------------|--------------|
21
21
  | **Art. 15 (Auskunft, light)** | `user-data-rights:query:my-audit-log` | User | Liest eigene Framework-Events aus `kumiko_events` (account-weit über alle Memberships). Filterbar nach `eventType`, `aggregateType`, `from`/`to`. Domain-Entities ohne `ctx.appendEvent` erscheinen NICHT — die kommen im Export-Bundle (Art. 20). |
22
22
  | **Art. 15 + 20 (Auskunft + Portabilität)** | `user-data-rights:write:request-export` | User | Async Job → ZIP mit user-Profil + fileRefs + alle EXT_USER_DATA-Provider-Daten (cross-tenant) + signed Magic-Link per Email. Idempotent: nur 1 active Job pro User (`ACTIVE_JOB_CONSTRAINT`). |
23
- | **Art. 15 + 20** | `GET /user-export/by-token?token=…` | Anonym (Magic-Link-Pfad) | Token-Hash-Lookup → 302-Redirect auf signed Storage-URL. Multi-use innerhalb TTL. Audit: `lastUsedAt`, `lastUsedFromIp`, `lastUsedUserAgent`. |
23
+ | **Art. 15 + 20** | `GET /user-export/by-token` (interstitial) + `POST /user-export/by-token` (exchange) | Anonym (Magic-Link-Pfad) | Email-Link trägt den Token als URL-Fragment (`#token=`, nie an den Server gesendet, issue #1271); Interstitial-Script liest das Fragment client-seitig und tauscht es per POST-Body gegen die signed Storage-URL. Kein `?token=`-Query-Fallback (#1512) — Query-Param landet unveränderlich in Proxy-/Access-Logs. Multi-use innerhalb TTL. Audit: `lastUsedAt`, `lastUsedFromIp`, `lastUsedUserAgent`. |
24
24
  | **Art. 15 + 20** | `GET /user-export/by-job/:jobId` | User (Session-Auth) | UI-Klick-Pfad. Cross-tenant-same-user: User in Tenant B kann Job aus Tenant A laden wenn er der Owner ist. |
25
25
  | **Art. 17 (Löschung)** | `user-data-rights:write:request-deletion` | User | Soft-Delete: `status=DeletionRequested` + `gracePeriodEnd = now + profile.gracePeriod`. Cron `run-forget-cleanup` führt nach Grace die Anonymisierung durch. |
26
26
  | **Art. 17** | `user-data-rights:write:cancel-deletion` | User | Während Grace: status zurück auf `Active`. |
@@ -45,6 +45,26 @@ Dieses Dokument bündelt die **technischen Fakten** zur DSGVO-Pipeline
45
45
  | `read_tenant_retention_overrides` | Per-Tenant Retention-Override pro Entity | nein | unbounded (Konfiguration) | Aufbewahrungspflicht-Edge-Cases |
46
46
  | Storage-Provider (Local / S3) | Export-ZIPs + File-Binaries | indirekt (Inhalt) | Local: per `exportDownloadTtl` Cleanup. S3: lifecycle-Policy (App-Author-Verantwortung) | Daten-Export-Auslieferung |
47
47
 
48
+ ### Bekanntes Residuum: `createdBy` / `metadata.userId` in `kumiko_events`
49
+
50
+ Nach der Forget-Pipeline (Art. 17, Abschnitt 1) bleiben `kumiko_events.created_by`
51
+ und `metadata.userId` (Event-Store-Schema `events-schema.ts`, EventMetadata-Feld)
52
+ als Klartext-UUID stehen. Die Forget-Hooks aus `user-data-rights-defaults` fassen
53
+ `kumiko_events` nicht an — `createdBy`/`metadata.userId` sind keine über
54
+ `defineEvent(..., { piiFields })` deklarierten Payload-PII-Felder (siehe
55
+ `packages/framework/src/crypto/event-pii.ts`) und laufen deshalb nicht durch die
56
+ Payload-Verschlüsselung. Löschung dieser Rows erfolgt ausschließlich über die
57
+ zeitbasierte `data-retention`-Policy (`keepFor`, s. `kumiko_events`-Zeile in Abschnitt
58
+ 2 oben), unabhängig vom User-Forget-Antrag.
59
+
60
+ Technische Basis: Der `read_users`-Row zur betroffenen UUID wird beim Forget-Run
61
+ in-place anonymisiert, nicht hard-gelöscht (Sentinel-Pattern, siehe Abschnitt 4
62
+ „Integrität"). Email, Displayname und Passwort-Hash werden dabei entfernt/ersetzt —
63
+ das ist der Schlüssel, der die Verknüpfung kappt: Sobald diese Felder weg sind, gibt
64
+ es keinen Rückweg von der UUID in `created_by` zu einer realen Identität mehr. Die
65
+ UUID bleibt als stabiler, aber orphaned Pseudonym-Wert bestehen, bis die
66
+ Retention-Policy irgendwann das Event selbst löscht.
67
+
48
68
  ---
49
69
 
50
70
  ## 3. Compliance-Profile
@@ -0,0 +1,145 @@
1
+ // Anonymer Deletion-Flow mit aktivem PII-KMS + Blind-Index (#818 PR 2):
2
+ // die User-Row liegt verschlüsselt in der DB. request-deletion-by-email muss
3
+ // den User über die email_bidx-Spalte finden und die Verify-Mail an den
4
+ // KLARTEXT schicken (vorher: Ciphertext-Adresse → Mail unzustellbar).
5
+
6
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
7
+ import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
8
+ import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
9
+ import {
10
+ configureBlindIndexKey,
11
+ configurePiiSubjectKms,
12
+ InMemoryKmsAdapter,
13
+ isPiiCiphertext,
14
+ resetBlindIndexKeyForTests,
15
+ resetPiiSubjectKmsForTests,
16
+ } from "@cosmicdrift/kumiko-framework/crypto";
17
+ import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
18
+ import {
19
+ setupTestStack,
20
+ type TestStack,
21
+ testTenantId,
22
+ unsafeCreateEntityTable,
23
+ } from "@cosmicdrift/kumiko-framework/stack";
24
+ import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
25
+ import {
26
+ createComplianceProfilesFeature,
27
+ tenantComplianceProfileEntity,
28
+ tenantComplianceProfileTable,
29
+ } from "../../compliance-profiles";
30
+ import { createDataRetentionFeature } from "../../data-retention";
31
+ import { createSessionsFeature } from "../../sessions";
32
+ import { USER_STATUS, userEntity, userTable } from "../../user";
33
+ import { createUserFeature } from "../../user/feature";
34
+ import { seedUser } from "../../user/seeding";
35
+ import { createUserDataRightsFeature } from "../feature";
36
+ import type { SendDeletionVerificationEmailFn } from "../handlers/request-deletion-by-email.write";
37
+
38
+ const REQUEST_BY_EMAIL = "user-data-rights:write:request-deletion-by-email";
39
+ const CONFIRM_BY_TOKEN = "user-data-rights:write:confirm-deletion-by-token";
40
+ const DELETION_SECRET = "test-deletion-secret-0123456789abcdef";
41
+ const VERIFY_URL = "https://app.example.test/delete-account/confirm";
42
+ const ALICE_EMAIL = "alice.kms-deletion@example.com";
43
+ const BIDX_KEY = Buffer.alloc(32, 7).toString("base64");
44
+
45
+ const tenantA = testTenantId(1);
46
+
47
+ type VerifyArgs = Parameters<SendDeletionVerificationEmailFn>[0];
48
+ const verifyCalls: VerifyArgs[] = [];
49
+ const sendDeletionVerificationEmail: SendDeletionVerificationEmailFn = async (args) => {
50
+ verifyCalls.push(args);
51
+ };
52
+
53
+ let stack: TestStack;
54
+ let aliceId: string;
55
+
56
+ beforeAll(async () => {
57
+ stack = await setupTestStack({
58
+ features: [
59
+ createUserFeature(),
60
+ createDataRetentionFeature(),
61
+ createComplianceProfilesFeature(),
62
+ authFoundationFeature,
63
+ createSessionsFeature(),
64
+ createUserDataRightsFeature({
65
+ deletionTokenSecret: DELETION_SECRET,
66
+ deletionVerifyUrl: VERIFY_URL,
67
+ sendDeletionVerificationEmail,
68
+ }),
69
+ ],
70
+ anonymousAccess: { defaultTenantId: tenantA },
71
+ });
72
+ await unsafeCreateEntityTable(stack.db, userEntity);
73
+ await unsafeCreateEntityTable(stack.db, tenantComplianceProfileEntity);
74
+ await createEventsTable(stack.db);
75
+ });
76
+
77
+ afterAll(async () => {
78
+ await stack.cleanup();
79
+ });
80
+
81
+ beforeEach(async () => {
82
+ verifyCalls.length = 0;
83
+ await resetTestTables(stack.db, [userTable, tenantComplianceProfileTable, eventsTable]);
84
+
85
+ // KMS + bidx-Key VOR dem Seed — seedUser läuft über den Executor, die
86
+ // Row soll den verschlüsselten Prod-Zustand abbilden.
87
+ configurePiiSubjectKms(new InMemoryKmsAdapter());
88
+ configureBlindIndexKey(BIDX_KEY);
89
+ ({ id: aliceId } = await seedUser(stack.db, {
90
+ email: ALICE_EMAIL,
91
+ displayName: "Alice",
92
+ passwordHash: "hashed",
93
+ emailVerified: true,
94
+ }));
95
+ });
96
+
97
+ afterEach(() => {
98
+ resetPiiSubjectKmsForTests();
99
+ resetBlindIndexKeyForTests();
100
+ });
101
+
102
+ function tokenFromLastVerifyCall(): string {
103
+ const url = new URL(verifyCalls[0]?.verifyUrl ?? "");
104
+ return url.searchParams.get("token") ?? "";
105
+ }
106
+
107
+ describe("anonymous deletion flow with active KMS", () => {
108
+ test("request-by-email finds the encrypted row via bidx and mails the PLAINTEXT address", async () => {
109
+ const rows = await asRawClient(stack.db).unsafe<Record<string, unknown>>(
110
+ `SELECT email, email_bidx FROM "${userTable.tableName}" WHERE id = $1`,
111
+ [aliceId],
112
+ );
113
+ expect(isPiiCiphertext(rows[0]?.["email"])).toBe(true);
114
+ expect(String(rows[0]?.["email_bidx"])).toStartWith("kumiko-bidx:v1:");
115
+
116
+ const res = await stack.http.raw("POST", "/api/write", {
117
+ type: REQUEST_BY_EMAIL,
118
+ payload: { email: ALICE_EMAIL },
119
+ });
120
+ expect(res.status).toBe(200);
121
+
122
+ expect(verifyCalls).toHaveLength(1);
123
+ expect(verifyCalls[0]?.email).toBe(ALICE_EMAIL);
124
+ expect(verifyCalls[0]?.verifyUrl).toContain("token=");
125
+ });
126
+
127
+ test("confirm-by-token flips to DeletionRequested; response carries no ciphertext", async () => {
128
+ await stack.http.raw("POST", "/api/write", {
129
+ type: REQUEST_BY_EMAIL,
130
+ payload: { email: ALICE_EMAIL },
131
+ });
132
+ const token = tokenFromLastVerifyCall();
133
+ expect(token).not.toBe("");
134
+
135
+ const res = await stack.http.raw("POST", "/api/write", {
136
+ type: CONFIRM_BY_TOKEN,
137
+ payload: { token },
138
+ });
139
+ expect(res.status).toBe(200);
140
+ const bodyText = await res.text();
141
+ expect(bodyText).not.toContain("kumiko-pii:");
142
+ const body = JSON.parse(bodyText) as { data?: { status?: string } };
143
+ expect(body.data?.status).toBe(USER_STATUS.DeletionRequested);
144
+ });
145
+ });
@@ -6,6 +6,7 @@
6
6
  // Beweist end-to-end via echte /api/write-Calls OHNE Auth (anonymousAccess).
7
7
 
8
8
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
9
+ import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
9
10
  import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
10
11
  import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
11
12
  import {
@@ -53,6 +54,7 @@ beforeAll(async () => {
53
54
  createUserFeature(),
54
55
  createDataRetentionFeature(),
55
56
  createComplianceProfilesFeature(),
57
+ authFoundationFeature,
56
58
  createSessionsFeature(),
57
59
  createUserDataRightsFeature({
58
60
  deletionTokenSecret: DELETION_SECRET,
@@ -331,6 +333,7 @@ describe("anonymous deletion flow — not configured (kein Secret)", () => {
331
333
  createUserFeature(),
332
334
  createDataRetentionFeature(),
333
335
  createComplianceProfilesFeature(),
336
+ authFoundationFeature,
334
337
  createSessionsFeature(),
335
338
  createUserDataRightsFeature({
336
339
  sendDeletionVerificationEmail: async (args) => {
@@ -1,6 +1,7 @@
1
1
  // S2.U7 — my-audit-log + invalid-attempt-audit + list-download-attempts.
2
2
 
3
3
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
4
+ import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
4
5
  import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
5
6
  import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
6
7
  import {
@@ -21,6 +22,8 @@ import { createSessionsFeature } from "../../sessions";
21
22
  import { USER_STATUS, userEntity, userTable } from "../../user";
22
23
  import { createUserFeature } from "../../user/feature";
23
24
  import { createUserDataRightsFeature } from "../feature";
25
+ import { listDownloadAttemptsQuery } from "../handlers/list-download-attempts.query";
26
+ import { myAuditLogQuery } from "../handlers/my-audit-log.query";
24
27
  import { downloadAttemptEntity, downloadAttemptsTable } from "../schema/download-attempt";
25
28
 
26
29
  const MY_AUDIT = "user-data-rights:query:my-audit-log";
@@ -39,6 +42,7 @@ beforeAll(async () => {
39
42
  createUserFeature(),
40
43
  createDataRetentionFeature(),
41
44
  createComplianceProfilesFeature(),
45
+ authFoundationFeature,
42
46
  createSessionsFeature(),
43
47
 
44
48
  createUserDataRightsFeature(),
@@ -156,6 +160,69 @@ describe("my-audit-log", () => {
156
160
  });
157
161
  });
158
162
 
163
+ describe("my-audit-log — kumiko-framework#1525: no ambient Temporal global", () => {
164
+ test("from/to filter works without deleting globalThis.Temporal for handler-internal state", async () => {
165
+ // Seed with Temporal intact — only the handler call runs ambient-free,
166
+ // mirroring the audit feature's #1525 regression test (going through
167
+ // full HTTP dispatch would also break buildHandlerContext's unrelated
168
+ // ctx.tz setup, which isn't what this pins down).
169
+ await seedEvent(alice.id, tenantA, "user.x", { from: "range-test" });
170
+
171
+ const savedGlobal = (globalThis as { Temporal?: unknown }).Temporal;
172
+ delete (globalThis as { Temporal?: unknown }).Temporal;
173
+ try {
174
+ const res = await myAuditLogQuery.handler(
175
+ {
176
+ payload: { limit: 50, from: "2020-01-01T00:00:00Z", to: "2030-01-01T00:00:00Z" },
177
+ user: alice,
178
+ } as Parameters<typeof myAuditLogQuery.handler>[0],
179
+ { db: { raw: stack.db } } as unknown as Parameters<typeof myAuditLogQuery.handler>[1],
180
+ );
181
+ expect(res.rows.length).toBeGreaterThanOrEqual(1);
182
+ } finally {
183
+ if (savedGlobal === undefined) delete (globalThis as { Temporal?: unknown }).Temporal;
184
+ else (globalThis as { Temporal?: unknown }).Temporal = savedGlobal;
185
+ }
186
+ });
187
+ });
188
+
189
+ describe("list-download-attempts — kumiko-framework#1525: no ambient Temporal global", () => {
190
+ test("from/to filter works without relying on globalThis.Temporal", async () => {
191
+ const T = await import("@cosmicdrift/kumiko-framework/time");
192
+ const now = T.getTemporal().Now.instant();
193
+ await seedRows(stack.db, downloadAttemptsTable, [
194
+ {
195
+ id: "33333333-3333-4333-8333-333333333333",
196
+ tenantId: tenantA,
197
+ result: "notFound",
198
+ via: "token",
199
+ tokenHash: "abc",
200
+ jobId: null,
201
+ attemptedByUserId: null,
202
+ ip: "1.2.3.4",
203
+ userAgent: "test",
204
+ attemptedAt: now,
205
+ },
206
+ ]);
207
+
208
+ const savedGlobal = (globalThis as { Temporal?: unknown }).Temporal;
209
+ delete (globalThis as { Temporal?: unknown }).Temporal;
210
+ try {
211
+ const res = await listDownloadAttemptsQuery.handler(
212
+ {
213
+ payload: { limit: 50, from: "2020-01-01T00:00:00Z", to: "2030-01-01T00:00:00Z" },
214
+ user: admin,
215
+ } as Parameters<typeof listDownloadAttemptsQuery.handler>[0],
216
+ { db: stack.db } as unknown as Parameters<typeof listDownloadAttemptsQuery.handler>[1],
217
+ );
218
+ expect(res.rows.length).toBeGreaterThanOrEqual(1);
219
+ } finally {
220
+ if (savedGlobal === undefined) delete (globalThis as { Temporal?: unknown }).Temporal;
221
+ else (globalThis as { Temporal?: unknown }).Temporal = savedGlobal;
222
+ }
223
+ });
224
+ });
225
+
159
226
  describe("download-attempt retention :: disk-bomb-Schutz bei Brute-Force", () => {
160
227
  test("Entity-Default ist 90d hardDelete (kein unbounded growth)", () => {
161
228
  expect(downloadAttemptEntity.retention).toBeDefined();
@@ -0,0 +1,170 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { integer, type SchemaTable, table, uuid } from "@cosmicdrift/kumiko-framework/db";
3
+ import {
4
+ createEntity,
5
+ createTextField,
6
+ defineFeature,
7
+ EXT_TENANT_DATA,
8
+ EXT_USER_DATA,
9
+ validateBoot,
10
+ } from "@cosmicdrift/kumiko-framework/engine";
11
+ import { createFilesFeature } from "@cosmicdrift/kumiko-framework/files";
12
+ import { z } from "zod";
13
+ import { authFoundationFeature } from "../../auth-foundation";
14
+ import { createComplianceProfilesFeature } from "../../compliance-profiles/feature";
15
+ import { createConfigFeature } from "../../config/feature";
16
+ import { createDataRetentionFeature } from "../../data-retention/feature";
17
+ import { createPersonalAccessTokensFeature } from "../../personal-access-tokens";
18
+ import { createSessionsFeature } from "../../sessions/feature";
19
+ import { createTenantFeature } from "../../tenant/feature";
20
+ import { createTenantLifecycleFeature } from "../../tenant-lifecycle/feature";
21
+
22
+ function projectionProbeTable(name: string): SchemaTable {
23
+ return table(name, {
24
+ id: uuid("id").primaryKey(),
25
+ count: integer("count").notNull().default(0),
26
+ }) as unknown as SchemaTable; // @cast-boundary test-fixture — minimal probe shape, real schema-table irrelevant here
27
+ }
28
+
29
+ import { createUserFeature } from "../../user/feature";
30
+ import { createUserDataRightsDefaultsFeature } from "../../user-data-rights-defaults/feature";
31
+ import { createUserDataRightsFeature } from "../feature";
32
+
33
+ // GDPR-storage guards V2 (export-without-erase) and V3 (pii-entity-without-
34
+ // hook) moved off the framework-internal boot-validator onto user-data-
35
+ // rights' own `r.bootCheck()` (#1314); V4 (tenantOwned-entity-without-hook)
36
+ // onto tenant-lifecycle's own `r.bootCheck()` — tenant-lifecycle owns
37
+ // EXT_TENANT_DATA, so its own mount preserves the original guard's trigger
38
+ // exactly (gating on a sibling feature would silently narrow coverage).
39
+ // Unlike the old direct-function tests, these only run inside a real
40
+ // validateBoot() pass — exercising the actual `r.bootCheck()` plumbing, not
41
+ // just the guard logic.
42
+ function baseFeatures() {
43
+ return [
44
+ createConfigFeature(),
45
+ createUserFeature(),
46
+ createTenantFeature(),
47
+ authFoundationFeature,
48
+ createPersonalAccessTokensFeature({ scopes: {} }),
49
+ createSessionsFeature(),
50
+ createDataRetentionFeature(),
51
+ createComplianceProfilesFeature(),
52
+ createFilesFeature(),
53
+ createUserDataRightsFeature(),
54
+ createUserDataRightsDefaultsFeature(),
55
+ ];
56
+ }
57
+
58
+ // Minimal tenant-lifecycle assembly with NO user-data-rights* mounted at all
59
+ // — proves V4 fires from tenant-lifecycle's own mount, not from a sibling
60
+ // feature accidentally re-gating it.
61
+ function minimalTenantLifecycleFeatures() {
62
+ return [
63
+ createConfigFeature(),
64
+ createTenantFeature(),
65
+ createComplianceProfilesFeature(),
66
+ createTenantLifecycleFeature(),
67
+ ];
68
+ }
69
+
70
+ describe("GDPR-storage boot guards V2-V4 (via r.bootCheck)", () => {
71
+ test("known-good assembly boot-validates (baseline)", () => {
72
+ expect(() => validateBoot(baseFeatures())).not.toThrow();
73
+ });
74
+
75
+ test("V2: export hook without delete hook → Art.17 throw", () => {
76
+ const bad = defineFeature("bad-export", (r) => {
77
+ r.requires("user-data-rights");
78
+ r.useExtension(EXT_USER_DATA, "badEntity", { export: async () => null });
79
+ });
80
+ expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/Art\.17/);
81
+ });
82
+
83
+ test("V3: pii entity without any EXT_USER_DATA hook → throws the guard's own message", () => {
84
+ const bad = defineFeature("crm", (r) => {
85
+ r.entity("contact", createEntity({ fields: { email: createTextField({ pii: true }) } }));
86
+ });
87
+ expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/EXT_USER_DATA hook.*Art\.17 gap/);
88
+ });
89
+
90
+ test("V3: pii field on a projection-only entity (no r.entity) is still caught", () => {
91
+ const projectionTable = projectionProbeTable("crm_contact_summary");
92
+ const bad = defineFeature("crm", (r) => {
93
+ const contactCreated = r.defineEvent("contact-created", z.unknown());
94
+ r.projection({
95
+ name: "contact-summary",
96
+ source: "contact",
97
+ table: projectionTable,
98
+ apply: { [contactCreated.name]: async () => {} },
99
+ entity: createEntity({ fields: { email: createTextField({ pii: true }) } }),
100
+ });
101
+ });
102
+ expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/EXT_USER_DATA hook.*Art\.17 gap/);
103
+ });
104
+
105
+ test("V3: projection-only pii entity WITH EXT_USER_DATA hook registered for its entity name → no throw", () => {
106
+ const projectionTable = projectionProbeTable("crm_contact_summary_hooked");
107
+ const hooked = defineFeature("crm-hooked", (r) => {
108
+ r.requires("user-data-rights");
109
+ const contactCreated = r.defineEvent("contact-hooked-created", z.unknown());
110
+ r.projection({
111
+ name: "contact-summary",
112
+ source: "contact",
113
+ table: projectionTable,
114
+ apply: { [contactCreated.name]: async () => {} },
115
+ entity: createEntity({ fields: { email: createTextField({ pii: true }) } }),
116
+ });
117
+ r.useExtension(EXT_USER_DATA, "contact-summary", {
118
+ export: async () => null,
119
+ delete: async () => {},
120
+ });
121
+ });
122
+ expect(() => validateBoot([...baseFeatures(), hooked])).not.toThrow();
123
+ });
124
+
125
+ test("V4: tenantOwned entity without EXT_TENANT_DATA hook, tenant-lifecycle mounted → throws the guard's own message", () => {
126
+ const bad = defineFeature("billing", (r) => {
127
+ r.entity(
128
+ "subscription",
129
+ createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
130
+ );
131
+ });
132
+ expect(() => validateBoot([...minimalTenantLifecycleFeatures(), bad])).toThrow(
133
+ /EXT_TENANT_DATA destroy hook/,
134
+ );
135
+ });
136
+
137
+ test("V4: fires with no user-data-rights* mounted at all — trigger is tenant-lifecycle's own mount", () => {
138
+ const bad = defineFeature("billing", (r) => {
139
+ r.entity(
140
+ "subscription",
141
+ createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
142
+ );
143
+ });
144
+ const features = minimalTenantLifecycleFeatures().concat(bad);
145
+ expect(features.some((f) => f.name.startsWith("user-data-rights"))).toBe(false);
146
+ expect(() => validateBoot(features)).toThrow(/EXT_TENANT_DATA destroy hook/);
147
+ });
148
+
149
+ test("V4: tenant-lifecycle not mounted → tenantOwned entity does not throw", () => {
150
+ const notMounted = defineFeature("billing-standalone", (r) => {
151
+ r.entity(
152
+ "subscription",
153
+ createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
154
+ );
155
+ });
156
+ expect(() => validateBoot([...baseFeatures(), notMounted])).not.toThrow();
157
+ });
158
+
159
+ test("V4: tenantOwned entity WITH EXT_TENANT_DATA hook registered → no throw", () => {
160
+ const hooked = defineFeature("billing-hooked", (r) => {
161
+ r.requires("tenant-lifecycle");
162
+ r.entity(
163
+ "subscription",
164
+ createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
165
+ );
166
+ r.useExtension(EXT_TENANT_DATA, "subscription", { destroy: async () => {} });
167
+ });
168
+ expect(() => validateBoot([...minimalTenantLifecycleFeatures(), hooked])).not.toThrow();
169
+ });
170
+ });
@@ -13,6 +13,7 @@
13
13
  // Alices Forget; Bobs Daten landen NICHT in Alices Export-Bundle.
14
14
 
15
15
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
16
+ import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
16
17
  import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
17
18
  import { defineUnmanagedTable } from "@cosmicdrift/kumiko-framework/db";
18
19
  import {
@@ -36,7 +37,7 @@ import {
36
37
  } from "../../compliance-profiles";
37
38
  import { createDataRetentionFeature, tenantRetentionOverrideEntity } from "../../data-retention";
38
39
  import { createFilesFeature } from "../../files";
39
- import { createSessionsFeature } from "../../sessions";
40
+ import { createSessionsFeature, userSessionEntity } from "../../sessions";
40
41
  import { tenantMembershipsTable } from "../../tenant";
41
42
  import { createUserFeature, USER_STATUS, userEntity, userTable } from "../../user";
42
43
  import { createUserDataRightsDefaultsFeature } from "../../user-data-rights-defaults";
@@ -118,6 +119,7 @@ beforeAll(async () => {
118
119
  createFilesFeature(),
119
120
  createDataRetentionFeature(),
120
121
  createComplianceProfilesFeature(),
122
+ authFoundationFeature,
121
123
  createSessionsFeature(),
122
124
 
123
125
  createUserDataRightsFeature(),
@@ -127,6 +129,7 @@ beforeAll(async () => {
127
129
  });
128
130
 
129
131
  await unsafeCreateEntityTable(stack.db, userEntity);
132
+ await unsafeCreateEntityTable(stack.db, userSessionEntity);
130
133
  await unsafeCreateEntityTable(stack.db, tenantRetentionOverrideEntity);
131
134
  await unsafeCreateEntityTable(stack.db, tenantComplianceProfileEntity);
132
135
  await asRawClient(stack.db).unsafe(`