@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,108 @@
1
+ // Security integration tests for jobs operator HTTP surface.
2
+ // Real HTTP via setupTestStack — no mocks.
3
+
4
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
5
+ import { defineFeature, type SessionUser } from "@cosmicdrift/kumiko-framework/engine";
6
+ import {
7
+ createTestUser,
8
+ setupTestStack,
9
+ type TestStack,
10
+ TestUsers,
11
+ unsafePushTables,
12
+ } from "@cosmicdrift/kumiko-framework/stack";
13
+ import { rolesOf } from "@cosmicdrift/kumiko-framework/testing";
14
+ import { createConfigFeature } from "../../config/feature";
15
+ import {
16
+ JOB_RUN_DETAIL_SCREEN_ID,
17
+ JOB_RUNS_SCREEN_ID,
18
+ JobHandlers,
19
+ JobQueries,
20
+ } from "../constants";
21
+ import { createJobsFeature } from "../feature";
22
+ import { jobRunLogsTable, jobRunsTable } from "../job-run-table";
23
+
24
+ const SYSTEM_ADMIN_ROLES = ["SystemAdmin"] as const;
25
+
26
+ const noopJobFeature = defineFeature("jobs-sec-app", (r) => {
27
+ r.job("ping", { trigger: { manual: true } }, async () => {});
28
+ });
29
+
30
+ let stack: TestStack;
31
+
32
+ const systemAdmin = TestUsers.systemAdmin;
33
+ const tenantAdmin: SessionUser = createTestUser({ id: 3, roles: ["TenantAdmin"] });
34
+ const regularUser: SessionUser = createTestUser({ id: 4, roles: ["User"] });
35
+
36
+ beforeAll(async () => {
37
+ stack = await setupTestStack({
38
+ features: [createConfigFeature(), noopJobFeature, createJobsFeature()],
39
+ });
40
+ await unsafePushTables(stack.db, { jobRunsTable, jobRunLogsTable });
41
+ });
42
+
43
+ afterAll(async () => {
44
+ await stack.cleanup();
45
+ });
46
+
47
+ describe("access matrix: jobs operator surface is SystemAdmin-only", () => {
48
+ test("list, detail, trigger, retry handlers share SystemAdmin", () => {
49
+ const roles = [...SYSTEM_ADMIN_ROLES];
50
+ expect(rolesOf(stack.registry.getQueryHandler(JobQueries.list)?.access)).toEqual(roles);
51
+ expect(rolesOf(stack.registry.getQueryHandler(JobQueries.details)?.access)).toEqual(roles);
52
+ expect(rolesOf(stack.registry.getWriteHandler(JobHandlers.trigger)?.access)).toEqual(roles);
53
+ expect(rolesOf(stack.registry.getWriteHandler(JobHandlers.retry)?.access)).toEqual(roles);
54
+ });
55
+
56
+ test("job screens match SystemAdmin handler access", () => {
57
+ const jobs = createJobsFeature();
58
+ for (const id of [JOB_RUNS_SCREEN_ID, JOB_RUN_DETAIL_SCREEN_ID] as const) {
59
+ const screen = jobs.screens[id];
60
+ if (screen && "access" in screen && screen.access && "roles" in screen.access) {
61
+ expect(screen.access.roles).toEqual(SYSTEM_ADMIN_ROLES);
62
+ }
63
+ }
64
+ });
65
+ });
66
+
67
+ describe("non-SystemAdmin denied jobs HTTP surface", () => {
68
+ test("TenantAdmin 403 on list, detail, trigger", async () => {
69
+ for (const fn of [
70
+ () => stack.http.query(JobQueries.list, {}, tenantAdmin),
71
+ () =>
72
+ stack.http.query(
73
+ JobQueries.details,
74
+ { runId: "00000000-0000-4000-8000-000000000099" },
75
+ tenantAdmin,
76
+ ),
77
+ () =>
78
+ stack.http.write(JobHandlers.trigger, { jobName: "jobs-sec-app:job:ping" }, tenantAdmin),
79
+ ] as const) {
80
+ const res = await fn();
81
+ expect(res.status).toBe(403);
82
+ }
83
+ });
84
+
85
+ test("regular User 403 on list and trigger", async () => {
86
+ expect((await stack.http.query(JobQueries.list, {}, regularUser)).status).toBe(403);
87
+ expect(
88
+ (
89
+ await stack.http.write(
90
+ JobHandlers.trigger,
91
+ { jobName: "jobs-sec-app:job:ping" },
92
+ regularUser,
93
+ )
94
+ ).status,
95
+ ).toBe(403);
96
+ });
97
+ });
98
+
99
+ describe("SystemAdmin can use jobs queries", () => {
100
+ test("empty list is OK", async () => {
101
+ const res = await stack.http.queryOk<{ rows: readonly unknown[] }>(
102
+ JobQueries.list,
103
+ {},
104
+ systemAdmin,
105
+ );
106
+ expect(Array.isArray(res.rows)).toBe(true);
107
+ });
108
+ });
@@ -0,0 +1,169 @@
1
+ // #1215: the `jobs` feature registers `reindexEntity` as a manual + perTenant
2
+ // job. perTenant jobs fan out through a BullMQ wrapper that needs
3
+ // `getActiveTenantIds` (job-runner.ts) — setupTestStack's jobRunner doesn't
4
+ // wire that, so this calls the exported handler directly with a constructed
5
+ // context instead of going through jobRunner.dispatch(), same as
6
+ // inbound-mail-foundation's retention job test calls its sweep function
7
+ // directly rather than round-tripping through the queue.
8
+
9
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
10
+ import {
11
+ buildEntityTable,
12
+ createEventStoreExecutor,
13
+ createTenantDb,
14
+ } from "@cosmicdrift/kumiko-framework/db";
15
+ import {
16
+ createEntity,
17
+ createTextField,
18
+ defineFeature,
19
+ type JobContext,
20
+ } from "@cosmicdrift/kumiko-framework/engine";
21
+ import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
22
+ import {
23
+ setupTestStack,
24
+ type TestStack,
25
+ TestUsers,
26
+ unsafeCreateEntityTable,
27
+ } from "@cosmicdrift/kumiko-framework/stack";
28
+ import { bridgeStub } from "@cosmicdrift/kumiko-framework/testing";
29
+ import { createJobsFeature } from "../feature";
30
+ import { reindexEntityJob } from "../handlers/reindex-entity.job";
31
+
32
+ // Resolved job name — operators trigger this via jobs:write:trigger with
33
+ // { jobName: "jobs:job:reindex-entity", payload: { entity: "..." } }.
34
+ const REINDEX_ENTITY_JOB = "jobs:job:reindex-entity";
35
+
36
+ const widgetEntity = createEntity({
37
+ table: "read_reindex_job_widgets",
38
+ fields: {
39
+ name: createTextField({ required: true, searchable: true }),
40
+ },
41
+ });
42
+
43
+ const widgetTable = buildEntityTable("widget", widgetEntity);
44
+
45
+ const widgetFeature = defineFeature("reindex-job-test", (r) => {
46
+ r.entity("widget", widgetEntity);
47
+ });
48
+
49
+ let stack: TestStack;
50
+ const admin = TestUsers.admin;
51
+
52
+ const noopLogger: JobContext["log"] = {
53
+ info() {},
54
+ warn() {},
55
+ error() {},
56
+ debug() {},
57
+ child() {
58
+ return noopLogger;
59
+ },
60
+ };
61
+
62
+ beforeAll(async () => {
63
+ stack = await setupTestStack({ features: [widgetFeature, createJobsFeature()] });
64
+ await unsafeCreateEntityTable(stack.db, widgetEntity);
65
+ await createEventsTable(stack.db);
66
+ });
67
+
68
+ afterAll(async () => {
69
+ await stack.cleanup();
70
+ });
71
+
72
+ describe("jobs feature: reindexEntity registration", () => {
73
+ test("registers reindexEntity as jobs:job:reindex-entity — the name jobs:write:trigger dispatches by", () => {
74
+ const job = stack.registry.getJob(REINDEX_ENTITY_JOB);
75
+ expect(job).toBeDefined();
76
+ expect(job?.handler).toBe(reindexEntityJob);
77
+ expect(job?.perTenant).toBe(true);
78
+ expect(job?.trigger).toEqual({ manual: true });
79
+ });
80
+ });
81
+
82
+ describe("reindexEntityJob", () => {
83
+ test("indexes rows for the tenant resolved from ctx.systemUser", async () => {
84
+ const executor = createEventStoreExecutor(widgetTable, widgetEntity, { entityName: "widget" });
85
+ const tenantDb = createTenantDb(stack.db, admin.tenantId, "system");
86
+ const created = await executor.create({ name: "Job Backfillable Widget" }, admin, tenantDb);
87
+ if (!created.isSuccess) throw new Error("seed failed");
88
+
89
+ // No stack.eventDispatcher.runOnce() — row was never indexed live.
90
+ const preResults = await stack.search.search(admin.tenantId, "backfillable", {
91
+ filterType: "widget",
92
+ });
93
+ expect(preResults).toHaveLength(0);
94
+
95
+ const ctx: JobContext = {
96
+ db: stack.db,
97
+ registry: stack.registry,
98
+ searchAdapter: stack.search,
99
+ systemUser: admin,
100
+ log: noopLogger,
101
+ triggeredBy: null,
102
+ ...bridgeStub(),
103
+ };
104
+ await reindexEntityJob({ entity: "widget" }, ctx);
105
+
106
+ const postResults = await stack.search.search(admin.tenantId, "backfillable", {
107
+ filterType: "widget",
108
+ });
109
+ expect(postResults.some((r) => r.entityId === created.data.id)).toBe(true);
110
+ });
111
+
112
+ test("the job is registered under REINDEX_ENTITY_JOB and its handler indexes", async () => {
113
+ // Honest scope: this still hand-builds JobContext (same as
114
+ // retention-cleanup's handler test) rather than going through
115
+ // jobRunner.dispatch() — it does NOT cover the dispatch chain's own
116
+ // ctx-resolution/guard logic, only that registry.getJob(REINDEX_ENTITY_JOB)
117
+ // resolves to a handler and that handler indexes correctly when called
118
+ // with a well-formed ctx. A bug in dispatch()'s ctx-resolution itself
119
+ // (e.g. a systemUser that isn't wired through) would be invisible here,
120
+ // same as in the two direct-call tests above.
121
+ const executor = createEventStoreExecutor(widgetTable, widgetEntity, { entityName: "widget" });
122
+ const tenantDb = createTenantDb(stack.db, admin.tenantId, "system");
123
+ const created = await executor.create({ name: "Registry-Resolved Widget" }, admin, tenantDb);
124
+ if (!created.isSuccess) throw new Error("seed failed");
125
+
126
+ const preResults = await stack.search.search(admin.tenantId, "registry-resolved", {
127
+ filterType: "widget",
128
+ });
129
+ expect(preResults).toHaveLength(0);
130
+
131
+ const job = stack.registry.getJob(REINDEX_ENTITY_JOB);
132
+ expect(job).toBeDefined();
133
+ if (!job) return;
134
+
135
+ const ctx: JobContext = {
136
+ db: stack.db,
137
+ registry: stack.registry,
138
+ searchAdapter: stack.search,
139
+ systemUser: admin,
140
+ log: noopLogger,
141
+ triggeredBy: null,
142
+ ...bridgeStub(),
143
+ };
144
+ await job.handler({ entity: "widget" }, ctx);
145
+
146
+ const postResults = await stack.search.search(admin.tenantId, "registry-resolved", {
147
+ filterType: "widget",
148
+ });
149
+ expect(postResults.some((r) => r.entityId === created.data.id)).toBe(true);
150
+ });
151
+
152
+ test("skips silently when no tenant is resolvable (fan-out misfire)", async () => {
153
+ await expect(
154
+ reindexEntityJob(
155
+ { entity: "widget" },
156
+ { db: stack.db, registry: stack.registry, searchAdapter: stack.search },
157
+ ),
158
+ ).resolves.toBeUndefined();
159
+ });
160
+
161
+ test("throws when ctx.searchAdapter is missing", async () => {
162
+ await expect(
163
+ reindexEntityJob(
164
+ { entity: "widget" },
165
+ { db: stack.db, registry: stack.registry, systemUser: admin },
166
+ ),
167
+ ).rejects.toThrow(/searchAdapter/);
168
+ });
169
+ });
@@ -1,4 +1,5 @@
1
- // Feature name
1
+ // @runtime client
2
+ // Feature name + qualified names for server handlers and web screens.
2
3
  export const JOBS_FEATURE = "jobs" as const;
