@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
@@ -11,35 +11,47 @@ export type {
11
11
  CurrentUserProfile,
12
12
  LoginFailure,
13
13
  LoginRequest,
14
+ LoginResponse,
14
15
  ResetPasswordFailure,
15
16
  SignupConfirmSuccess,
16
17
  TenantSummary,
17
18
  VerifyEmailFailure,
18
19
  } from "./auth-client";
19
20
  export {
21
+ confirmAccountUnlock,
20
22
  confirmSignup,
23
+ requestAccountUnlock,
21
24
  requestEmailVerification,
22
25
  requestPasswordReset,
23
26
  requestSignup,
24
27
  resetPassword,
25
28
  verifyEmail,
26
29
  } from "./auth-client";
27
- export type { AuthShellRenderer } from "./auth-form-primitives";
28
- export { AuthShellProvider, useAuthShell } from "./auth-form-primitives";
29
- export { makeAuthGate, makeSessionAuthGate } from "./auth-gate";
30
+ export type { AuthCardProps, AuthShellRenderer } from "./auth-form-primitives";
31
+ export { AuthCard, AuthShellProvider, useAuthShell } from "./auth-form-primitives";
32
+ export type {
33
+ LoginRouteOptions,
34
+ MfaSetupComponentProps,
35
+ MfaVerifyComponentProps,
36
+ } from "./auth-gate";
37
+ export { createLoginRoute, makeAuthGate, makeSessionAuthGate } from "./auth-gate";
30
38
  export type {
31
39
  EmailPasswordClientFeature,
32
40
  EmailPasswordClientOptions,
33
41
  } from "./client-plugin";
34
42
  export { emailPasswordClient } from "./client-plugin";
43
+ export type { ConfirmAccountUnlockScreenProps } from "./confirm-account-unlock-screen";
44
+ export { ConfirmAccountUnlockScreen } from "./confirm-account-unlock-screen";
35
45
  export type { DefaultTopbarActionsProps } from "./default-topbar-actions";
36
46
  export { DefaultTopbarActions } from "./default-topbar-actions";
37
47
  export type { ForgotPasswordScreenProps } from "./forgot-password-screen";
38
48
  export { ForgotPasswordScreen } from "./forgot-password-screen";
39
49
  export type { InviteAcceptScreenProps } from "./invite-accept-screen";
40
50
  export { InviteAcceptScreen } from "./invite-accept-screen";
41
- export type { LoginScreenProps } from "./login-screen";
51
+ export type { AuthLegalLink, LoginScreenProps } from "./login-screen";
42
52
  export { LoginScreen } from "./login-screen";
53
+ export type { RequestAccountUnlockScreenProps } from "./request-account-unlock-screen";
54
+ export { RequestAccountUnlockScreen } from "./request-account-unlock-screen";
43
55
  export type { ResetPasswordScreenProps } from "./reset-password-screen";
44
56
  export { ResetPasswordScreen } from "./reset-password-screen";
45
57
  export type { SessionApi, SessionState, SessionStatus } from "./session";
@@ -19,7 +19,7 @@
19
19
  import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
20
20
  import { type FormEvent, type ReactNode, useContext, useState } from "react";
21
21
  import { csrfHeader } from "./auth-client";
22
- import { AuthCard, authMutedLinkClass, parseUrlToken } from "./auth-form-primitives";
22
+ import { AuthCard, useUrlToken } from "./auth-form-primitives";
23
23
  import { SessionContext, UNAUTHENTICATED } from "./session";
24
24
 
