@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
@@ -0,0 +1,455 @@
1
+ // userData-Hook Integration-Tests für die Bundled-Entity-Hooks (GDPR-Audit
2
+ // 2026-07): user-session, api-token, in-app-message, tenant-invitation,
3
+ // notification-preference, config-value.
4
+ //
5
+ // Zwei Stacks:
6
+ // FULL — alle Source-Features gemountet; testet Export-Inhalt, Forget-
7
+ // Semantik (hard-delete vs. executor-forget vs. anonymize) und
8
+ // Tenant-Isolation.
9
+ // MINIMAL — nur user/files/udr/defaults; testet das Presence-Gating: jeder
10
+ // Hook muss null/no-op liefern statt gegen fehlende Tabellen zu
11
+ // crashen (der Export-Runner hat kein per-Hook try/catch).
12
+
13
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
14
+ import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
15
+ import {
16
+ setupTestStack,
17
+ type TestStack,
18
+ unsafeCreateEntityTable,
19
+ unsafePushTables,
20
+ } from "@cosmicdrift/kumiko-framework/stack";
21
+ import { seedRow } from "@cosmicdrift/kumiko-framework/testing";
22
+ import { authFoundationFeature } from "../../auth-foundation";
23
+ import { createChannelInAppFeature, inAppMessagesTable } from "../../channel-in-app";
24
+ import { createComplianceProfilesFeature } from "../../compliance-profiles";
25
+ import { configValueEntity, createConfigFeature } from "../../config";
26
+ import { createDataRetentionFeature } from "../../data-retention";
27
+ import { createDeliveryFeature, notificationPreferenceEntity } from "../../delivery";
28
+ import { createFilesFeature } from "../../files";
29
+ import { createJobsFeature, jobRunLogsTable, jobRunsTable } from "../../jobs";
30
+ import {
31
+ apiTokenEntity,
32
+ createPersonalAccessTokensFeature,
33
+ PatQueries,
34
+ type PatScopeConfig,
35
+ } from "../../personal-access-tokens";
36
+ import { createSessionsFeature, userSessionEntity } from "../../sessions";
37
+ import { createTenantFeature, tenantInvitationEntity } from "../../tenant";
38
+ import { createUserFeature, USER_STATUS, userEntity, userTable } from "../../user";
39
+ import { createUserDataRightsFeature } from "../../user-data-rights";
40
+ import { createUserDataRightsDefaultsFeature } from "../feature";
41
+ import {
42
+ apiTokenDeleteHook,
43
+ apiTokenExportHook,
44
+ configValueDeleteHook,
45
+ configValueExportHook,
46
+ deliveryAttemptExportHook,
47
+ inAppMessageDeleteHook,
48
+ inAppMessageExportHook,
49
+ jobRunExportHook,
50
+ notificationPreferenceDeleteHook,
51
+ notificationPreferenceExportHook,
52
+ tenantInvitationDeleteHook,
53
+ tenantInvitationExportHook,
54
+ userSessionDeleteHook,
55
+ userSessionExportHook,
56
+ } from "../index";
57
+
58
+ const PAT_SCOPES: PatScopeConfig = {
59
+ tokens: { label: "Tokens", read: [PatQueries.mine] },
60
+ };
61
+
62
+ let full: TestStack;
63
+ let minimal: TestStack;
64
+
65
+ beforeAll(async () => {
66
+ full = await setupTestStack({
67
+ features: [
68
+ createUserFeature(),
69
+ createFilesFeature(),
70
+ createDataRetentionFeature(),
71
+ createComplianceProfilesFeature(),
72
+ createSessionsFeature(),
73
+ createConfigFeature(),
74
+ createTenantFeature(),
75
+ createPersonalAccessTokensFeature({ scopes: PAT_SCOPES }),
76
+ authFoundationFeature,
77
+ createDeliveryFeature(),
78
+ createChannelInAppFeature(),
79
+ createJobsFeature(),
80
+ createUserDataRightsFeature(),
81
+ createUserDataRightsDefaultsFeature(),
82
+ ],
83
+ });
84
+ await unsafeCreateEntityTable(full.db, userEntity);
85
+ await unsafeCreateEntityTable(full.db, userSessionEntity);
86
+ await unsafeCreateEntityTable(full.db, apiTokenEntity);
87
+ await unsafeCreateEntityTable(full.db, tenantInvitationEntity);
88
+ await unsafeCreateEntityTable(full.db, notificationPreferenceEntity);
89
+ await unsafeCreateEntityTable(full.db, configValueEntity);
90
+ await unsafePushTables(full.db, { inAppMessagesTable, jobRunsTable, jobRunLogsTable });
91
+
92
+ minimal = await setupTestStack({
93
+ features: [
94
+ createUserFeature(),
95
+ createFilesFeature(),
96
+ createDataRetentionFeature(),
97
+ createComplianceProfilesFeature(),
98
+ authFoundationFeature,
99
+ createSessionsFeature(),
100
+ createUserDataRightsFeature(),
101
+ createUserDataRightsDefaultsFeature(),
102
+ ],
103
+ });
104
+ });
105
+
106
+ afterAll(async () => {
107
+ await full.cleanup();
108
+ await minimal.cleanup();
109
+ });
110
+
111
+ const TENANT_A = "00000000-0000-4000-8000-00000000000a";
112
+ const TENANT_B = "00000000-0000-4000-8000-00000000000b";
113
+
114
+ function uuid(suffix: number): string {
115
+ return `bbbbbbbb-bbbb-4bbb-8bbb-${suffix.toString(16).padStart(12, "0")}`;
116
+ }
117
+
118
+ function ctx(userId: string, overrides: Record<string, unknown> = {}) {
119
+ return {
120
+ db: full.db,
121
+ registry: full.registry,
122
+ tenantId: TENANT_A,
123
+ userId,
124
+ ...overrides,
125
+ };
126
+ }
127
+
128
+ async function rawSelect(sql: string, params: unknown[]) {
129
+ const result = await asRawClient(full.db).unsafe(sql, params);
130
+ // biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
131
+ return ((result as any).rows ?? result) as Array<Record<string, unknown>>;
132
+ }
133
+
134
+ async function seedUser(id: string): Promise<void> {
135
+ const SYSTEM_TENANT = "00000000-0000-4000-8000-000000000001";
136
+ await seedRow(full.db, userTable, {
137
+ id,
138
+ tenantId: SYSTEM_TENANT,
139
+ email: `user-${id}@example.com`,
140
+ passwordHash: "hashed-password",
141
+ displayName: `User ${id}`,
142
+ locale: "de",
143
+ emailVerified: true,
144
+ roles: '["Member"]',
145
+ status: USER_STATUS.Active,
146
+ });
147
+ }
148
+
149
+ describe("user-session userData-hooks", () => {
150
+ async function seedSession(id: string, userId: string, tenantId: string): Promise<void> {
151
+ await asRawClient(full.db).unsafe(
152
+ `INSERT INTO store_user_sessions (id, user_id, tenant_id, created_at, expires_at, ip, user_agent)
153
+ VALUES ($1, $2, $3, now(), now() + interval '1 day', '203.0.113.7', 'TestAgent/1.0')`,
154
+ [id, userId, tenantId],
155
+ );
156
+ }
157
+
158
+ test("export liefert ip/userAgent, aber keine Session-Id (jti)", async () => {
159
+ await seedSession(uuid(1), "sess-user-1", TENANT_A);
160
+
161
+ const result = await userSessionExportHook(ctx("sess-user-1"));
162
+ expect(result?.entity).toBe("user-session");
163
+ expect(result?.rows).toHaveLength(1);
164
+ expect(result?.rows[0]?.["ip"]).toBe("203.0.113.7");
165
+ expect(result?.rows[0]?.["userAgent"]).toBe("TestAgent/1.0");
166
+ expect(result?.rows[0]?.["id"]).toBeUndefined();
167
+ });
168
+
169
+ test("delete entfernt Sessions des Users, Cross-Tenant bleibt", async () => {
170
+ await seedSession(uuid(2), "sess-user-2", TENANT_A);
171
+ await seedSession(uuid(3), "sess-user-2", TENANT_B);
172
+
173
+ await userSessionDeleteHook(ctx("sess-user-2"), "delete");
174
+
175
+ const a = await rawSelect(
176
+ "SELECT * FROM store_user_sessions WHERE user_id = $1 AND tenant_id = $2",
177
+ ["sess-user-2", TENANT_A],
178
+ );
179
+ const b = await rawSelect(
180
+ "SELECT * FROM store_user_sessions WHERE user_id = $1 AND tenant_id = $2",
181
+ ["sess-user-2", TENANT_B],
182
+ );
183
+ expect(a).toHaveLength(0);
184
+ expect(b).toHaveLength(1);
185
+ });
186
+ });
187
+
188
+ describe("api-token userData-hooks", () => {
189
+ async function seedToken(id: string, userId: string): Promise<void> {
190
+ await asRawClient(full.db).unsafe(
191
+ `INSERT INTO store_api_tokens (id, user_id, tenant_id, name, token_hash, prefix, scopes, created_at)
192
+ VALUES ($1, $2, $3, 'My MacBook', $4, 'kum_ab12', '["read"]', now())`,
193
+ [id, userId, TENANT_A, `hash-${id}`],
194
+ );
195
+ }
196
+
197
+ test("export liefert Token-Metadata ohne tokenHash", async () => {
198
+ await seedToken(uuid(10), "pat-user-1");
199
+
200
+ const result = await apiTokenExportHook(ctx("pat-user-1"));
201
+ expect(result?.entity).toBe("api-token");
202
+ expect(result?.rows).toHaveLength(1);
203
+ expect(result?.rows[0]?.["name"]).toBe("My MacBook");
204
+ expect(result?.rows[0]?.["prefix"]).toBe("kum_ab12");
205
+ expect(result?.rows[0]?.["tokenHash"]).toBeUndefined();
206
+ });
207
+
208
+ test("delete entfernt Tokens (= Revoke)", async () => {
209
+ await seedToken(uuid(11), "pat-user-2");
210
+
211
+ await apiTokenDeleteHook(ctx("pat-user-2"), "delete");
212
+
213
+ const rows = await rawSelect("SELECT * FROM store_api_tokens WHERE user_id = $1", [
214
+ "pat-user-2",
215
+ ]);
216
+ expect(rows).toHaveLength(0);
217
+ });
218
+ });
219
+
220
+ describe("in-app-message userData-hooks", () => {
221
+ async function seedMessage(userId: string, title: string): Promise<void> {
222
+ await asRawClient(full.db).unsafe(
223
+ `INSERT INTO in_app_messages (tenant_id, user_id, notification_type, title, body)
224
+ VALUES ($1, $2, 'test:notify', $3, 'Hallo Marc, dein Export ist da')`,
225
+ [TENANT_A, userId, title],
226
+ );
227
+ }
228
+
229
+ test("export liefert Messages, delete entfernt sie", async () => {
230
+ await seedMessage("inapp-user-1", "Export fertig");
231
+
232
+ const result = await inAppMessageExportHook(ctx("inapp-user-1"));
233
+ expect(result?.entity).toBe("in-app-message");
234
+ expect(result?.rows[0]?.["title"]).toBe("Export fertig");
235
+
236
+ await inAppMessageDeleteHook(ctx("inapp-user-1"), "delete");
237
+ const rows = await rawSelect("SELECT * FROM in_app_messages WHERE user_id = $1", [
238
+ "inapp-user-1",
239
+ ]);
240
+ expect(rows).toHaveLength(0);
241
+ });
242
+ });
243
+
244
+ describe("tenant-invitation userData-hooks", () => {
245
+ async function seedInvitation(
246
+ id: string,
247
+ email: string,
248
+ invitedBy: string,
249
+ tenantId = TENANT_A,
250
+ ): Promise<void> {
251
+ await asRawClient(full.db).unsafe(
252
+ `INSERT INTO read_tenant_invitations (id, tenant_id, email, role, status, invited_by, expires_at)
253
+ VALUES ($1, $2, $3, 'Member', 'pending', $4, now() + interval '7 days')`,
254
+ [id, tenantId, email, invitedBy],
255
+ );
256
+ }
257
+
258
+ test("export matcht Invitations über die User-Email", async () => {
259
+ await seedUser(uuid(20));
260
+ await seedInvitation(uuid(21), `user-${uuid(20)}@example.com`, "admin-1");
261
+
262
+ const result = await tenantInvitationExportHook(ctx(uuid(20)));
263
+ expect(result?.entity).toBe("tenant-invitation");
264
+ expect(result?.rows).toHaveLength(1);
265
+ expect(result?.rows[0]?.["role"]).toBe("Member");
266
+ });
267
+
268
+ test("forget delete purged die Invitee-Row (rebuild-sicher via Executor)", async () => {
269
+ await seedUser(uuid(22));
270
+ await seedInvitation(uuid(23), `user-${uuid(22)}@example.com`, "admin-1");
271
+
272
+ await tenantInvitationDeleteHook(ctx(uuid(22)), "delete");
273
+
274
+ const rows = await rawSelect("SELECT * FROM read_tenant_invitations WHERE id = $1", [uuid(23)]);
275
+ expect(rows).toHaveLength(0);
276
+ });
277
+
278
+ test("anonymize pseudonymisiert die Invitee-Email (unique-index-sicher)", async () => {
279
+ await seedUser(uuid(24));
280
+ await seedInvitation(uuid(25), `user-${uuid(24)}@example.com`, "admin-1");
281
+
282
+ await tenantInvitationDeleteHook(ctx(uuid(24)), "anonymize");
283
+
284
+ const rows = await rawSelect("SELECT email FROM read_tenant_invitations WHERE id = $1", [
285
+ uuid(25),
286
+ ]);
287
+ expect(rows).toHaveLength(1);
288
+ expect(String(rows[0]?.["email"])).toBe(`forgotten-${uuid(25)}@anonymized.invalid`);
289
+ });
290
+
291
+ test("userEmailBeforeDelete hat Vorrang (User-Row schon anonymisiert)", async () => {
292
+ await seedInvitation(uuid(26), "vanished@example.com", "admin-1");
293
+
294
+ await tenantInvitationDeleteHook(
295
+ ctx("ghost-user-forgotten", { userEmailBeforeDelete: "Vanished@Example.com" }),
296
+ "delete",
297
+ );
298
+
299
+ const rows = await rawSelect("SELECT * FROM read_tenant_invitations WHERE id = $1", [uuid(26)]);
300
+ expect(rows).toHaveLength(0);
301
+ });
302
+
303
+ test("Inviter-Forget severs invitedBy, Row bleibt (gehört dem Invitee)", async () => {
304
+ // userId als echte UUID — der Email-Lookup geht gegen read_users.id (uuid).
305
+ const inviter = uuid(28);
306
+ await seedInvitation(uuid(27), "someone-else@example.com", inviter);
307
+
308
+ await tenantInvitationDeleteHook(ctx(inviter), "delete");
309
+
310
+ const rows = await rawSelect(
311
+ "SELECT email, invited_by FROM read_tenant_invitations WHERE id = $1",
312
+ [uuid(27)],
313
+ );
314
+ expect(rows).toHaveLength(1);
315
+ expect(rows[0]?.["email"]).toBe("someone-else@example.com");
316
+ expect(rows[0]?.["invited_by"]).toBe("anonymized");
317
+ });
318
+ });
319
+
320
+ describe("notification-preference userData-hooks", () => {
321
+ async function seedPreference(id: string, userId: string): Promise<void> {
322
+ await asRawClient(full.db).unsafe(
323
+ `INSERT INTO read_notification_preferences (id, tenant_id, user_id, notification_type, channel, enabled)
324
+ VALUES ($1, $2, $3, 'test:notify', 'email', false)`,
325
+ [id, TENANT_A, userId],
326
+ );
327
+ }
328
+
329
+ test("export liefert Prefs, forget purged sie via Executor", async () => {
330
+ await seedPreference(uuid(30), "pref-user-1");
331
+
332
+ const result = await notificationPreferenceExportHook(ctx("pref-user-1"));
333
+ expect(result?.entity).toBe("notification-preference");
334
+ expect(result?.rows[0]?.["channel"]).toBe("email");
335
+ expect(result?.rows[0]?.["enabled"]).toBe(false);
336
+
337
+ await notificationPreferenceDeleteHook(ctx("pref-user-1"), "delete");
338
+ const rows = await rawSelect("SELECT * FROM read_notification_preferences WHERE user_id = $1", [
339
+ "pref-user-1",
340
+ ]);
341
+ expect(rows).toHaveLength(0);
342
+ });
343
+ });
344
+
345
+ describe("config-value userData-hooks", () => {
346
+ async function seedConfig(id: string, key: string, userId: string | null): Promise<void> {
347
+ await asRawClient(full.db).unsafe(
348
+ `INSERT INTO read_config_values (id, tenant_id, key, value, user_id)
349
+ VALUES ($1, $2, $3, '"dark"', $4)`,
350
+ [id, TENANT_A, key, userId],
351
+ );
352
+ }
353
+
354
+ test("export + forget treffen NUR user-scoped Rows, tenant-scope bleibt", async () => {
355
+ await seedConfig(uuid(40), "ui.theme", "cfg-user-1");
356
+ await seedConfig(uuid(41), "tenant.brand", null);
357
+
358
+ const result = await configValueExportHook(ctx("cfg-user-1"));
359
+ expect(result?.rows).toHaveLength(1);
360
+ expect(result?.rows[0]?.["key"]).toBe("ui.theme");
361
+
362
+ await configValueDeleteHook(ctx("cfg-user-1"), "delete");
363
+ const userRows = await rawSelect("SELECT * FROM read_config_values WHERE user_id = $1", [
364
+ "cfg-user-1",
365
+ ]);
366
+ const tenantRows = await rawSelect("SELECT * FROM read_config_values WHERE id = $1", [
367
+ uuid(41),
368
+ ]);
369
+ expect(userRows).toHaveLength(0);
370
+ expect(tenantRows).toHaveLength(1);
371
+ });
372
+ });
373
+
374
+ describe("delivery-attempt userData-hooks (#799)", () => {
375
+ async function seedAttempt(id: string, recipientId: string, tenantId: string): Promise<void> {
376
+ await asRawClient(full.db).unsafe(
377
+ `INSERT INTO store_delivery_attempts
378
+ (id, tenant_id, notification_type, channel, recipient_id, recipient_address, status, priority)
379
+ VALUES ($1, $2, 'app:notify:ping', 'email', $3, $4, 'sent', 'normal')`,
380
+ [id, tenantId, recipientId, `user-${recipientId}@example.com`],
381
+ );
382
+ }
383
+
384
+ test("export returns only the user's attempts in the export tenant", async () => {
385
+ await seedAttempt(uuid(60), "da-user-1", TENANT_A);
386
+ await seedAttempt(uuid(61), "da-user-1", TENANT_B);
387
+ await seedAttempt(uuid(62), "da-user-2", TENANT_A);
388
+
389
+ const result = await deliveryAttemptExportHook(ctx("da-user-1"));
390
+ expect(result?.entity).toBe("delivery-attempt");
391
+ expect(result?.rows).toHaveLength(1);
392
+ expect(result?.rows[0]?.["recipientAddress"]).toBe("user-da-user-1@example.com");
393
+ expect(result?.rows[0]?.["channel"]).toBe("email");
394
+ });
395
+
396
+ test("export returns null for a user without attempts", async () => {
397
+ expect(await deliveryAttemptExportHook(ctx("da-nobody"))).toBeNull();
398
+ });
399
+ });
400
+
401
+ describe("job-run userData-hooks (#799)", () => {
402
+ async function seedRun(id: string, triggeredById: string | null): Promise<void> {
403
+ await asRawClient(full.db).unsafe(
404
+ `INSERT INTO read_job_runs
405
+ (id, tenant_id, job_name, bull_job_id, status, payload, attempt, started_at, triggered_by_id)
406
+ VALUES ($1::uuid, $2, 'app:job:export', $3, 'completed', '{"scope":"mine"}', 1, now(), $4)`,
407
+ [id, TENANT_A, `bull-${id}`, triggeredById],
408
+ );
409
+ }
410
+
411
+ test("export filters by triggeredById only (system-tenant rows)", async () => {
412
+ await seedRun(uuid(70), "jr-user-1");
413
+ await seedRun(uuid(71), "jr-user-2");
414
+ await seedRun(uuid(72), null);
415
+
416
+ const result = await jobRunExportHook(ctx("jr-user-1"));
417
+ expect(result?.entity).toBe("job-run");
418
+ expect(result?.rows).toHaveLength(1);
419
+ expect(result?.rows[0]?.["jobName"]).toBe("app:job:export");
420
+ expect(result?.rows[0]?.["payload"]).toBe('{"scope":"mine"}');
421
+ });
422
+
423
+ test("export returns null for a user without runs", async () => {
424
+ expect(await jobRunExportHook(ctx("jr-nobody"))).toBeNull();
425
+ });
426
+ });
427
+
428
+ describe("Presence-Gating: Source-Feature nicht gemountet → null/no-op statt Crash", () => {
429
+ test("alle gegateten Hooks no-open auf dem Minimal-Stack", async () => {
430
+ const minCtx = {
431
+ db: minimal.db,
432
+ registry: minimal.registry,
433
+ tenantId: TENANT_A,
434
+ userId: "any-user",
435
+ };
436
+
437
+ // sessions ist im Minimal-Stack GEMOUNTET aber die Tabelle existiert
438
+ // nicht — der Gate-Check greift auf Feature-Ebene, deshalb hier nur die
439
+ // fünf wirklich ungemounteten Features. (user-session wird im FULL-Stack
440
+ // getestet.)
441
+ expect(await apiTokenExportHook(minCtx)).toBeNull();
442
+ expect(await inAppMessageExportHook(minCtx)).toBeNull();
443
+ expect(await tenantInvitationExportHook(minCtx)).toBeNull();
444
+ expect(await notificationPreferenceExportHook(minCtx)).toBeNull();
445
+ expect(await configValueExportHook(minCtx)).toBeNull();
446
+ expect(await deliveryAttemptExportHook(minCtx)).toBeNull();
447
+ expect(await jobRunExportHook(minCtx)).toBeNull();
448
+
449
+ await expect(apiTokenDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
450
+ await expect(inAppMessageDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
451
+ await expect(tenantInvitationDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
452
+ await expect(notificationPreferenceDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
453
+ await expect(configValueDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
454
+ });
455
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { UserDataHookCtx } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { featureMounted } from "../hooks/feature-mounted";
4
+
5
+ function ctxWithFeatures(mounted: readonly string[]): UserDataHookCtx {
6
+ return {
7
+ registry: {
8
+ getFeature: (name: string) => (mounted.includes(name) ? { name } : undefined),
9
+ // @cast-boundary test fixture — featureMounted only reads ctx.registry.getFeature
10
+ } as unknown as UserDataHookCtx["registry"],
11
+ } as unknown as UserDataHookCtx;
12
+ }
13
+
14
+ describe("featureMounted", () => {
15
+ test("returns true when the feature is registered", () => {
16
+ const ctx = ctxWithFeatures(["sessions"]);
17
+ expect(featureMounted(ctx, "sessions")).toBe(true);
18
+ });
19
+
20
+ test("returns false when the feature is not registered", () => {
21
+ const ctx = ctxWithFeatures(["sessions"]);
22
+ expect(featureMounted(ctx, "channel-email")).toBe(false);
23
+ });
24
+ });
@@ -11,6 +11,7 @@
11
11
  // fileRef-Forget beruehrt Tenant B's Files nicht)
12
12
 
13
13
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
14
+ import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
14
15
  import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
15
16
  import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
16
17
  import {
@@ -43,6 +44,7 @@ const features = [
43
44
  createFilesFeature(),
44
45
  createDataRetentionFeature(),
45
46
  createComplianceProfilesFeature(),
47
+ authFoundationFeature,
46
48
  createSessionsFeature(),
47
49
  createUserDataRightsFeature(),
48
50
  createUserDataRightsDefaultsFeature(),
@@ -162,6 +164,7 @@ describe("S2.H1 :: userExportHook", () => {
162
164
 
163
165
  const result = await userExportHook({
164
166
  db: stack.db,
167
+ registry: stack.registry,
165
168
  tenantId: TENANT_A,
166
169
  userId: uuid(1001),
167
170
  });
@@ -182,6 +185,7 @@ describe("S2.H1 :: userExportHook", () => {
182
185
  test("returns null wenn User nicht existiert", async () => {
183
186
  const result = await userExportHook({
184
187
  db: stack.db,
188
+ registry: stack.registry,
185
189
  tenantId: TENANT_A,
186
190
  userId: uuid(1002),
187
191
  });
@@ -193,7 +197,10 @@ describe("S2.H1 :: userDeleteHook", () => {
193
197
  test('strategy="delete" → softDelete + email/displayName anonymisiert + status=deleted', async () => {
194
198
  await seedUser(uuid(1003));
195
199
 
196
- await userDeleteHook({ db: stack.db, tenantId: TENANT_A, userId: uuid(1003) }, "delete");
200
+ await userDeleteHook(
201
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1003) },
202
+ "delete",
203
+ );
197
204
 
198
205
  const row = await fetchUser(uuid(1003));
199
206
  expect(row).not.toBeNull();
@@ -209,7 +216,10 @@ describe("S2.H1 :: userDeleteHook", () => {
209
216
  test('strategy="anonymize" → email/displayName anonymisiert aber status bleibt active', async () => {
210
217
  await seedUser(uuid(1004));
211
218
 
212
- await userDeleteHook({ db: stack.db, tenantId: TENANT_A, userId: uuid(1004) }, "anonymize");
219
+ await userDeleteHook(
220
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1004) },
221
+ "anonymize",
222
+ );
213
223
 
214
224
  const row = await fetchUser(uuid(1004));
215
225
  if (!row) throw new Error("row should exist");
@@ -222,13 +232,19 @@ describe("S2.H1 :: userDeleteHook", () => {
222
232
  test("idempotent: zweiter delete-Call crasht nicht UND State bleibt korrekt deleted", async () => {
223
233
  await seedUser(uuid(1005));
224
234
 
225
- await userDeleteHook({ db: stack.db, tenantId: TENANT_A, userId: uuid(1005) }, "delete");
235
+ await userDeleteHook(
236
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1005) },
237
+ "delete",
238
+ );
226
239
  const afterFirst = await fetchUser(uuid(1005));
227
240
  if (!afterFirst) throw new Error("user should exist after first delete");
228
241
 
229
242
  // Zweiter Call: kein Crash + State unverändert
230
243
  await expect(
231
- userDeleteHook({ db: stack.db, tenantId: TENANT_A, userId: uuid(1005) }, "delete"),
244
+ userDeleteHook(
245
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1005) },
246
+ "delete",
247
+ ),
232
248
  ).resolves.toBeUndefined();
233
249
 
234
250
  // State-Verifikation (S2.H1+H2-Audit N3): Row weiterhin deleted,
@@ -249,6 +265,7 @@ describe("S2.H2 :: fileRefExportHook", () => {
249
265
 
250
266
  const result = await fileRefExportHook({
251
267
  db: stack.db,
268
+ registry: stack.registry,
252
269
  tenantId: TENANT_A,
253
270
  userId: "user-files-1",
254
271
  });
@@ -263,6 +280,7 @@ describe("S2.H2 :: fileRefExportHook", () => {
263
280
  test("returns null wenn User keine Files hat", async () => {
264
281
  const result = await fileRefExportHook({
265
282
  db: stack.db,
283
+ registry: stack.registry,
266
284
  tenantId: TENANT_A,
267
285
  userId: "ghost-user-no-files",
268
286
  });
@@ -276,7 +294,7 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
276
294
  await seedFileRef(uuid(202), TENANT_A, "user-delete-files", "f2.pdf");
277
295
 
278
296
  await fileRefDeleteHook(
279
- { db: stack.db, tenantId: TENANT_A, userId: "user-delete-files" },
297
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-delete-files" },
280
298
  "delete",
281
299
  );
282
300
 
@@ -288,7 +306,7 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
288
306
  await seedFileRef(uuid(203), TENANT_A, "user-anon-files", "shared.pdf");
289
307
 
290
308
  await fileRefDeleteHook(
291
- { db: stack.db, tenantId: TENANT_A, userId: "user-anon-files" },
309
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-anon-files" },
292
310
  "anonymize",
293
311
  );
294
312
 
@@ -305,7 +323,10 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
305
323
  await seedFileRef(uuid(302), TENANT_B, "shared-user", "tenantB.pdf");
306
324
 
307
325
  // Tenant A loescht alle Files von "shared-user"
308
- await fileRefDeleteHook({ db: stack.db, tenantId: TENANT_A, userId: "shared-user" }, "delete");
326
+ await fileRefDeleteHook(
327
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "shared-user" },
328
+ "delete",
329
+ );
309
330
 
310
331
  const aRemaining = await fetchFileRefs(TENANT_A, "shared-user");
311
332
  const bRemaining = await fetchFileRefs(TENANT_B, "shared-user");
@@ -319,7 +340,7 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
319
340
  await seedFileRef(uuid(401), TENANT_A, "user-idem-files", "f.pdf");
320
341
 
321
342
  await fileRefDeleteHook(
322
- { db: stack.db, tenantId: TENANT_A, userId: "user-idem-files" },
343
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-idem-files" },
323
344
  "delete",
324
345
  );
325
346
  const afterFirst = await fetchFileRefs(TENANT_A, "user-idem-files");
@@ -327,7 +348,10 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
327
348
 
328
349
  // Zweiter Call: kein Crash + State weiter 0 Files
329
350
  await expect(
330
- fileRefDeleteHook({ db: stack.db, tenantId: TENANT_A, userId: "user-idem-files" }, "delete"),
351
+ fileRefDeleteHook(
352
+ { db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-idem-files" },
353
+ "delete",
354
+ ),
331
355
  ).resolves.toBeUndefined();
332
356
  const afterSecond = await fetchFileRefs(TENANT_A, "user-idem-files");
333
357
  expect(afterSecond).toHaveLength(0);