@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,104 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { mapWithConcurrency } from "./map-with-concurrency";
3
+
4
+ describe("mapWithConcurrency", () => {
5
+ test("preserves result order regardless of completion order", async () => {
6
+ const delays = [30, 10, 20, 0];
7
+ const results = await mapWithConcurrency(delays, 4, async (delay, index) => {
8
+ await new Promise((resolve) => setTimeout(resolve, delay));
9
+ return index;
10
+ });
11
+ expect(results).toEqual([0, 1, 2, 3]);
12
+ });
13
+
14
+ test("never runs more than `limit` calls concurrently", async () => {
15
+ let active = 0;
16
+ let maxActive = 0;
17
+ const items = Array.from({ length: 12 }, (_, i) => i);
18
+ await mapWithConcurrency(items, 4, async (item) => {
19
+ active++;
20
+ maxActive = Math.max(maxActive, active);
21
+ await new Promise((resolve) => setTimeout(resolve, 5));
22
+ active--;
23
+ return item;
24
+ });
25
+ expect(maxActive).toBeLessThanOrEqual(4);
26
+ expect(maxActive).toBeGreaterThan(1);
27
+ });
28
+
29
+ test("propagates the first rejection", async () => {
30
+ const items = [1, 2, 3];
31
+ await expect(
32
+ mapWithConcurrency(items, 4, async (item) => {
33
+ if (item === 2) throw new Error("boom");
34
+ return item;
35
+ }),
36
+ ).rejects.toThrow("boom");
37
+ });
38
+
39
+ // Two workers rejecting at different times, under 4-way concurrency —
40
+ // regression guard for the #1398 hazard (an eagerly-fired, never-awaited
41
+ // Promise surfacing as an unattributed unhandled rejection). Every
42
+ // worker promise here is an element of the internal `Promise.all` array,
43
+ // so both rejections are always handled even though the aggregate
44
+ // `mapWithConcurrency` call already settled on the first one.
45
+ test("a second rejection after the first doesn't surface as an unhandled rejection", async () => {
46
+ const unhandled: unknown[] = [];
47
+ const onUnhandledRejection = (reason: unknown): void => {
48
+ unhandled.push(reason);
49
+ };
50
+ process.on("unhandledRejection", onUnhandledRejection);
51
+ try {
52
+ const items = [1, 2, 3, 4, 5, 6, 7, 8];
53
+ await expect(
54
+ mapWithConcurrency(items, 4, async (item) => {
55
+ if (item === 2) {
56
+ await new Promise((resolve) => setTimeout(resolve, 5));
57
+ throw new Error("boom-early");
58
+ }
59
+ if (item === 5) {
60
+ await new Promise((resolve) => setTimeout(resolve, 20));
61
+ throw new Error("boom-late");
62
+ }
63
+ await new Promise((resolve) => setTimeout(resolve, 10));
64
+ return item;
65
+ }),
66
+ ).rejects.toThrow("boom-early");
67
+ // Give the later-rejecting worker's promise a chance to settle and
68
+ // for a real unhandled rejection (if any) to fire.
69
+ await new Promise((resolve) => setTimeout(resolve, 50));
70
+ } finally {
71
+ process.off("unhandledRejection", onUnhandledRejection);
72
+ }
73
+ expect(unhandled).toEqual([]);
74
+ });
75
+
76
+ test("does not claim new items after the first rejection", async () => {
77
+ const started: number[] = [];
78
+ const items = Array.from({ length: 20 }, (_, i) => i);
79
+ await expect(
80
+ mapWithConcurrency(items, 2, async (item) => {
81
+ started.push(item);
82
+ if (item === 0) throw new Error("boom");
83
+ // Slow enough that without fail-fast, more workers would drain the list.
84
+ await new Promise((resolve) => setTimeout(resolve, 30));
85
+ return item;
86
+ }),
87
+ ).rejects.toThrow("boom");
88
+ // At most `limit` in-flight when the first throw sets `failed`; without
89
+ // the flag the other worker would keep claiming the rest of the 20.
90
+ expect(started.length).toBeLessThan(items.length);
91
+ expect(started.length).toBeLessThanOrEqual(3);
92
+ });
93
+
94
+ test("empty input resolves to an empty array without running the worker", async () => {
95
+ const results = await mapWithConcurrency<number, number>([], 4, async (item) => item);
96
+ expect(results).toEqual([]);
97
+ });
98
+
99
+ test("limit larger than item count doesn't over-spawn workers", async () => {
100
+ const items = [1, 2];
101
+ const results = await mapWithConcurrency(items, 10, async (item) => item * 2);
102
+ expect(results).toEqual([2, 4]);
103
+ });
104
+ });
@@ -0,0 +1,38 @@
1
+ // Bounded-concurrency map — runs `fn` over `items` with at most `limit`
2
+ // calls in flight. `Promise.all(items.map(fn))` is unbounded (as many
3
+ // calls as rows); a plain sequential loop caps concurrency at 1, which
4
+ // under-uses a resource pool that allows more (e.g. PgKmsAdapter's
5
+ // default `max: 4` — sequential decrypt-per-row leaves 3 pool slots idle
6
+ // and turns each query into 2N serial round-trips instead of ~2N/4).
7
+ // Order of `results` matches `items`, independent of completion order.
8
+ // On rejection: the returned promise rejects with the first error, and
9
+ // remaining workers stop claiming new items (`failed` flag). In-flight
10
+ // calls already past the claim still run to completion (their promises
11
+ // stay in the `Promise.all` array, so a later rejection can't surface as
12
+ // an unhandled rejection — verified in `mapWithConcurrency.test.ts`).
13
+ export async function mapWithConcurrency<T, R>(
14
+ items: readonly T[],
15
+ limit: number,
16
+ fn: (item: T, index: number) => Promise<R>,
17
+ ): Promise<R[]> {
18
+ const results: R[] = new Array(items.length);
19
+ let nextIndex = 0;
20
+ let failed = false;
21
+ async function worker(): Promise<void> {
22
+ for (;;) {
23
+ // skip: another worker already failed — stop claiming new items.
24
+ if (failed) return;
25
+ const index = nextIndex++;
26
+ // skip: cursor exhausted, normal worker-loop exit
27
+ if (index >= items.length) return;
28
+ try {
29
+ results[index] = await fn(items[index] as T, index);
30
+ } catch (err) {
31
+ failed = true;
32
+ throw err;
33
+ }
34
+ }
35
+ }
36
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
37
+ return results;
38
+ }
@@ -0,0 +1,35 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { hashPassword, verifyDummyPassword, verifyPassword } from "./password-hashing";
3
+
4
+ const medianMs = async (fn: () => Promise<unknown>, runs = 5): Promise<number> => {
5
+ const samples: number[] = [];
6
+ for (let i = 0; i < runs; i++) {
7
+ const start = performance.now();
8
+ await fn();
9
+ samples.push(performance.now() - start);
10
+ }
11
+ samples.sort((a, b) => a - b);
12
+ return samples[Math.floor(samples.length / 2)] ?? 0;
13
+ };
14
+
15
+ // #774: the login no-user path must cost the same argon2 latency as a real
16
+ // verify, otherwise response timing leaks whether an email is registered.
17
+ describe("verifyDummyPassword (anti-enumeration timing)", () => {
18
+ test("resolves to void, never matches", async () => {
19
+ expect(await verifyDummyPassword("whatever")).toBeUndefined();
20
+ });
21
+
22
+ test("burns real argon2 cost, comparable to a failed verify (not a no-op)", async () => {
23
+ const realHash = await hashPassword("correct-horse-battery");
24
+ // warm the lazily-cached dummy hash so we time the verify, not the one-off hash
25
+ await verifyDummyPassword("warmup");
26
+
27
+ const realMs = await medianMs(() => verifyPassword(realHash, "wrong-password"));
28
+ const dummyMs = await medianMs(() => verifyDummyPassword("wrong-password"));
29
+
30
+ // A skipped miss-path (the bug) is sub-millisecond; a real argon2 verify
31
+ // is ~20ms. A half-of-real floor is a wide, non-flaky bound that still
32
+ // fails hard if the dummy verify is ever removed.
33
+ expect(dummyMs).toBeGreaterThan(realMs * 0.5);
34
+ });
35
+ });
@@ -42,3 +42,14 @@ export async function verifyPassword(hashString: string, password: string): Prom
42
42
  return false;