3
4
 
4
5
  // Qualified write handler names (QN format: scope:type:name)
@@ -19,3 +20,9 @@ export const JobErrors = {
19
20
  notFound: "not_found",
20
21
  onlyFailedCanRetry: "only_failed_jobs_can_be_retried",
21
22
  } as const;
23
+
24
+ /** SystemAdmin job-runs list. Nav: `jobs:screen:job-runs`. */
25
+ export const JOB_RUNS_SCREEN_ID = "job-runs" as const;
26
+
27
+ /** Run detail — route entityId is the run uuid. Nav: `jobs:screen:job-run-detail`. */
28
+ export const JOB_RUN_DETAIL_SCREEN_ID = "job-run-detail" as const;
@@ -5,6 +5,7 @@ import {
5
5
  type FeatureDefinition,
6
6
  } from "@cosmicdrift/kumiko-framework/engine";
7
7
  import type { z } from "zod";
8
+ import { JOB_RUN_DETAIL_SCREEN_ID, JOB_RUNS_SCREEN_ID } from "./constants";
8
9
  // Event-payload schemas live in a sibling module so the logger can import
9
10
  // them without the cycle jobs-feature ↔ job-run-logger. The logger parses
10
11
  // payloads against these schemas before low-level append() — that's what
@@ -16,8 +17,11 @@ import {
16
17
  projectionRebuildJob,
17
18
  projectionRebuildPayloadSchema,
18
19
  } from "./handlers/projection-rebuild.job";