25
25
  export type InviteAcceptScreenProps = {
@@ -41,13 +41,13 @@ export function InviteAcceptScreen({
41
41
  loginHref = "/login",
42
42
  }: InviteAcceptScreenProps): ReactNode {
43
43
  const t = useTranslation();
44
- const { Form, Field, Input, Button, Banner } = usePrimitives();
44
+ const { Form, Field, Input, Button, Banner, Link } = usePrimitives();
45
45
  // NOT useSession(): InviteAcceptScreen is a public route (anon invite-links
46
46
  // are the documented use case) — useSession() throws without a
47
47
  // <SessionProvider> ancestor. Read the context directly and fall back to
48
48
  // the anonymous state when no provider wraps this screen (632/1).
49
49
  const session = useContext(SessionContext) ?? UNAUTHENTICATED;
50
- const [token] = useState(() => tokenProp ?? parseUrlToken());
50
+ const token = useUrlToken(tokenProp);
51
51
  const [mode, setMode] = useState<Mode>(() =>
52
52
  session.status === "authenticated" ? "loggedin" : "anon-existing",
53
53
  );
@@ -142,9 +142,9 @@ export function InviteAcceptScreen({
142
142
  <AuthCard title={effectiveTitle}>
143
143
  <div className="p-6 pt-0 flex flex-col gap-4">
144
144
  <p className="text-sm text-muted-foreground">{t("auth.inviteAccept.missingToken")}</p>
145
- <a href={loginHref} className={authMutedLinkClass}>
145
+ <Link href={loginHref} variant="muted">
146
146
  {t("auth.inviteAccept.goToLogin")}
147
- </a>
147
+ </Link>
148
148
  </div>
149
149
  </AuthCard>
150
150
  );
@@ -11,8 +11,12 @@
11
11
 
12
12
  import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
13
13
  import { type FormEvent, type ReactNode, useState } from "react";
14
- import { type LoginFailure, requestEmailVerification } from "./auth-client";
15
- import { AuthCard, authMutedLinkClass } from "./auth-form-primitives";
14
+ import {
15
+ type KnownLoginFailureReason,
16
+ type LoginFailure,
17
+ requestEmailVerification,
18
+ } from "./auth-client";
19
+ import { AuthCard } from "./auth-form-primitives";
16
20
  import { useSession } from "./session";
17
21
 
18
22
  // Resend-Status für den "Bestätigungs-Mail erneut senden"-Flow, der bei
@@ -40,6 +44,13 @@ export type LoginScreenProps = {
40
44
  * Link. Apps die den Reset-Flow NICHT anbieten (z.B. nur Magic-Link),
41
45
  * setzen das einfach nicht — Login bleibt minimalistisch. */
42
46
  readonly forgotPasswordHref?: string;
47
+ /** Optional href to RequestAccountUnlockScreen (#1266). When set, an
48
+ * account_locked login error renders an "Unlock account?" link inline —
49
+ * the account-lockout escape hatch, since forgotPasswordHref does NOT
50
+ * help here: the lockout gate blocks login before password verify even
51
+ * runs, so it also blocks the post-reset login. Apps without
52
+ * accountUnlock configured just leave this unset. */
53
+ readonly unlockAccountHref?: string;
43
54
  /** Optional href zum SignupScreen. Wenn gesetzt rendert die LoginScreen
44
55
  * einen "Account erstellen"-Link. Apps die kein Self-Signup wollen
45
56
  * (closed-invite-only) setzen das einfach nicht. */
@@ -50,6 +61,20 @@ export type LoginScreenProps = {
50
61
  * Labels kommen vom Caller (typisch schon übersetzt bzw. Eigennamen
51
62
  * wie "Impressum"). */
52
63
  readonly legalLinks?: readonly AuthLegalLink[];
64
+ /** Called when the server responds with an MFA challenge instead of a
65
+ * session. Apps typically swap this screen out for auth-mfa's
66
+ * MfaVerifyScreen(challengeToken). Without a handler, MFA-enrolled
67
+ * users see a generic "not supported" error — better than a silent
68
+ * hang, but apps mounting auth-mfa must wire this. */
69
+ readonly onMfaChallenge?: (challengeToken: string) => void;
70
+ /** Called when the tenant's enforcement policy requires MFA but this
71
+ * user has no factor enrolled yet. Carries the preauthSetupToken the
72
+ * login response issued plus the typed email (no session to derive it
73
+ * from) — apps typically swap this screen out for auth-mfa's
74
+ * MfaSetupPreauthScreen(preauthSetupToken, accountLabel). Without a
75
+ * handler, the user sees a "setup required, contact your administrator"
76
+ * error. */
77
+ readonly onMfaSetupRequired?: (preauthSetupToken: string, accountLabel: string) => void;
53
78
  };
54
79
 
55
80
  // Map vom Reason-Code des Login-Handlers auf einen i18n-Key plus
@@ -78,6 +103,10 @@ function reasonToKey(failure: LoginFailure): {
78
103
  return { key: "auth.errors.rateLimited" };
79
104
  case "invalid_body":
80
105
  return { key: "auth.errors.invalidBody" };
106
+ case "mfa_not_supported":
107
+ return { key: "auth.errors.mfaNotSupported" };
108
+ case "mfa_setup_required":
109
+ return { key: "auth.errors.mfaSetupRequired" };
81
110
  default:
82
111
  return { key: "auth.errors.loginFailed" };
83
112
  }
@@ -88,11 +117,14 @@ export function LoginScreen({
88
117
  subtitle,
89
118
  submitLabel,
90
119
  forgotPasswordHref,
120
+ unlockAccountHref,
91
121
  signupHref,
92
122
  legalLinks,
123
+ onMfaChallenge,
124
+ onMfaSetupRequired,
93
125
  }: LoginScreenProps): ReactNode {
94
126
  const t = useTranslation();
95
- const { Form, Field, Input, Button, Banner } = usePrimitives();
127
+ const { Form, Field, Input, Button, Banner, Link } = usePrimitives();
96
128
  const session = useSession();
97
129
  const [email, setEmail] = useState("");
98
130
  const [password, setPassword] = useState("");
@@ -110,10 +142,27 @@ export function LoginScreen({
110
142
  setResendStatus({ kind: "idle" });
111
143
  const res = await session.login({ email, password });
112
144
  setSubmitting(false);
113
- if (!res.ok) {
114
- setError(res.error);
145
+ if (res.kind === "success") return;
146
+ if (res.kind === "mfa-challenge") {
147
+ if (onMfaChallenge) {
148
+ onMfaChallenge(res.challengeToken);
149
+ return;
150
+ }
151
+ setError({ reason: "mfa_not_supported" satisfies KnownLoginFailureReason });
115
152
  setFailedLoginEmail(email);
153
+ return;
116
154
  }
155
+ if (res.kind === "mfa-setup-required") {
156
+ if (onMfaSetupRequired) {
157
+ onMfaSetupRequired(res.preauthSetupToken, email);
158
+ return;
159
+ }
160
+ setError({ reason: "mfa_setup_required" satisfies KnownLoginFailureReason });
161
+ setFailedLoginEmail(email);
162
+ return;
163
+ }
164
+ setError(res.error);
165
+ setFailedLoginEmail(email);
117
166
  };
118
167
 
119
168
  const onSubmit = (e?: FormEvent): void => {
@@ -186,18 +235,25 @@ export function LoginScreen({
186
235
  {error.reason === "email_not_verified" &&
187
236
  email.trim().length > 0 &&
188
237
  email === failedLoginEmail && (
189
- // kumiko-lint-ignore primitives-discipline Inline-Link im Banner (UX-Choice); Button-Primitive hat keinen link-Variant
190
- <button
191
- type="button"
192
- onClick={() => void onResend()}
193
- disabled={resendStatus.kind === "sending"}
194
- className={`${authMutedLinkClass} self-start text-left disabled:opacity-50`}
195
- >
196
- {resendStatus.kind === "sending"
197
- ? t("auth.login.submitting")
198
- : t("auth.login.resendVerification")}
199
- </button>
238
+ <span className="self-start">
239
+ <Button
240
+ variant="link"
241
+ onClick={() => void onResend()}
242
+ disabled={resendStatus.kind === "sending"}
243
+ >
244
+ {resendStatus.kind === "sending"
245
+ ? t("auth.login.submitting")
246
+ : t("auth.login.resendVerification")}
247
+ </Button>
248
+ </span>
200
249
  )}
250
+ {error.reason === "account_locked" && unlockAccountHref !== undefined && (
251
+ <span className="self-start">
252
+ <Link href={unlockAccountHref} variant="default">
253
+ {t("auth.login.unlockAccount")}
254
+ </Link>
255
+ </span>
256
+ )}
201
257
  {resendStatus.kind === "rateLimited" && (
202
258
  <span className="text-xs">{t("auth.login.resendRateLimited")}</span>
203
259
  )}
@@ -212,14 +268,14 @@ export function LoginScreen({
212
268
  </Button>
213
269
  </Form>
214
270
  {forgotPasswordHref !== undefined && (
215
- <a href={forgotPasswordHref} className={`${authMutedLinkClass} self-center`}>
271
+ <Link href={forgotPasswordHref} variant="muted" className="self-center">
216
272
  {t("auth.login.forgotPassword")}
217
- </a>
273
+ </Link>
218
274
  )}
219
275
  {signupHref !== undefined && (
220
- <a href={signupHref} className={`${authMutedLinkClass} self-center`}>
276
+ <Link href={signupHref} variant="muted" className="self-center">
221
277
  {t("auth.signup.title")}
222
- </a>
278
+ </Link>
223
279
  )}
224
280
  {legalLinks !== undefined && legalLinks.length > 0 && (
225
281
  <nav
@@ -227,9 +283,9 @@ export function LoginScreen({
227
283
  className="flex items-center justify-center gap-3 pt-2 border-t border-border/50"
228
284
  >
229
285
  {legalLinks.map((link) => (
230
- <a key={link.href} href={link.href} className={`${authMutedLinkClass} text-xs`}>
286
+ <Link key={link.href} href={link.href} variant="muted" className="text-xs">
231
287
  {link.label}
232
- </a>
288
+ </Link>
233
289
  ))}
234
290
  </nav>
235
291
  )}
@@ -0,0 +1,112 @@
1
+ // @runtime client
2
+ // RequestAccountUnlockScreen — form with an email input. Submit triggers
3
+ // /api/auth/request-account-unlock (silent-success, no account
4
+ // enumeration). UI unconditionally shows an "if your account is locked,
5
+ // a mail is on its way" confirmation — even when the server internally
6
+ // knows the email doesn't exist or isn't locked. Mirrors
7
+ // ForgotPasswordScreen 1:1.
8
+ //
9
+ // The app is responsible for mounting this screen under a URL (e.g.
10
+ // /unlock-account) and for reaching it — LoginScreen can render a link
11
+ // to the app route on accountLocked errors.
12
+
13
+ import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
14
+ import { type FormEvent, type ReactNode, useState } from "react";
15
+ import { requestAccountUnlock } from "./auth-client";
16
+ import { AuthCard } from "./auth-form-primitives";
17
+
18
+ export type RequestAccountUnlockScreenProps = {
19
+ readonly title?: string;
20
+ readonly subtitle?: ReactNode;
21
+ /** href for the "back to sign in" link in success + form state. App-
22
+ * specific — default "/login". */
23
+ readonly loginHref?: string;
24
+ };
25
+
26
+ export function RequestAccountUnlockScreen({
27
+ title,
28
+ subtitle,
29
+ loginHref = "/login",
30
+ }: RequestAccountUnlockScreenProps): ReactNode {
31
+ const t = useTranslation();
32
+ const { Form, Field, Input, Button, Banner, Link } = usePrimitives();
33
+ const [email, setEmail] = useState("");
34
+ const [submitting, setSubmitting] = useState(false);
35
+ const [done, setDone] = useState(false);
36
+ const [error, setError] = useState<string | null>(null);
37
+
38
+ // guard:dup-ok — same submit pattern as forgot-password-screen, but different API endpoint and state
39
+ const doSubmit = async (): Promise<void> => {
40
+ setSubmitting(true);
41
+ setError(null);
42
+ try {
43
+ const res = await requestAccountUnlock(email);
44
+ if (res.ok) {
45
+ setDone(true);
46
+ } else if (res.error.reason === "rate_limited") {
47
+ const minutes =
48
+ res.error.retryAfterSeconds !== undefined
49
+ ? Math.ceil(res.error.retryAfterSeconds / 60)
50
+ : undefined;
51
+ setError(
52
+ minutes !== undefined
53
+ ? t("auth.requestUnlock.rateLimited", { minutes })
54
+ : t("auth.errors.rateLimited"),
55
+ );
56
+ } else {
57
+ setError(t("auth.errors.unknownError"));
58
+ }
59
+ } catch {
60
+ setError(t("auth.errors.unknownError"));
61
+ } finally {
62
+ setSubmitting(false);
63
+ }
64
+ };
65
+
66
+ const onSubmit = (e?: FormEvent): void => {
67
+ e?.preventDefault();
68
+ void doSubmit();
69
+ };
70
+
71
+ const effectiveTitle = title ?? t("auth.requestUnlock.title");
72
+
73
+ return (
74
+ <AuthCard title={effectiveTitle} subtitle={subtitle}>
75
+ {done ? (
76
+ <div className="p-6 pt-0 flex flex-col gap-4">
77
+ <Banner variant="info">
78
+ <p className="font-medium text-foreground">{t("auth.requestUnlock.successTitle")}</p>
79
+ <p className="mt-1">{t("auth.requestUnlock.successBody")}</p>
80
+ </Banner>
81
+ <Link href={loginHref} variant="muted">
82
+ {t("auth.requestUnlock.backToLogin")}
83
+ </Link>
84
+ </div>
85
+ ) : (
86
+ <div className="p-6 pt-0 flex flex-col gap-4">
87
+ <p className="text-sm text-muted-foreground">{t("auth.requestUnlock.intro")}</p>
88
+ <Form onSubmit={onSubmit}>
89
+ <Field id="unlock-email" label={t("auth.requestUnlock.email")} required>
90
+ <Input
91
+ kind="text"
92
+ id="unlock-email"
93
+ name="unlock-email"
94
+ value={email}
95
+ onChange={setEmail}
96
+ disabled={submitting}
97
+ required
98
+ />
99
+ </Field>
100
+ {error !== null && <Banner variant="error">{error}</Banner>}
101
+ <Button type="submit" loading={submitting} disabled={submitting}>
102
+ {submitting ? t("auth.requestUnlock.submitting") : t("auth.requestUnlock.submit")}
103
+ </Button>
104
+ </Form>
105
+ <Link href={loginHref} variant="muted" className="self-center">
106
+ {t("auth.requestUnlock.backToLogin")}
107
+ </Link>
108
+ </div>
109
+ )}
110
+ </AuthCard>
111
+ );
112
+ }
@@ -5,20 +5,16 @@
5
5
  // Code (anti-enumeration); UI zeigt unified "Link ungültig oder
6
6
  // abgelaufen"-message.
7
7
  //
8
- // Token-Quelle ist read-once: wir lesen via parseUrlToken() einmalig
9
- // im useState-Initializer. Apps die das anders brauchen (server-
10
- // injected Token-Prop, andere Parameter-Namen) reichen einen
11
- // expliziten `token` als Prop durch.
8
+ // Token-Quelle ist read-once via useUrlToken(): liest `?token=...` beim
9
+ // Mount und scrubbt den Param danach aus der URL (#774). Apps die das
10
+ // anders brauchen (server-injected Token-Prop) reichen `token` als Prop
11
+ // durch — dann bleibt die URL unangetastet.
12
12
 
13
13
  import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
14
14
  import { type FormEvent, type ReactNode, useState } from "react";
15
15
  import { resetPassword } from "./auth-client";
16
- import {
17
- AuthCard,
18
- authButtonClass,
19
- authMutedLinkClass,
20
- parseUrlToken,
21
- } from "./auth-form-primitives";
16
+ import { passwordPairIssue } from "./auth-form-logic";
17
+ import { AuthCard, useUrlToken } from "./auth-form-primitives";
22
18
 
23
19
  export type ResetPasswordScreenProps = {
24
20
  readonly title?: string;
@@ -36,8 +32,8 @@ export function ResetPasswordScreen({
36
32
  loginHref = "/login",
37
33
  }: ResetPasswordScreenProps): ReactNode {
38
34
  const t = useTranslation();
39
- const { Form, Field, Input, Button, Banner } = usePrimitives();
40
- const [token] = useState(() => tokenProp ?? parseUrlToken());
35
+ const { Form, Field, Input, Button, Banner, Link } = usePrimitives();
36
+ const token = useUrlToken(tokenProp);
41
37
  const [newPassword, setNewPassword] = useState("");
42
38
  const [confirmPassword, setConfirmPassword] = useState("");
43
39
  const [submitting, setSubmitting] = useState(false);
@@ -46,11 +42,12 @@ export function ResetPasswordScreen({
46
42
 
47
43
  const doSubmit = async (): Promise<void> => {
48
44
  setError(null);
49
- if (newPassword.length < 8) {
45
+ const issue = passwordPairIssue(newPassword, confirmPassword);
46
+ if (issue === "too_short") {
50
47
  setError(t("auth.resetPassword.tooShort"));
51
48
  return;
52
49
  }
53
- if (newPassword !== confirmPassword) {
50
+ if (issue === "mismatch") {
54
51
  setError(t("auth.resetPassword.mismatch"));
55
52
  return;
56
53
  }
@@ -88,9 +85,9 @@ export function ResetPasswordScreen({
88
85
  <AuthCard title={effectiveTitle}>
89
86
  <div className="p-6 pt-0 flex flex-col gap-4">
90
87
  <p className="text-sm text-muted-foreground">{t("auth.resetPassword.missingToken")}</p>
91
- <a href={loginHref} className={authMutedLinkClass}>
88
+ <Link href={loginHref} variant="muted">
92
89
  {t("auth.resetPassword.goToLogin")}
93
- </a>
90
+ </Link>
94
91
  </div>
95
92
  </AuthCard>
96
93
  );
@@ -104,9 +101,9 @@ export function ResetPasswordScreen({
104
101
  <p className="font-medium text-foreground">{t("auth.resetPassword.successTitle")}</p>
105
102
  <p className="mt-1">{t("auth.resetPassword.successBody")}</p>
106
103
  </Banner>
107
- <a href={loginHref} className={authButtonClass}>
104
+ <Link href={loginHref} variant="button">
108
105
  {t("auth.resetPassword.goToLogin")}
109
- </a>
106
+ </Link>
110
107
  </div>
111
108
  ) : (
112
109
  <div className="p-6 pt-0 flex flex-col gap-4">
@@ -15,8 +15,8 @@ import {
15
15
  type CurrentUserProfile,
16
16
  fetchCurrentUser,
17
17
  fetchTenants,
18
- type LoginFailure,
19
18
  type LoginRequest,
19
+ type LoginResult,
20
20
  login as loginApi,
21
21
  logout as logoutApi,
22
22
  switchTenant as switchTenantApi,
@@ -39,9 +39,13 @@ export type SessionState = {
39
39
  };
40
40
 
41
41
  export type SessionApi = SessionState & {
42
- readonly login: (req: LoginRequest) => Promise<{ ok: true } | { ok: false; error: LoginFailure }>;
42
+ readonly login: (req: LoginRequest) => Promise<LoginResult>;
43
43
  readonly logout: () => Promise<void>;
44
44
  readonly switchTenant: (tenantId: string) => Promise<void>;
45
+ /** Re-runs the /auth/tenants + user:me refresh round without a full page
46
+ * reload. Used by auth-mfa's verify screen after a challenge completes
47
+ * and mints cookies via a route session.tsx never calls directly. */
48
+ readonly refresh: () => Promise<void>;
45
49
  };
46
50
 
47
51
  // Exported so screens that render on BOTH authenticated and anonymous public
@@ -131,9 +135,10 @@ export function SessionProvider({ children }: { readonly children: ReactNode }):
131
135
  const login = useCallback<SessionApi["login"]>(
132
136
  async (req) => {
133
137
  const res = await loginApi(req);
134
- if (!res.ok) return { ok: false, error: res.error };
135
- await doRefresh();
136
- return { ok: true };
138
+ if (res.kind === "success") {
139
+ await doRefresh();
140
+ }
141
+ return res;
137
142
  },
138
143
  [doRefresh],
139
144
  );
@@ -161,7 +166,7 @@ export function SessionProvider({ children }: { readonly children: ReactNode }):
161
166
  }
162
167
  }, []);
163
168
 
164
- const api: SessionApi = { ...state, login, logout, switchTenant };
169
+ const api: SessionApi = { ...state, login, logout, switchTenant, refresh: doRefresh };
165
170
  return <SessionContext.Provider value={api}>{children}</SessionContext.Provider>;
166
171
  }
167
172
 
@@ -6,9 +6,9 @@
6
6
  // Server JWT + Cookies (Auto-Login!) und liefert tenantKey für den
7
7
  // Post-Signup-Redirect.
8
8
  //
9
- // Token-Quelle ist read-once: parseUrlToken im useState-Initializer.
10
- // Apps die einen anderen URL-Param nutzen, reichen `token` als Prop
11
- // durch.
9
+ // Token-Quelle ist read-once via useUrlToken: liest `?token=...` beim
10
+ // Mount und scrubbt den Param danach aus der URL (#774). Apps die einen
11
+ // server-injected Token nutzen, reichen `token` als Prop durch.
12
12
  //
13
13
  // Nach success: redirect via window.location.assign zu loggedInHref.
14
14
  // Default-Pattern ist "/<tenantKey>/" — die App reicht ein Template
@@ -18,7 +18,8 @@
18
18
  import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
19
19
  import { type FormEvent, type ReactNode, useState } from "react";
20
20
  import { confirmSignup } from "./auth-client";
21
- import { AuthCard, authMutedLinkClass, parseUrlToken } from "./auth-form-primitives";
21
+ import { passwordPairIssue, resolveLoggedInHref } from "./auth-form-logic";
22
+ import { AuthCard, useUrlToken } from "./auth-form-primitives";
22
23
 
23
24
  export type SignupCompleteScreenProps = {
24
25
  readonly title?: string;
@@ -43,8 +44,8 @@ export function SignupCompleteScreen({
43
44
  loginHref = "/login",
44
45
  }: SignupCompleteScreenProps): ReactNode {
45
46
  const t = useTranslation();
46
- const { Form, Field, Input, Button, Banner } = usePrimitives();
47
- const [token] = useState(() => tokenProp ?? parseUrlToken());
47
+ const { Form, Field, Input, Button, Banner, Link } = usePrimitives();
48
+ const token = useUrlToken(tokenProp);
48
49
  const [password, setPassword] = useState("");
49
50
  const [confirmPassword, setConfirmPassword] = useState("");
50
51
  const [submitting, setSubmitting] = useState(false);
@@ -52,11 +53,12 @@ export function SignupCompleteScreen({
52
53
 
53
54
  const doSubmit = async (): Promise<void> => {
54
55
  setError(null);
55
- if (password.length < 8) {
56
+ const issue = passwordPairIssue(password, confirmPassword);
57
+ if (issue === "too_short") {
56
58
  setError(t("auth.signupComplete.tooShort"));
57
59
  return;
58
60
  }
59
- if (password !== confirmPassword) {
61
+ if (issue === "mismatch") {
60
62
  setError(t("auth.signupComplete.mismatch"));
61
63
  return;
62
64
  }
@@ -66,11 +68,7 @@ export function SignupCompleteScreen({
66
68
  if (res.ok) {
67
69
  // Auto-Login: Cookies sind via Set-Cookie schon im Browser. Wir
68
70
  // schicken den User direkt zur eingeloggten Page.
69
- const target =
70
- typeof loggedInHref === "function"
71
- ? loggedInHref({ tenantKey: res.data.tenantKey })
72
- : loggedInHref;
73
- window.location.assign(target);
71
+ window.location.assign(resolveLoggedInHref(loggedInHref, res.data.tenantKey));
74
72
  return;
75
73
  }
76
74
  if (res.error.reason === "invalid_signup_token") {
@@ -98,9 +96,9 @@ export function SignupCompleteScreen({
98
96
  <AuthCard title={effectiveTitle}>
99
97
  <div className="p-6 pt-0 flex flex-col gap-4">
100
98
  <p className="text-sm text-muted-foreground">{t("auth.signupComplete.missingToken")}</p>
101
- <a href={loginHref} className={authMutedLinkClass}>
99
+ <Link href={loginHref} variant="muted">
102
100
  {t("auth.signup.haveAccount")}
103
- </a>
101
+ </Link>
104
102
  </div>
105
103
  </AuthCard>
106
104
  );
@@ -15,7 +15,8 @@
15
15
  import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
16
16
  import { type FormEvent, type ReactNode, useState } from "react";
17
17
  import { requestSignup } from "./auth-client";
18
- import { AuthCard, authMutedLinkClass } from "./auth-form-primitives";
18
+ import { retryAfterMinutes } from "./auth-form-logic";
19
+ import { AuthCard } from "./auth-form-primitives";
19
20
 
20
21
  export type SignupScreenProps = {
21
22
  readonly title?: string;
@@ -31,7 +32,7 @@ export function SignupScreen({
31
32
  loginHref = "/login",
32
33
  }: SignupScreenProps): ReactNode {
33
34
  const t = useTranslation();
34
- const { Form, Field, Input, Button, Banner } = usePrimitives();
35
+ const { Form, Field, Input, Button, Banner, Link } = usePrimitives();
35
36
  const [email, setEmail] = useState("");
36
37
  const [submitting, setSubmitting] = useState(false);
37
38
  const [done, setDone] = useState(false);
@@ -45,10 +46,7 @@ export function SignupScreen({
45
46
  if (res.ok) {
46
47
  setDone(true);
47
48
  } else if (res.error.reason === "rate_limited") {
48
- const minutes =
49
- res.error.retryAfterSeconds !== undefined
50
- ? Math.ceil(res.error.retryAfterSeconds / 60)
51
- : undefined;
49
+ const minutes = retryAfterMinutes(res.error.retryAfterSeconds);
52
50
  setError(
53
51
  minutes !== undefined
54
52
  ? t("auth.errors.accountLockedRetry", { minutes })
@@ -95,9 +93,9 @@ export function SignupScreen({
95
93
  <Button variant="secondary" onClick={onResend} disabled={submitting}>
96
94
  {t("auth.signup.resend")}
97
95
  </Button>
98
- <a href={loginHref} className={authMutedLinkClass}>
96
+ <Link href={loginHref} variant="muted">
99
97
  {t("auth.signup.haveAccount")}
100
- </a>
98
+ </Link>
101
99
  </div>
102
100
  ) : (
103
101
  <div className="p-6 pt-0 flex flex-col gap-4">
@@ -120,9 +118,9 @@ export function SignupScreen({
120
118
  {submitting ? t("auth.signup.submitting") : t("auth.signup.submit")}
121
119
  </Button>
122
120
  </Form>
123
- <a href={loginHref} className={`${authMutedLinkClass} self-center`}>
121
+ <Link href={loginHref} variant="muted" className="self-center">
124
122
  {t("auth.signup.haveAccount")}
125
- </a>
123
+ </Link>
126
124
  </div>
127
125
  )}
128
126
  </AuthCard>
@@ -29,6 +29,9 @@ export function useShellUser(): ShellUser | undefined {
29
29
  if (session.status !== "authenticated" || session.user === null) return undefined;
30
30
  const activeTenant = session.tenants.find((t) => t.tenantId === session.activeTenantId);
31
31
  if (activeTenant === undefined) return undefined;
32
- return { id: session.user.id, roles: activeTenant.roles };
33
- }, [session.status, session.user, session.activeTenantId, session.tenants]);
32
+ // session.roles merges globalRoles (e.g. SystemAdmin) + membership — same
33
+ // merge the server uses for handler access; membership-only broke platform
34
+ // workspace gating for global operators.
35
+ return { id: session.user.id, roles: session.roles };
36
+ }, [session.status, session.user, session.activeTenantId, session.tenants, session.roles]);
34
37
  }
@@ -33,7 +33,9 @@ export type UserMenuProps = {
33
33
  * controlliert der Caller — wir packen nur den Frame drumrum. */
34
34
  readonly children?: ReactNode;
35
35
  /** "pill" (Default) = kompakter Topbar-Trigger; "sidebar" = volle NavUser-
36
- * Row (Avatar + Name + Email) für den `sidebarFooter`-Slot der App-Shell. */
36
+ * Row (Avatar + Name + Email) für den `sidebarFooter`-Slot der App-Shell.
37
+ * Requires a `SidebarProvider` ancestor — the default App-Shell
38
+ * `sidebarFooter` slot already provides one. */
37
39
  readonly variant?: UserMenuVariant;
38
40
  };
39
41