43
43
  }
44
44
  }
45
+
46
+ // Anti-enumeration timing equaliser (#774). The login handler runs this on
47
+ // the no-user / no-hash path so a missing account costs the same argon2
48
+ // latency as a real verify — otherwise response timing leaks whether an
49
+ // email is registered. Derived from hashPassword once and cached, so it
50
+ // always tracks HASH_OPTIONS; the password never matches, result discarded.
51
+ let dummyHash: Promise<string> | undefined;
52
+ export async function verifyDummyPassword(password: string): Promise<void> {
53
+ dummyHash ??= hashPassword("anti-enumeration-dummy");
54
+ await verifyPassword(await dummyHash, password);
55
+ }
@@ -0,0 +1,5 @@
1
+ // Minimal shape of the httpRoute handler's `{ systemQuery }` dep — mirrors
2
+ // http-route.ts's signature. Not importing HttpRouteHandlerDeps itself: it
3
+ // isn't part of the engine's public surface (only the httpRoute-
4
+ // registration types are).
5
+ export type SystemQueryFn = (type: string, payload: unknown, tenantId: string) => Promise<unknown>;
@@ -1,5 +1,5 @@
1
1
  // Single-use enforcement for HMAC-signed auth tokens (password-reset,
2
- // email-verification).
2
+ // email-verification, MFA setup/challenge).
3
3
  //