20
+ import { reindexEntityJob, reindexEntityPayloadSchema } from "./handlers/reindex-entity.job";
19
21
  import { retryWrite } from "./handlers/retry.write";
20
22
  import { triggerWrite } from "./handlers/trigger.write";
23
+ import { JOBS_I18N } from "./i18n";
24
+ import { parseJobInstant } from "./job-instant";
21
25
  import {
22
26
  JOB_RUN_COMPLETED_EVENT,
23
27
  JOB_RUN_FAILED_EVENT,
@@ -28,7 +32,7 @@ import { jobRunLogsTable, jobRunLogsTableMeta, jobRunsTable } from "./job-run-ta
28
32
  export function createJobsFeature(): FeatureDefinition {
29
33
  return defineFeature("jobs", (r) => {
30
34
  r.describe(
31
- "Persistence and operator tooling for background jobs registered via `r.job(...)`. Every job execution appends `run-started`, `run-completed`, and `run-failed` events to the `jobRun` aggregate stream, which two inline projections materialize into `read_job_runs` (current status + duration) and `read_job_run_logs` (per-line log rows). Exposes `jobs:write:trigger` (manual run) and `jobs:write:retry` (operator retry of a failed run), plus `jobs:query:list` and `jobs:query:details` for the operator UI.",
35
+ "Persistence and operator tooling for background jobs registered via `r.job(...)`. Every job execution appends `run-started`, `run-completed`, and `run-failed` events to the `jobRun` aggregate stream, which two inline projections materialize into `read_job_runs` (current status + duration) and `store_job_run_logs` (per-line log rows). Exposes `jobs:write:trigger` (manual run) and `jobs:write:retry` (operator retry of a failed run), plus `jobs:query:list` and `jobs:query:details` for the operator UI.",
32
36
  );
33
37
  r.uiHints({
34
38
  displayLabel: "Jobs · Audit & Operator UI",
@@ -36,7 +40,7 @@ export function createJobsFeature(): FeatureDefinition {
36
40
  recommended: false,
37
41
  });
38
42
  r.systemScope();
39
- r.unmanagedTable(jobRunLogsTableMeta, {
43
+ r.storeTable(jobRunLogsTableMeta, {
40
44
  reason: "read_side.job_run_logs",
41
45
  });
42
46
  // Events-only aggregate: "jobRun" has no r.entity registration, because
@@ -44,7 +48,12 @@ export function createJobsFeature(): FeatureDefinition {
44
48
  // (no executor, no CRUD). The boot-validator accepts the two
45
49
  // projections below because every apply-key is a registered
46
50
  // domain-event.
47
- r.defineEvent("run-started", runStartedSchema);
51
+ // payload can carry arbitrary user data; triggeredById stays plaintext
52
+ // (pseudonymous fk). System runs (triggeredById null) stay plaintext —
53
+ // no user subject to shred.
54
+ r.defineEvent("run-started", runStartedSchema, {
55
+ piiFields: { payload: { subjectField: "triggeredById" } },
56
+ });
48
57
  r.defineEvent("run-completed", runCompletedSchema);
49
58
  r.defineEvent("run-failed", runFailedSchema);
50
59
 
@@ -70,7 +79,7 @@ export function createJobsFeature(): FeatureDefinition {
70
79
  status: p.status,
71
80
  payload: p.payload,
72
81
  attempt: p.attempt,
73
- startedAt: Temporal.Instant.from(p.startedAt),
82
+ startedAt: parseJobInstant(p.startedAt),
74
83
  triggeredById: p.triggeredById,
75
84
  });
76
85
  },
@@ -84,7 +93,7 @@ export function createJobsFeature(): FeatureDefinition {
84
93
  {
85
94
  status: "completed",
86
95
  duration: p.duration,
87
- finishedAt: Temporal.Instant.from(p.finishedAt),
96
+ finishedAt: parseJobInstant(p.finishedAt),
88
97
  version: event.version,
89
98
  modifiedAt: event.createdAt,
90
99
  modifiedById: event.metadata?.userId ?? "system",
@@ -103,7 +112,7 @@ export function createJobsFeature(): FeatureDefinition {
103
112
  status: "failed",
104
113
  error: p.error,
105
114
  duration: p.duration,
106
- finishedAt: Temporal.Instant.from(p.finishedAt),
115
+ finishedAt: parseJobInstant(p.finishedAt),
107
116
  version: event.version,
108
117
  modifiedAt: event.createdAt,
109
118
  modifiedById: event.metadata?.userId ?? "system",
@@ -137,7 +146,7 @@ export function createJobsFeature(): FeatureDefinition {
137
146
  runId: event.aggregateId,
138
147
  level: log.level,
139
148
  message: log.message,
140
- timestamp: Temporal.Instant.from(log.timestamp),
149
+ timestamp: parseJobInstant(log.timestamp),
141
150
  })),
142
151
  );
143
152
  },
@@ -153,7 +162,7 @@ export function createJobsFeature(): FeatureDefinition {
153
162
  runId: event.aggregateId,
154
163
  level: log.level,
155
164
  message: log.message,
156
- timestamp: Temporal.Instant.from(log.timestamp),
165
+ timestamp: parseJobInstant(log.timestamp),
157
166
  })),
158
167
  );
159
168
  }),