4
4
  // Problem: the token itself carries only userId + expiry + signature.
5
5
  // Without server-side burn, the same token can be replayed within its
@@ -1,8 +1,17 @@
1
1
  // feature.ts contract tests for subscription-mollie.
2
2
 
3
3
  import { describe, expect, test } from "bun:test";
4
+ import type { HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
5
+ import type {
6
+ MollieClient,
7
+ Payment as MolliePayment,
8
+ Subscription as MollieSubscription,
9
+ } from "@mollie/api-client";
10
+ import { describeSubscriptionProviderContract } from "../../billing-foundation/__tests__/subscription-provider-contract";
4
11
  import { MOLLIE_PROVIDER_NAME, SUBSCRIPTION_MOLLIE_FEATURE } from "../constants";
5
12
  import { createSubscriptionMollieFeature } from "../feature";
13
+ import { createMollieCheckoutSession } from "../plugin-methods";
14
+ import { type MollieClientShape, verifyAndParseMollieWebhook } from "../verify-webhook";
6
15
 
7
16
  const VALID_OPTIONS = {
8
17
  apiKey: "test_dummy_apikey",
@@ -98,9 +107,97 @@ describe("subscription-mollie — plugin-registration", () => {
98
107
  createPortalSession?: unknown;
99
108
  cancelSubscription?: unknown;
100
109
  };
110
+ // Checks the mounted feature (the contract below builds its plugin
111
+ // by hand-wiring and would not catch a wiring gap here).
101
112
  expect(typeof plugin?.verifyAndParseWebhook).toBe("function");
102
113
  expect(typeof plugin?.createCheckoutSession).toBe("function");
103
114
  expect(plugin?.createPortalSession).toBeUndefined();
104
115
  expect(plugin?.cancelSubscription).toBeUndefined();
105
116
  });
106
117
  });
118
+
119
+ // =============================================================================
120
+ // SubscriptionProviderPlugin contract
121
+ // =============================================================================
122
+ //
123
+ // Built directly from the plugin-method constructors (not from
124
+ // createSubscriptionMollieFeature's mounted plugin) — same reasoning as
125
+ // subscription-stripe's contract fixture: keeps SDK-call mocking local to
126
+ // the fixture instead of routing through the full feature-mount.
127
+
128
+ const CONTRACT_TENANT_ID = "tenant-contract";
129
+ const CONTRACT_PRICE_ID = "plan_pro";
130
+
131
+ function buildMollieContractFixture() {
132
+ const subscription = {
133
+ id: "sub_contract_001",
134
+ customerId: "cst_contract_001",
135
+ status: "active",
136
+ nextPaymentDate: "2026-06-15",
137
+ startDate: "2026-05-15",
138
+ metadata: { tenantId: CONTRACT_TENANT_ID, priceId: CONTRACT_PRICE_ID },
139
+ // Minimal Mollie-SDK-shape mock — full Subscription has more fields
140
+ // than the plugin reads; double-cast at the test/SDK boundary instead
141
+ // of `as any` per coding-standards.
142
+ } as unknown as MollieSubscription;
143
+ const payment = {
144
+ id: "tr_contract_001",
145
+ customerId: "cst_contract_001",
146
+ subscriptionId: "sub_contract_001",
147
+ sequenceType: "first",
148
+ status: "paid",
149
+ // Minimal Mollie-SDK-shape mock, see subscription above.
150
+ } as unknown as MolliePayment;
151
+
152
+ const webhookClient: MollieClientShape = {
153
+ payments: { get: async () => payment },
154
+ customerSubscriptions: {
155
+ get: async () => subscription,
156
+ list: async () => [],
157
+ create: async () => subscription,
158
+ },
159
+ };
160
+
161
+ const checkoutClient = {
162
+ customers: { create: async () => ({ id: "cus_contract" }) },
163
+ payments: {
164
+ create: async () => ({
165
+ getCheckoutUrl: () => "https://www.mollie.com/checkout/contract-test",
166
+ }),
167
+ },
168
+ } as unknown as MollieClient;
169
+
170
+ return {
171
+ plugin: {
172
+ verifyAndParseWebhook: verifyAndParseMollieWebhook(webhookClient, {
173
+ priceToTier: VALID_OPTIONS.priceToTier,
174
+ priceToConfig: VALID_OPTIONS.priceToConfig,
175
+ }),
176
+ createCheckoutSession: createMollieCheckoutSession(
177
+ checkoutClient,
178
+ VALID_OPTIONS.priceToConfig,
179
+ VALID_OPTIONS.webhookUrl,
180
+ ),
181
+ // createPortalSession + cancelSubscription deliberately omitted — Mollie limitation.
182
+ },
183
+ ctx: {} as HandlerContext,
184
+ checkout: {
185
+ priceId: CONTRACT_PRICE_ID,
186
+ tenantId: CONTRACT_TENANT_ID,
187
+ successUrl: "https://example.com/success",
188
+ cancelUrl: "https://example.com/cancel",
189
+ },
190
+ webhook: {
191
+ rawBody: JSON.stringify({ id: "tr_contract_001" }),
192
+ headers: { "content-type": "application/json" },
193
+ expectedTenantId: CONTRACT_TENANT_ID,
194
+ expectedTier: "pro",
195
+ },
196
+ };
197
+ }
198
+
199
+ describeSubscriptionProviderContract("subscription-mollie", buildMollieContractFixture, {
200
+ // Mollie has no customer-portal or programmatic cancel API.
201
+ hasPortal: false,
202
+ hasCancel: false,
203
+ });
@@ -39,6 +39,10 @@ import type {
39
39
  Subscription as MollieSubscription,
40
40
  } from "@mollie/api-client";
41
41
  import { Hono } from "hono";
42
+ import { createComplianceProfilesFeature } from "../../compliance-profiles";
43
+ import { createConfigFeature } from "../../config";
44
+ import { createTenantFeature } from "../../tenant/feature";
45
+ import { createTenantLifecycleFeature } from "../../tenant-lifecycle";
42
46
  import { createSubscriptionMollieFeature } from "../feature";
43
47
  import type { MollieClientShape } from "../verify-webhook";
44
48
 
@@ -133,7 +137,14 @@ beforeAll(async () => {
133
137
  });
134
138
 
135
139
  stack = await setupTestStack({
136
- features: [billingFoundationFeature, mollieFeature],
140
+ features: [
141
+ createConfigFeature(),
142
+ createTenantFeature(),
143
+ createComplianceProfilesFeature(),
144
+ createTenantLifecycleFeature(),
145
+ billingFoundationFeature,
146
+ mollieFeature,
147
+ ],
137
148
  });
138
149
  db = stack.db;
139
150
  // subscriptionsProjectionTable wird von setupTestStack automatisch