@@ -167,6 +176,16 @@ export function createJobsFeature(): FeatureDefinition {
167
176
  projectionRebuildJob,
168
177
  );
169
178
 
179
+ // Retroactive search backfill (#1206/#1215) — manual + perTenant, so one
180
+ // `jobs:write:trigger` call with { entity } fans out to every active
181
+ // tenant (job-runner.ts perTenant dispatch applies to manual triggers
182
+ // too, not just cron).
183
+ r.job(
184
+ "reindexEntity",
185
+ { trigger: { manual: true }, perTenant: true, schema: reindexEntityPayloadSchema },
186
+ reindexEntityJob,
187
+ );
188
+
170
189
  const handlers = {
171
190
  trigger: r.writeHandler(triggerWrite),
172
191
  retry: r.writeHandler(retryWrite),
@@ -177,6 +196,31 @@ export function createJobsFeature(): FeatureDefinition {
177
196
  detail: r.queryHandler(detailQuery),
178
197
  };
179
198
 
199
+ const systemAdminAccess = { roles: ["SystemAdmin"] as const };
200
+
201
+ r.translations({ keys: JOBS_I18N });
202
+
203
+ r.screen({
204
+ id: JOB_RUNS_SCREEN_ID,
205
+ type: "custom",
206
+ renderer: { react: { __component: "JobRunsScreen" } },
207
+ access: systemAdminAccess,
208
+ });
209
+ r.screen({
210
+ id: JOB_RUN_DETAIL_SCREEN_ID,
211
+ type: "custom",
212
+ renderer: { react: { __component: "JobRunDetailScreen" } },
213
+ listScreenId: JOB_RUNS_SCREEN_ID,
214
+ access: systemAdminAccess,
215
+ });
216
+ r.nav({
217
+ id: "job-runs",
218
+ label: "jobs:nav.jobRuns",
219
+ icon: "list",
220
+ screen: "jobs:screen:job-runs",
221
+ order: 10,
222
+ });
223
+
180
224
  return { handlers, queries };
181
225
  });