@@ -347,6 +347,27 @@ describe("verifyAndParseMollieWebhook — error + ignore paths", () => {
347
347
  });
348
348
  });
349
349
 
350
+ describe("verifyAndParseMollieWebhook — kumiko-framework#1525: no ambient Temporal global", () => {
351
+ const verify = (client: MollieClientShape) =>
352
+ verifyAndParseMollieWebhook(client, {
353
+ priceToTier: { plan_pro: "pro" },
354
+ priceToConfig: TEST_PRICE_CONFIG,
355
+ });
356
+
357
+ test("computes currentPeriodEnd without relying on globalThis.Temporal", async () => {
358
+ const client = buildClient();
359
+ const savedGlobal = (globalThis as { Temporal?: unknown }).Temporal;
360
+ delete (globalThis as { Temporal?: unknown }).Temporal;
361
+ try {
362
+ const event = await verify(client)("id=tr_test_001", {});
363
+ expect(event?.currentPeriodEnd).toBe("2026-06-15T00:00:00Z");
364
+ } finally {
365
+ if (savedGlobal === undefined) delete (globalThis as { Temporal?: unknown }).Temporal;
366
+ else (globalThis as { Temporal?: unknown }).Temporal = savedGlobal;
367
+ }
368
+ });
369
+ });
370
+
350
371
  // =============================================================================
351
372
  // Mapping-helpers
352
373
  // =============================================================================
@@ -25,6 +25,7 @@ import type {
25
25
  Payment as MolliePayment,
26
26
  Subscription as MollieSubscription,
27
27
  } from "@mollie/api-client";
28
+ import { Temporal } from "temporal-polyfill";
28
29
  import { MOLLIE_PROVIDER_NAME } from "./constants";
29
30
  import type { MolliePriceConfig } from "./plugin-methods";
30
31
 
@@ -1,8 +1,18 @@
1
1
  // feature.ts contract tests for subscription-stripe.
2
2
 
3
- import { describe, expect, test } from "bun:test";
3
+ import { describe, expect, spyOn, test } from "bun:test";
4
+ import type { HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
5
+ import Stripe from "stripe";
6
+ import { describeSubscriptionProviderContract } from "../../billing-foundation/__tests__/subscription-provider-contract";
4
7
  import { STRIPE_PROVIDER_NAME, StripeEventTypes, SUBSCRIPTION_STRIPE_FEATURE } from "../constants";
5
8
  import { createSubscriptionStripeFeature } from "../feature";
9
+ import {
10
+ createStripeCancelSubscription,
11
+ createStripeCheckoutSession,
12
+ createStripePortalSession,
13
+ } from "../plugin-methods";
14
+ import type { StripeCtxRuntime } from "../runtime";
15
+ import { verifyAndParseStripeWebhook } from "../verify-webhook";
6
16
 
7
17
  const OPTIONS = {
8
18
  priceToTier: { price_test: "pro" },
@@ -69,6 +79,8 @@ describe("subscription-stripe — plugin-registration", () => {
69
79
  // "method not supported"-error brechen — type-check würde es nicht
70
80
  // fangen weil die useExtension-options als `unknown` durchgereicht
71
81
  // werden.
82
+ // Checks the mounted feature — the contract below builds its plugin
83
+ // by hand-wiring, so it would not catch a wiring gap here.
72
84
  const feature = createSubscriptionStripeFeature(OPTIONS);
73
85
  const usage = feature.extensionUsages.find((u) => u.entityName === STRIPE_PROVIDER_NAME);
74
86
  expect(usage).toBeDefined();
@@ -100,3 +112,117 @@ describe("constants — Stripe-event-types die wir mappen", () => {
100
112
  ]);
101
113
  });
102
114
  });
115
+
116
+ // =============================================================================
117
+ // SubscriptionProviderPlugin contract
118
+ // =============================================================================
119
+ //
120
+ // Built directly from the plugin-method constructors (not from
121
+ // createSubscriptionStripeFeature's mounted plugin) — the mounted plugin
122
+ // resolves its Stripe client per-call from ctx.config/ctx.secrets, which
123
+ // can't be spied on from outside. plugin-methods.test.ts already exercises
124
+ // this same runtime-injection pattern for the individual methods.
125
+
126
+ const CONTRACT_API_KEY = "sk_test_contract_dummy";
127
+ const CONTRACT_WEBHOOK_SECRET = "whsec_contract_test_secret";
128
+
129
+ function contractCtxRuntime(stripe: Stripe): StripeCtxRuntime {
130
+ return { clientForCtx: async () => stripe, assertBillingLive: async () => {} };
131
+ }
132
+
133
+ async function buildStripeContractFixture() {
134
+ const stripe = new Stripe(CONTRACT_API_KEY);
135
+ spyOn(stripe.checkout.sessions, "create").mockResolvedValue({
136
+ url: "https://checkout.stripe.com/c/pay/contract-test",
137
+ // Only `.url` is read by createStripeCheckoutSession. mockResolvedValue
138
+ // wants Stripe.Response<Session> (adds lastResponse metadata) — the SDK
139
+ // wrapper type makes a structural mock impractical, so double-cast at
140
+ // the test/SDK boundary instead of `as any`.
141
+ } as unknown as Stripe.Response<Stripe.Checkout.Session>);
142
+ spyOn(stripe.billingPortal.sessions, "create").mockResolvedValue({
143
+ url: "https://billing.stripe.com/p/session/contract-test",
144
+ // Only `.url` is read by createStripePortalSession, see above.
145
+ } as unknown as Stripe.Response<Stripe.BillingPortal.Session>);
146
+ spyOn(stripe.subscriptions, "cancel").mockResolvedValue({
147
+ id: "sub_contract",
148
+ status: "canceled",
149
+ // Return value isn't read by createStripeCancelSubscription (fire-and-
150
+ // forget) — double-cast at the test/SDK boundary instead of `as any`.
151
+ } as unknown as Stripe.Response<Stripe.Subscription>);
152
+ const runtime = contractCtxRuntime(stripe);
153
+
154
+ const stripeForWebhookFixture = new Stripe(CONTRACT_API_KEY);
155
+ const webhookEvent = {
156
+ id: "evt_contract_001",
157
+ object: "event",
158
+ api_version: "2026-04-22.dahlia",
159
+ created: 1_770_000_000,
160
+ type: StripeEventTypes.customerSubscriptionCreated,
161
+ livemode: false,
162
+ pending_webhooks: 1,
163
+ request: { id: null, idempotency_key: null },
164
+ data: {
165
+ object: {
166
+ id: "sub_contract_001",
167
+ object: "subscription",
168
+ customer: "cus_contract_001",
169
+ status: "active",
170
+ metadata: { tenantId: "tenant-contract" },
171
+ items: {
172
+ object: "list",
173
+ data: [
174
+ {
175
+ id: "si_contract",
176
+ object: "subscription_item",
177
+ current_period_end: 1_780_000_000,
178
+ price: { id: "price_test", object: "price" },
179
+ },
180
+ ],
181
+ has_more: false,
182
+ },
183
+ },
184
+ },
185
+ };
186
+ const rawBody = JSON.stringify(webhookEvent);
187
+ const signature = await stripeForWebhookFixture.webhooks.generateTestHeaderStringAsync({
188
+ payload: rawBody,
189
+ secret: CONTRACT_WEBHOOK_SECRET,
190
+ });
191
+
192
+ return {
193
+ plugin: {
194
+ verifyAndParseWebhook: verifyAndParseStripeWebhook(
195
+ {
196
+ resolve: async () => ({
197
+ stripe: stripeForWebhookFixture,
198
+ webhookSecret: CONTRACT_WEBHOOK_SECRET,
199
+ }),
200
+ },
201
+ { priceToTier: OPTIONS.priceToTier },
202
+ ),
203
+ createCheckoutSession: createStripeCheckoutSession(runtime),
204
+ createPortalSession: createStripePortalSession(runtime),
205
+ cancelSubscription: createStripeCancelSubscription(runtime),
206
+ },
207
+ ctx: {} as HandlerContext,
208
+ checkout: {
209
+ priceId: "price_test",
210
+ tenantId: "tenant-contract",
211
+ successUrl: "https://example.com/success",
212
+ cancelUrl: "https://example.com/cancel",
213
+ },
214
+ portal: { providerCustomerId: "cus_contract", returnUrl: "https://example.com/return" },
215
+ cancelSubscriptionId: "sub_contract",
216
+ webhook: {
217
+ rawBody,
218
+ headers: { "stripe-signature": signature },
219
+ expectedTenantId: "tenant-contract",
220
+ expectedTier: "pro",
221
+ },
222
+ };
223
+ }
224
+
225
+ describeSubscriptionProviderContract("subscription-stripe", buildStripeContractFixture, {
226
+ hasPortal: true,
227
+ hasCancel: true,
228
+ });
@@ -44,34 +44,24 @@ const BILLING_LIVE_HANDLE: ConfigKeyHandle<"boolean"> = {
44
44
 
45
45
  /** Stub-SecretsContext: liest aus einer in-memory-map, matcht per
46
46
  * qualified-name. backing:"secrets" persistiert config-Werte JSON-
47
- * serialisiert — der Stub spiegelt das, damit der Runtime-parseStoredSecret
48
- * denselben Pfad nimmt wie gegen den echten Store. Ignoriert auditCtx. */
49
- function stubSecrets(values: Record<string, string>): SecretsContext {
47
+ * serialisiert — der Stub spiegelt das per Default, damit der Runtime-
48
+ * parseStoredSecret denselben Pfad nimmt wie gegen den echten Store.
49
+ * `raw: true` speichert UN-serialisiert (482/1) — um parseStoredSecret's
50
+ * Fehlerpfad zu treffen: ein Credential, das der Store un-JSON-kodiert
51
+ * zurückgibt (Korruption oder außerhalb des backing:"secrets"-Roundtrips
52
+ * geschrieben) muss laut failen, nicht still Müll liefern. Ignoriert
53
+ * auditCtx. */
54
+ function stubSecrets(
55
+ values: Record<string, string>,
56
+ opts?: { readonly raw?: boolean },
57
+ ): SecretsContext {
50
58
  const nameOf = (k: string | { readonly name: string }): string =>
51
59
  typeof k === "string" ? k : k.name;
52
60
  return {
53
61
  get: async (_tenantId, key) => {
54
62
  const value = values[nameOf(key)];
55
- return value === undefined ? undefined : createSecret(JSON.stringify(value));
56
- },
57
- has: async (_tenantId, key) => values[nameOf(key)] !== undefined,
58
- set: async () => undefined,
59
- delete: async () => false,
60
- };
61
- }
62
-
63
- /** Wie stubSecrets, aber speichert den Wert ROH (kein JSON.stringify) — um
64
- * parseStoredSecret's Fehlerpfad zu treffen: ein Credential, das der Store
65
- * un-JSON-kodiert zurückgibt (Korruption oder ein außerhalb des
66
- * backing:"secrets"-Roundtrips geschriebener Wert) muss laut failen, nicht
67
- * still Müll liefern. */
68
- function rawSecretsStub(values: Record<string, string>): SecretsContext {
69
- const nameOf = (k: string | { readonly name: string }): string =>
70
- typeof k === "string" ? k : k.name;
71
- return {
72
- get: async (_tenantId, key) => {
73
- const value = values[nameOf(key)];
74
- return value === undefined ? undefined : createSecret(value); // RAW, not JSON
63
+ if (value === undefined) return undefined;
64
+ return createSecret(opts?.raw ? value : JSON.stringify(value));
75
65
  },
76
66
  has: async (_tenantId, key) => values[nameOf(key)] !== undefined,
77
67
  set: async () => undefined,
@@ -164,7 +154,7 @@ describe("StripeCtxRuntime.clientForCtx", () => {
164
154
  // throw, not silently fall through to undefined/fallback.
165
155
  const rt = makeRuntimes({ apiKey: "sk_test_fallback" });
166
156
  const ctx = stubCtx({
167
- secrets: rawSecretsStub({ [API_KEY_HANDLE.name]: "sk_test_raw_unquoted" }),
157
+ secrets: stubSecrets({ [API_KEY_HANDLE.name]: "sk_test_raw_unquoted" }, { raw: true }),
168
158
  });
169
159
  await expect(rt.ctx.clientForCtx(ctx)).rejects.toThrow(
170
160
  /Invalid JSON in subscription-stripe credential/,
@@ -20,7 +20,7 @@ import {
20
20
  type SubscriptionProviderPlugin,
21
21
  subscriptionAggregateId,
22
22
  } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
23
- import { createEncryptionProvider, type DbConnection } from "@cosmicdrift/kumiko-framework/db";
23
+ import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
24
24
  import { SYSTEM_TENANT_ID, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
25
25
  import { createEventsTable, loadAggregate } from "@cosmicdrift/kumiko-framework/event-store";
26
26
  import { createEnvMasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
@@ -31,8 +31,10 @@ import {
31
31
  testTenantId,
32
32
  unsafePushTables,
33
33
  } from "@cosmicdrift/kumiko-framework/stack";
34
+ import { createTestEnvelopeCipher } from "@cosmicdrift/kumiko-framework/testing";
34
35
  import { Hono } from "hono";
35
36
  import Stripe from "stripe";
37
+ import { createComplianceProfilesFeature } from "../../compliance-profiles";
36
38
  import { configValuesTable, createConfigFeature } from "../../config";
37
39
  import { createConfigAccessorFactory } from "../../config/feature";
38
40
  import { createConfigResolver } from "../../config/resolver";
@@ -42,6 +44,8 @@ import {
42
44
  type SecretsContext,
43
45
  tenantSecretsTable,
44
46
  } from "../../secrets";
47
+ import { createTenantFeature } from "../../tenant/feature";
48
+ import { createTenantLifecycleFeature } from "../../tenant-lifecycle";
45
49
  import { createSubscriptionStripeFeature } from "../feature";
46
50
 
47
51
  // Qualified-names der runtime-keys (drift-pin: müssen 1:1 dem entsprechen,
@@ -79,8 +83,8 @@ beforeAll(async () => {
79
83
  priceToTier: PRICE_TO_TIER,
80
84
  });
81
85
 
82
- const encryption = createEncryptionProvider(randomBytes(32).toString("base64"));
83
- const resolver = createConfigResolver({ encryption });
86
+ const encryption = createTestEnvelopeCipher(randomBytes(32).toString("base64"));
87
+ const resolver = createConfigResolver({ cipher: encryption });
84
88
  const masterKeyProvider = createEnvMasterKeyProvider({
85
89
  env: {
86
90
  KUMIKO_SECRETS_MASTER_KEY_V1: randomBytes(32).toString("base64"),
@@ -92,6 +96,9 @@ beforeAll(async () => {
92
96
  features: [
93
97
  createConfigFeature(),
94
98
  createSecretsFeature(),
99
+ createTenantFeature(),
100
+ createComplianceProfilesFeature(),
101
+ createTenantLifecycleFeature(),
95
102
  billingFoundationFeature,
96
103
  stripeFeature,
97
104
  ],
@@ -470,8 +477,8 @@ describe("scenario 6: billing-live gate end-to-end (#104)", () => {
470
477
 
471
478
  beforeAll(async () => {
472
479
  const stripeFeature = createSubscriptionStripeFeature({ priceToTier: PRICE_TO_TIER });
473
- const encryption = createEncryptionProvider(randomBytes(32).toString("base64"));
474
- const resolver = createConfigResolver({ encryption });
480
+ const encryption = createTestEnvelopeCipher(randomBytes(32).toString("base64"));
481
+ const resolver = createConfigResolver({ cipher: encryption });
475
482
  const masterKeyProvider = createEnvMasterKeyProvider({
476
483
  env: {
477
484
  KUMIKO_SECRETS_MASTER_KEY_V1: randomBytes(32).toString("base64"),
@@ -482,6 +489,9 @@ describe("scenario 6: billing-live gate end-to-end (#104)", () => {
482
489
  features: [
483
490
  createConfigFeature(),
484
491
  createSecretsFeature(),
492
+ createTenantFeature(),
493
+ createComplianceProfilesFeature(),
494
+ createTenantLifecycleFeature(),
485
495
  billingFoundationFeature,
486
496
  stripeFeature,
487
497
  ],