182
226
  }
@@ -1,6 +1,7 @@
1
1
  import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { z } from "zod";
4
+ import { decryptStoredPii } from "../../shared";
4
5
  import { jobRunLogsTable, jobRunsTable } from "../job-run-table";
5
6
 
6
7
  export const detailQuery = defineQueryHandler({
@@ -18,6 +19,11 @@ export const detailQuery = defineQueryHandler({
18
19
 
19
20
  if (!row) return null;
20
21
 
22
+ // payload is stored encrypted under the triggering user's DEK (#799).
23
+ if (typeof row["payload"] === "string") {
24
+ row["payload"] = await decryptStoredPii(row["payload"], "payload", "job-run-detail");
25
+ }
26
+
21
27
  const logs = await selectMany(
22
28
  db,
23
29
  jobRunLogsTable,
@@ -1,8 +1,14 @@
1
1
  import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { z } from "zod";
4
+ import { decryptStoredPii } from "../../shared";
4
5
  import { jobRunsTable } from "../job-run-table";
5
6
 
7
+ async function decryptRunPayload<T extends Record<string, unknown>>(row: T): Promise<T> {
8
+ if (typeof row["payload"] !== "string") return row;
9
+ return { ...row, payload: await decryptStoredPii(row["payload"], "payload", "job-runs") };
10
+ }
11
+
6
12
  export const listQuery = defineQueryHandler({
7
13
  name: "list",
8
14
  schema: z.object({
@@ -19,6 +25,7 @@ export const listQuery = defineQueryHandler({
19
25
  orderBy: { col: "id", direction: "desc" },
20
26
  limit: query.payload.limit ?? 50,
21
27
  });
22
- return { rows };
28
+ // payload is stored encrypted under the triggering user's DEK (#799).
29
+ return { rows: await Promise.all(rows.map(decryptRunPayload)) };
23
30
  },
24
31
  });
@@ -6,10 +6,15 @@ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
6
6
  import { rebuildProjection } from "@cosmicdrift/kumiko-framework/pipeline";
7
7
  import { z } from "zod";
8
8
 
9
- export const projectionRebuildPayloadSchema = z.object({ projection: z.string().min(1) });
9
+ export const projectionRebuildPayloadSchema = z.object({
10
+ projection: z.string().min(1),
11
+ // Quarantine mode (#760): skip + dead-letter poison events instead of
12
+ // failing the whole rebuild. Operator opt-in per run.
13
+ skipApplyErrors: z.boolean().optional(),
14
+ });
10
15
 
11
16
  export const projectionRebuildJob: JobHandlerFn = async (rawPayload, ctx): Promise<void> => {
12
- const { projection } = projectionRebuildPayloadSchema.parse(rawPayload);
17
+ const { projection, skipApplyErrors } = projectionRebuildPayloadSchema.parse(rawPayload);
13
18
  if (!ctx.db) {
14
19
  throw new InternalError({
15
20
  message:
@@ -23,8 +28,13 @@ export const projectionRebuildJob: JobHandlerFn = async (rawPayload, ctx): Promi
23
28
  });
24
29
  }
25
30
  const db = ctx.db as DbConnection; // @cast-boundary db-operator
26
- const result = await rebuildProjection(projection, { db, registry: ctx.registry });
31
+ const result = await rebuildProjection(projection, {
32
+ db,
33
+ registry: ctx.registry,
34
+ ...(skipApplyErrors === true && { errorPolicy: { skipApplyErrors: true } }),
35
+ });
27
36
  ctx.log?.info?.(
28
- `[jobs:projection-rebuild] rebuilt ${projection}: ${result.eventsProcessed} events in ${result.durationMs}ms`,
37
+ `[jobs:projection-rebuild] rebuilt ${projection}: ${result.eventsProcessed} events` +
38
+ `${result.eventsSkipped > 0 ? ` (${result.eventsSkipped} quarantined)` : ""} in ${result.durationMs}ms`,
29
39
  );
30
40
  };
@@ -0,0 +1,49 @@
1
+ // Manual, perTenant backfill job (#1206/#1215) — indexes existing rows for
2
+ // an entity that only got `searchable: true` after rows already existed.
3
+ // perTenant fan-out (job-runner.ts) also applies to a manual dispatch, not
4
+ // just cron: one `jobs:write:trigger` call with { entity } re-runs once per
5
+ // active tenant.
6
+
7
+ import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
8
+ import type { JobHandlerFn } from "@cosmicdrift/kumiko-framework/engine";
9
+ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
10
+ import { reindexEntity } from "@cosmicdrift/kumiko-framework/search";
11
+ import { z } from "zod";
12
+
13
+ export const reindexEntityPayloadSchema = z.object({
14
+ entity: z.string().min(1),
15
+ });
16
+
17
+ export const reindexEntityJob: JobHandlerFn = async (rawPayload, ctx): Promise<void> => {
18
+ const { entity } = reindexEntityPayloadSchema.parse(rawPayload);
19
+ if (!ctx.db) {
20
+ throw new InternalError({
21
+ message: "[jobs:reindex-entity] ctx.db missing — job context requires a database connection.",
22
+ });
23
+ }
24
+ if (!ctx.registry) {
25
+ throw new InternalError({
26
+ message: "[jobs:reindex-entity] ctx.registry missing — job context requires the registry.",
27
+ });
28
+ }
29
+ if (!ctx.searchAdapter) {
30
+ throw new InternalError({
31
+ message:
32
+ "[jobs:reindex-entity] ctx.searchAdapter missing — mount a search adapter to run this job.",
33
+ });
34
+ }
35
+ // perTenant fan-out — the job-runner enqueues one child run per active
36
+ // tenant with _tenantId set; a run without one (e.g. misconfigured
37
+ // manual dispatch outside the fan-out) has nothing scoped to reindex.
38
+ const tenantId = ctx.systemUser?.tenantId ?? ctx._tenantId;
39
+ if (tenantId === undefined) {
40
+ // skip: fired without a perTenant fan-out tenant — nothing scoped to reindex
41
+ return;
42
+ }
43
+ const db = ctx.db as DbConnection; // @cast-boundary db-operator
44
+ const result = await reindexEntity(db, ctx.registry, ctx.searchAdapter, entity, tenantId);
45
+ ctx.log?.info?.(
46
+ `[jobs:reindex-entity] tenant=${tenantId} reindexed ${entity}: ${result.indexedRows}/${result.scannedRows} rows indexed` +
47
+ `${result.failures.length > 0 ? ` (${result.failures.length} failed)` : ""}`,
48
+ );
49
+ };
@@ -0,0 +1,37 @@
1
+ // @runtime client
2
+ // Server + client i18n for jobs operator screens.
3
+
4
+ type LocalizedString = { readonly de: string; readonly en: string };
5
+
6
+ export const JOBS_I18N: Readonly<Record<string, LocalizedString>> = {
7
+ "screen:job-runs.title": { de: "Job-Läufe", en: "Job runs" },
8
+ "screen:job-run-detail.title": { de: "Job-Lauf", en: "Job run" },
9
+ "jobs:nav.jobRuns": { de: "Jobs", en: "Jobs" },
10
+ "jobs.runs.title": { de: "Job-Läufe", en: "Job runs" },
11
+ "jobs.runs.loading": { de: "Lade Läufe…", en: "Loading runs…" },
12
+ "jobs.runs.empty": { de: "Keine Job-Läufe.", en: "No job runs." },
13
+ "jobs.runs.open": { de: "Details", en: "Details" },
14
+ "jobs.runs.filter.status": { de: "Status", en: "Status" },
15
+ "jobs.runs.filter.all": { de: "Alle", en: "All" },
16
+ "jobs.runs.filter.completed": { de: "Abgeschlossen", en: "Completed" },
17
+ "jobs.runs.filter.failed": { de: "Fehlgeschlagen", en: "Failed" },
18
+ "jobs.runs.filter.running": { de: "Läuft", en: "Running" },
19
+ "jobs.runs.filter.queued": { de: "Wartend", en: "Queued" },
20
+ "jobs.runs.col.job": { de: "Job", en: "Job" },
21
+ "jobs.runs.col.status": { de: "Status", en: "Status" },
22
+ "jobs.runs.col.started": { de: "Gestartet", en: "Started" },
23
+ "jobs.runs.col.duration": { de: "Dauer (ms)", en: "Duration (ms)" },
24
+ "jobs.detail.loading": { de: "Lade Details…", en: "Loading details…" },
25
+ "jobs.detail.missing": { de: "Lauf nicht gefunden.", en: "Run not found." },
26
+ "jobs.detail.field.job": { de: "Job", en: "Job" },
27
+ "jobs.detail.field.status": { de: "Status", en: "Status" },
28
+ "jobs.detail.field.id": { de: "Run-ID", en: "Run ID" },
29
+ "jobs.detail.field.started": { de: "Gestartet", en: "Started" },
30
+ "jobs.detail.field.finished": { de: "Beendet", en: "Finished" },
31
+ "jobs.detail.field.duration": { de: "Dauer (ms)", en: "Duration (ms)" },
32
+ "jobs.detail.field.error": { de: "Fehler", en: "Error" },
33
+ "jobs.detail.logs": { de: "Logs", en: "Logs" },
34
+ "jobs.detail.logs.empty": { de: "Keine Log-Zeilen.", en: "No log lines." },
35
+ "jobs.detail.retry": { de: "Erneut ausführen", en: "Retry" },
36
+ "jobs.detail.retrying": { de: "Wird erneut gestartet…", en: "Retrying…" },
37
+ };
@@ -0,0 +1,9 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+
3
+ // kumiko-framework#1525: the five value-position Temporal.Instant.from
4
+ // call sites in feature.ts live inside defineApply callbacks with no
5
+ // exported seam, so this thin wrapper gives the ambient-global-independence
6
+ // regression test something to import directly.
7
+ export function parseJobInstant(iso: string): Temporal.Instant {
8
+ return Temporal.Instant.from(iso);
9
+ }
@@ -45,10 +45,10 @@ export const jobRunEntity = createEntity({
45
45
  status: createTextField({ required: true }),
46
46
  payload: createTextField(),
47
47
  error: createTextField(),
48
- attempt: createNumberField({ required: true, default: 1 }),
48
+ attempt: createNumberField({ required: true, default: 1, integer: true }),
49
49
  startedAt: createTimestampField({ required: true }),
50
50
  finishedAt: createTimestampField(),
51
- duration: createNumberField(),
51
+ duration: createNumberField({ integer: true }),
52
52
  triggeredById: createTextField(),
53
53
  },
54
54
  });
@@ -60,7 +60,7 @@ export const jobRunsTable = buildEntityTable("job-run", jobRunEntity);
60
60
  // uuid of the parent jobRun. Existing detail-query callers treat it as an
61
61
  // opaque identifier, so the type-switch is backward-compatible at the
62
62
  // query surface.
63
- export const jobRunLogsTable = pgTable("read_job_run_logs", {
63
+ export const jobRunLogsTable = pgTable("store_job_run_logs", {
64
64
  id: serial("id").primaryKey(),
65
65
  runId: text("run_id").notNull(),
66
66
  level: text("level").notNull().$type<JobLogLevel>(),
@@ -76,7 +76,7 @@ export const jobRunLogsTable = pgTable("read_job_run_logs", {
76
76
  // pgTable bleibt source-of-truth für Query-API; Phase 4 leitet das pgTable
77
77
  // aus dieser Meta ab.
78
78
  export const jobRunLogsTableMeta: EntityTableMeta = defineUnmanagedTable({
79
- tableName: "read_job_run_logs",
79
+ tableName: "store_job_run_logs",
80
80
  columns: [
81
81
  { name: "id", pgType: "serial", notNull: true, primaryKey: true },
82
82
  { name: "run_id", pgType: "text", notNull: true },
@@ -0,0 +1,22 @@
1
+ // @runtime client
2
+ import { mergeTranslations, type TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
3
+ import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer-web";
4
+ import { JOB_RUN_DETAIL_SCREEN_ID, JOB_RUNS_SCREEN_ID, JOBS_FEATURE } from "../constants";
5
+ import { defaultTranslations } from "./i18n";
6
+ import { JobRunDetailScreen } from "./job-run-detail-screen";
7
+ import { JobRunsScreen } from "./job-runs-screen";
8
+
9
+ export type JobsClientOptions = {
10
+ readonly translations?: TranslationsByLocale;
11
+ };
12
+
13
+ export function jobsClient(options?: JobsClientOptions): ClientFeatureDefinition {
14
+ return {
15
+ name: JOBS_FEATURE,
16
+ translations: mergeTranslations(defaultTranslations, options?.translations ?? {}),
17
+ components: {
18
+ [JOB_RUNS_SCREEN_ID]: JobRunsScreen,
19
+ [JOB_RUN_DETAIL_SCREEN_ID]: JobRunDetailScreen,
20
+ },
21
+ };
22
+ }