@cosmicdrift/kumiko-samples 0.386.0 → 0.387.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 (394) hide show
  1. package/LICENSE +21 -57
  2. package/README.md +1 -1
  3. package/package.json +2 -2
  4. package/packages/bundled-features/package.json +2 -2
  5. package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
  6. package/packages/bundled-features/src/audit/screens.ts +1 -1
  7. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
  8. package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
  9. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
  10. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  11. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
  12. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
  13. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
  14. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
  16. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
  17. package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
  18. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
  19. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
  20. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
  21. package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
  22. package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
  23. package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
  24. package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
  25. package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
  26. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
  27. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
  28. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
  29. package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
  30. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
  31. package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
  32. package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
  33. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
  34. package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
  35. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
  36. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
  37. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
  38. package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
  39. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
  40. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
  41. package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
  42. package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
  43. package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
  44. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
  45. package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
  46. package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
  47. package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
  48. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
  49. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
  50. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
  51. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  52. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
  53. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
  54. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
  55. package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
  56. package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
  57. package/packages/bundled-features/src/auth-mfa/web/i18n.ts +3 -3
  58. package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
  59. package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
  60. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +6 -6
  61. package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
  62. package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
  63. package/packages/bundled-features/src/billing-foundation/events.ts +22 -21
  64. package/packages/bundled-features/src/billing-foundation/feature.ts +42 -43
  65. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
  66. package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +18 -17
  67. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
  68. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
  69. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +20 -20
  70. package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
  71. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
  72. package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
  73. package/packages/bundled-features/src/billing-foundation/types.ts +64 -65
  74. package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +4 -4
  75. package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
  76. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
  77. package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
  78. package/packages/bundled-features/src/cap-counter/enforce-cap.ts +64 -67
  79. package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
  80. package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
  81. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
  82. package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
  83. package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +1 -1
  84. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
  85. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
  86. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  87. package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
  88. package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
  89. package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
  90. package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
  91. package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
  92. package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
  93. package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
  94. package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
  95. package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
  96. package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
  97. package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
  98. package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
  99. package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
  100. package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
  101. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
  102. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  103. package/packages/bundled-features/src/config/resolver.ts +4 -4
  104. package/packages/bundled-features/src/config/table.ts +10 -10
  105. package/packages/bundled-features/src/config/write-helpers.ts +1 -1
  106. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +13 -12
  107. package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
  108. package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
  109. package/packages/bundled-features/src/custom-fields/events.ts +7 -8
  110. package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
  111. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
  112. package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
  113. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
  114. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
  115. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
  116. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
  117. package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
  118. package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
  119. package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
  120. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
  121. package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
  122. package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
  123. package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
  124. package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
  125. package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
  126. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
  127. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
  128. package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
  129. package/packages/bundled-features/src/data-retention/feature.ts +11 -18
  130. package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
  131. package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
  132. package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
  133. package/packages/bundled-features/src/data-retention/presets.ts +18 -23
  134. package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
  135. package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
  136. package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
  137. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
  138. package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
  139. package/packages/bundled-features/src/delivery/feature.ts +3 -3
  140. package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
  141. package/packages/bundled-features/src/delivery/tables.ts +9 -8
  142. package/packages/bundled-features/src/delivery/unsubscribe.ts +4 -4
  143. package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
  144. package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
  145. package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
  146. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
  147. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
  148. package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
  149. package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
  150. package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
  151. package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
  152. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
  153. package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
  154. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  155. package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
  156. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  157. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  158. package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
  159. package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
  160. package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
  161. package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
  162. package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
  163. package/packages/bundled-features/src/files/feature.ts +6 -6
  164. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
  165. package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
  166. package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
  167. package/packages/bundled-features/src/folders/feature.ts +1 -1
  168. package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
  169. package/packages/bundled-features/src/folders/web/tree.ts +5 -5
  170. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +1 -1
  171. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
  172. package/packages/bundled-features/src/form-draft/entity.ts +3 -3
  173. package/packages/bundled-features/src/form-draft/feature.ts +4 -5
  174. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
  175. package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
  176. package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
  177. package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
  178. package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
  179. package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
  180. package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
  181. package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
  182. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
  183. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
  184. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
  185. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
  186. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
  187. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
  188. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
  189. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
  190. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
  191. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +11 -11
  192. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
  193. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
  194. package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
  195. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
  196. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
  197. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +66 -67
  198. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +69 -68
  199. package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
  200. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
  201. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
  202. package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
  203. package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
  204. package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
  205. package/packages/bundled-features/src/jobs/events.ts +1 -1
  206. package/packages/bundled-features/src/jobs/feature.ts +6 -7
  207. package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
  208. package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
  209. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  210. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
  211. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  212. package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
  213. package/packages/bundled-features/src/jobs/job-run-logger.ts +14 -15
  214. package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
  215. package/packages/bundled-features/src/jobs/screens.ts +1 -1
  216. package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
  217. package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
  218. package/packages/bundled-features/src/ledger/entity.ts +5 -5
  219. package/packages/bundled-features/src/ledger/feature.ts +3 -3
  220. package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
  221. package/packages/bundled-features/src/ledger/recurring.ts +12 -12
  222. package/packages/bundled-features/src/ledger/schemas.ts +1 -1
  223. package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
  224. package/packages/bundled-features/src/ledger/web/index.ts +7 -7
  225. package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
  226. package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
  227. package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
  228. package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
  229. package/packages/bundled-features/src/mail-foundation/feature.ts +26 -27
  230. package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
  231. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
  232. package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
  233. package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
  234. package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
  235. package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
  236. package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
  237. package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
  238. package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
  239. package/packages/bundled-features/src/managed-pages/table.ts +16 -15
  240. package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
  241. package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
  242. package/packages/bundled-features/src/notes-history/entity.ts +4 -4
  243. package/packages/bundled-features/src/notes-history/feature.ts +2 -2
  244. package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
  245. package/packages/bundled-features/src/page-render/layout.ts +9 -9
  246. package/packages/bundled-features/src/page-render/markdown.ts +11 -11
  247. package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
  248. package/packages/bundled-features/src/personal-access-tokens/feature.ts +5 -5
  249. package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
  250. package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
  251. package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
  252. package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
  253. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  254. package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
  255. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  256. package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
  257. package/packages/bundled-features/src/seo/feature.ts +3 -3
  258. package/packages/bundled-features/src/sessions/constants.ts +1 -1
  259. package/packages/bundled-features/src/sessions/feature.ts +9 -9
  260. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
  261. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
  262. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  263. package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
  264. package/packages/bundled-features/src/sessions/screens.ts +2 -2
  265. package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
  266. package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
  267. package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
  268. package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +1 -1
  269. package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
  270. package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
  271. package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
  272. package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
  273. package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
  274. package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
  275. package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
  276. package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
  277. package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
  278. package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
  279. package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
  280. package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
  281. package/packages/bundled-features/src/subscription-stripe/feature.ts +44 -43
  282. package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
  283. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
  284. package/packages/bundled-features/src/subscription-stripe/runtime.ts +2 -2
  285. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
  286. package/packages/bundled-features/src/tags/feature.ts +3 -3
  287. package/packages/bundled-features/src/tags/screens.ts +1 -1
  288. package/packages/bundled-features/src/template-resolver/api.ts +10 -10
  289. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +5 -5
  290. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +1 -1
  291. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
  292. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
  293. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
  294. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
  295. package/packages/bundled-features/src/template-resolver/seeding.ts +4 -4
  296. package/packages/bundled-features/src/template-resolver/table.ts +13 -14
  297. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
  298. package/packages/bundled-features/src/tenant/feature.ts +7 -7
  299. package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +1 -1
  300. package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
  301. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  302. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
  303. package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +1 -1
  304. package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
  305. package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
  306. package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
  307. package/packages/bundled-features/src/tenant/screens.ts +1 -1
  308. package/packages/bundled-features/src/tenant/seeding.ts +19 -18
  309. package/packages/bundled-features/src/tenant/testing.ts +5 -6
  310. package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
  311. package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
  312. package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
  313. package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
  314. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  315. package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
  316. package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
  317. package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
  318. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  319. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
  320. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +2 -2
  321. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
  322. package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
  323. package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
  324. package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
  325. package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
  326. package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
  327. package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
  328. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
  329. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
  330. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -8
  331. package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
  332. package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
  333. package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
  334. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
  335. package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
  336. package/packages/bundled-features/src/user/schema/user.ts +45 -44
  337. package/packages/bundled-features/src/user/seeding.ts +40 -40
  338. package/packages/bundled-features/src/user/testing.ts +3 -3
  339. package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
  340. package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
  341. package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
  342. package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
  343. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
  344. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
  345. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
  346. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
  347. package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
  348. package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
  349. package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
  350. package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
  351. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
  352. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
  353. package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
  354. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
  355. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
  356. package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
  357. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
  358. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
  359. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +166 -276
  360. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +85 -107
  361. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
  362. package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
  363. package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
  364. package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
  365. package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
  366. package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
  367. package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
  368. package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
  369. package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
  370. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
  371. package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
  372. package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
  373. package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
  374. package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
  375. package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
  376. package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
  377. package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
  378. package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
  379. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
  380. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
  381. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
  382. package/packages/bundled-features/src/user-profile/index.ts +3 -3
  383. package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
  384. package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
  385. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
  386. package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
  387. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
  388. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
  389. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  390. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
  391. package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
  392. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
  393. package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
  394. package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
@@ -1,30 +1,29 @@
1
- // GET /api/query → user-data-rights:query:download-by-token (S2.U3 Atom 4b).
1
+ // GET /api/query → user-data-rights:query:download-by-token.
2
2
  //
3
- // **Magic-Link-Pfad** (anonymous): User klickt Email-Link mit
4
- // `#token=<plain>` (URL fragment, issue #1271). Worker hat beim done-flip Token-Hash in DB
5
- // persistiert (Atom 4a). Verify-Pipeline:
3
+ // **Magic-link path** (anonymous): The user clicks an email link with
4
+ // `#token=<plain>` (URL fragment). The worker persisted the token hash in the database
5
+ // when marking the job done. Verification:
6
6
  //
7
7
  // 1. hashDownloadToken(plain) → fetchOne in download-tokens
8
- // 2. expiresAt > now (Multi-use within TTL — Plan-Decision)
8
+ // 2. expiresAt > now (multiple uses within the TTL)
9
9
  // 3. job.status === "done"
10
- // 4. job.downloadStorageKey != null (storage nicht gecleared)
11
- // 5. provider.getSignedUrl pflicht (501 wenn Provider local-fs ist)
12
- // 6. Audit-Update: useCount + 1, lastUsedAt, IP, UA (best-effort, race-tolerant)
10
+ // 4. job.downloadStorageKey != null (storage was not cleared)
11
+ // 5. provider.getSignedUrl is required (501 when the provider is local-fs)
12
+ // 6. Audit update: useCount + 1, lastUsedAt, IP, UA (best-effort, race-tolerant)
13
13
  // 7. Return {url, expiresAt}
14
14
  //
15
- // **Sicherheit:**
16
- // - Token-Hash-Compare via DB-fetchOne (nicht constant-time, aber
17
- // timing-attacks auf SHA256-bytes brauchen >>10k requests + stable
18
- // latenz — in Web-App-Kontext nicht ausnutzbar). Plan-Decision: harden
19
- // wenn Pen-Test es flaggt.
20
- // - 404 bei invalidem Token (kein Existenz-Leak) — gleicher Code-Pfad
21
- // wie nicht-gefundenes Token.
22
- // - tenant-agnostic: Token ist global eindeutig (UUID + SHA256), kein
23
- // Tenant-Filter nötig.
15
+ // **Security:**
16
+ // - Token-hash comparison via DB-fetchOne is not constant-time, but timing attacks
17
+ // against SHA256 bytes require >>10k requests plus stable latency and are not
18
+ // exploitable in a web-app context.
19
+ // - Invalid tokens return 404 without leaking existence, using the same code path
20
+ // as a token that was not found.
21
+ // - The token is tenant-agnostic: UUID + SHA256 makes it globally unique, so no
22
+ // tenant filter is needed.
24
23
  //
25
- // **r.httpRoute-Wrapper** (siehe feature.ts) macht 302-Redirect zu
26
- // signedUrl — User klickt 1× Email-Link, Browser folgt redirect, Download
27
- // startet. Dieser query-handler liefert nur das JSON.
24
+ // **r.httpRoute wrapper** (see feature.ts) redirects to signedUrl with a 302. The user
25
+ // clicks the email link once, the browser follows the redirect, and the download
26
+ // starts. This query handler returns only the JSON.
28
27
 
29
28
  import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
30
29
  import {
@@ -43,7 +42,7 @@ import { hashDownloadToken } from "../token-helpers.ts";
43
42
 
44
43
  type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
45
44
 
46
- const SIGNED_URL_TTL_SECONDS = 300; // 5 min — kurz genug fuer Replay-Schutz, lang genug fuer slow connections
45
+ const SIGNED_URL_TTL_SECONDS = 300; // 5 min — short enough to limit replay, long enough for slow connections
47
46
 
48
47
  const DOWNLOAD_BY_TOKEN_REASON =
49
48
  "the download token, its job and audit rows are keyed by token hash / job id, not the caller's tenant — the anonymous magic-link path has no tenant context at all";
@@ -82,12 +81,11 @@ export const downloadByTokenQuery = defineQueryHandler({
82
81
  personalData: "public-intake",
83
82
  },
84
83
  agent: { expose: false },
85
- // Brute-Force-Schutz fuer Token-Hash-Probing. Anonymous-Endpoint mit
86
- // 32-byte-Random-Token = 256 Bit Search-Space, aber rate-limit als
87
- // defense-in-depth + Schutz gegen Storm-Patterns die DB-Last erzeugen.
88
- // 30 attempts/min/IP reicht fuer legitime User (mehrere Klicks bei
89
- // Connection-Abbruch); blockiert automatisierte Probing-Loops.
90
- // Memory `feedback_security_default_on`.
84
+ // Brute-force protection for token-hash probing. This anonymous endpoint uses a
85
+ // 32-byte random token with a 256-bit search space, but rate limiting provides
86
+ // defense in depth and protects against storm patterns that generate database load.
87
+ // 30 attempts/min/IP allows legitimate users several retries after connection
88
+ // failures while blocking automated probing loops.
91
89
  rateLimit: { per: "ip+handler", limit: 30, windowSeconds: 60 },
92
90
  escapeHatch: {
93
91
  grants: ["unsafeRaw"],
@@ -105,9 +103,9 @@ export const downloadByTokenQuery = defineQueryHandler({
105
103
  });
106
104
 
107
105
  if (!tokenRow) {
108
- // Invalid token — 404 ohne Existenz-Leak. Generic NotFoundError
109
- // damit alle Failure-Pfade die gleiche externe Shape haben (kein
110
- // Probing zwischen "Token existiert nicht" vs "Job ist failed").
106
+ // Invalid token — 404 without leaking existence. Use a generic NotFoundError
107
+ // so all failure paths have the same external shape, preventing probing that
108
+ // distinguishes "Token does not exist" from "Job has failed".
111
109
  throw new NotFoundError("export-download", undefined, {
112
110
  i18nKey: "userDataRights.errors.download.notFound",
113
111
  });
@@ -116,17 +114,17 @@ export const downloadByTokenQuery = defineQueryHandler({
116
114
  const auditIp = query.payload.auditMeta?.ip ?? null;
117
115
  const auditUa = query.payload.auditMeta?.userAgent ?? null;
118
116
 
119
- // Step 2: TTL-check.
117
+ // Step 2: TTL check.
120
118
  //
121
- // **Pragma:** semantisch waere 410 Gone richtig (war mal da, jetzt
122
- // nicht mehr). Framework hat keine GoneError-Class; wir nutzen
123
- // NotFoundError + i18nKey "expired" als Kompromiss. UI rendert
124
- // anhand des i18nKeys, nicht des HTTP-Status — also User sieht
125
- // "Dein Download ist abgelaufen", nicht generic "not found".
119
+ // 410 Gone would be semantically right (the download existed and is now
120
+ // gone), but the framework has no GoneError class. NotFoundError with
121
+ // i18nKey "expired" is the compromise: the UI renders by i18nKey, not by
122
+ // HTTP status, so the user sees "Your download has expired" instead of a
123
+ // generic "not found".
126
124
  if (tokenRow.expiresAt.epochMilliseconds <= now.epochMilliseconds) {
127
- // Audit-Skip noch nicht moeglich — jobRow noch nicht geladen,
128
- // tenantId unbekannt. Wir laden den Job hier noch fuer Audit-Context
129
- // (best-effort — wenn Job auch fehlt, audit-skip ist akzeptabel).
125
+ // Auditing cannot be skipped yet because jobRow has not been loaded and the
126
+ // tenantId is unknown. Load the job to obtain audit context; if it is also
127
+ // missing, skipping the audit is acceptable (best-effort).
130
128
  const jobForAudit = await fetchOne<{ requestedFromTenantId: string }>(
131
129
  runner,
132
130
  exportJobsTable,
@@ -240,9 +238,9 @@ export const downloadByTokenQuery = defineQueryHandler({
240
238
  now.epochMilliseconds + SIGNED_URL_TTL_SECONDS * 1000,
241
239
  );
242
240
 
243
- // Step 6: Audit-Update best-effort. auditMeta kommt vom httpRoute-
244
- // Wrapper (trusted-source). Direct-API-caller koennen luegen, aber
245
- // Audit ist nicht security-relevant.
241
+ // Step 6: Best-effort audit update. auditMeta comes from the httpRoute wrapper
242
+ // (trusted source). Direct API callers can falsify it, but the audit data is not
243
+ // security-sensitive.
246
244
  await recordDownloadUse({
247
245
  db: runner,
248
246
  tokenId: tokenRow.id,
@@ -1,15 +1,14 @@
1
- // GET /api/user/export-status (S2.U3 Atom 2) — User-Polling.
1
+ // GET /api/user/export-status — user polling.
2
2
  //
3
- // Liefert den meist-aktuellen ExportJob des aufrufenden Users (in
4
- // Reihenfolge: aktiver Job zuerst, sonst neuester done/failed).
3
+ // Returns the calling user's most recent ExportJob (active jobs first,
4
+ // otherwise the newest done/failed job).
5
5
  //
6
- // **Cross-User-Isolation:** Filter ist `userId === query.user.id` — kein
7
- // User kann fremde Job-Status sehen, auch nicht via ID-Guess. Pre-Check
8
- // uses ctx.db.unsafeRaw — ExportJob is tenant-agnostic (see plan doc,
9
- // "Cross-Tenant-Semantik").
6
+ // **Cross-User-Isolation:** The filter is `userId === query.user.id`, so no
7
+ // user can view another user's job status, even by guessing an ID. The
8
+ // pre-check uses ctx.db.unsafeRaw because ExportJob is tenant-agnostic.
10
9
  //
11
- // **Read-Only-Endpoint:** Pollt nur, kein State-Flip. Idempotent + cache-
12
- // fest. UI poll-Intervall typisch 2-5s waehrend running.
10
+ // **Read-Only-Endpoint:** Polling only; it does not change state. The endpoint
11
+ // is idempotent and cacheable. The UI typically polls every 2-5s while running.
13
12
 
14
13
  import { selectMany } from "@cosmicdrift/kumiko-framework/db";
15
14
  import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
@@ -8,7 +8,7 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle.ts";
8
8
  const LIFT_RESTRICTION_ESCAPE_HATCH_REASON =
9
9
  "checks the target user's membership in the admin's tenant and appends the user lifecycle status change on the SYSTEM_TENANT_ID user stream, both via DbRunner helpers outside the admin's own tenant scope.";
10
10
 
11
- // POST /api/user/lift-restriction (S2.U6) — DSGVO Art. 18 Reverse.
11
+ // POST /api/user/lift-restriction — GDPR Art. 18 Reverse.
12
12
  //
13
13
  // Operator-only. A Restricted user's own session is unconditionally
14
14
  // rejected by sessionChecker (BLOCKED_STATUSES) the moment their status
@@ -16,16 +16,15 @@ const LIFT_RESTRICTION_ESCAPE_HATCH_REASON =
16
16
  // endpoint. There is no self-service path today, so lifting a restriction
17
17
  // always targets someone else's account by id.
18
18
  //
19
- // Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even
20
- // though the User-entity is global — see user-data-rights.md "Cross-Tenant-Semantik". Without a membership check, a TenantAdmin
21
- // from tenant A could unrestrict/reactivate a user who has never been a
22
- // member of tenant A. Only SystemAdmin (platform-wide) skips the check;
23
- // the target must have a membership row in the acting admin's
24
- // `event.user.tenantId` (row existence only — no active-status field).
19
+ // Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even though the User-entity is
20
+ // global — see user-data-rights.md "Cross-Tenant Semantics". Without a membership check, a
21
+ // TenantAdmin from tenant A could unrestrict/reactivate a user who has never been a member of
22
+ // tenant A. Only SystemAdmin (platform-wide) skips the check; the target must have a membership row
23
+ // in the acting admin's `event.user.tenantId` (row existence only — no active-status field).
25
24
  //
26
25
  // State-Transitions:
27
26
  // Restricted → Active ✓
28
- // Active → ... ✗ 422 not_restricted (Idempotenz-Guard)
27
+ // Active → ... ✗ 422 not_restricted (Idempotency Guard)
29
28
  // DeletionRequested → ... ✗ 422 not_restricted
30
29
  // Deleted → ... ✗ 422 not_restricted
31
30
  export const liftRestrictionWrite = defineWriteHandler({
@@ -5,8 +5,8 @@ import * as z from "zod";
5
5
  import { decryptStoredPii } from "../../shared/index.ts";
6
6
  import { downloadAttemptsTable } from "../schema/download-attempt.ts";
7
7
 
8
- // Operator-Query: invalid Download-Attempts (S2.U7).
9
- // DPO-Sicht fuer Brute-Force-Detection. Tenant-isolated via WHERE.
8
+ // Operator query for invalid download attempts. Provides tenant-isolated visibility for data
9
+ // protection officers investigating brute-force probing.
10
10
 
11
11
  const MAX_LIMIT = 100;
12
12
  const REQUEST_ID = "user-data-rights:list-download-attempts";
@@ -5,10 +5,10 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
5
5
  import * as z from "zod";
6
6
  import { redactPiiCiphertext } from "../../shared/index.ts";
7
7
 
8
- // DSGVO Art. 15 Selbstauskunft — User reads HIS OWN audit-log.
9
- // WHERE createdBy = ctx.user.id ist hard-coded (kein userId-Param,
10
- // anti-cross-user-Snooping). KEIN tenantId-Filter: User hat Anspruch
11
- // auf account-weite Sicht ueber alle Memberships — analog Forget-Pfad.
8
+ // GDPR Art. 15 self-disclosure: users may read only their own audit log.
9
+ // `createdBy = ctx.user.id` is hard-coded to prevent cross-user snooping;
10
+ // no `tenantId` filter is applied because the user is entitled to an
11
+ // account-wide view across all memberships, as in the forget path.
12
12
  const MAX_LIMIT = 100;
13
13
 
14
14
  const MY_AUDIT_LOG_REASON =
@@ -4,20 +4,20 @@ import { USER_STATUS, userTable } from "../../user/index.ts";
4
4
  import { signDeletionToken } from "../deletion-token.ts";
5
5
  import { updateUserLifecycle } from "../lib/update-user-lifecycle.ts";
6
6
 
7
- // TTL des Verify-Links. 60 min — lang genug für einen Mail-Roundtrip,
8
- // kurz genug dass ein abgefangener Link nicht ewig gültig ist.
7
+ // Verification-link TTL: 60 minutes — long enough for an email round trip,
8
+ // but short enough that an intercepted link does not remain valid indefinitely.
9
9
  const DELETION_VERIFY_TTL_MINUTES = 60;
10
10
 
11
- // Apex-Magic-Link-Versand für den anonymen Deletion-Request. Der Handler
12
- // gibt den Link NICHT zurück (content-Enumeration-Safety) — er ruft den
13
- // Callback direkt, analog sendDeletionRequestedEmail.
11
+ // Sends the Apex magic link for an anonymous deletion request. The handler
12
+ // does NOT return the link (content-enumeration safety); it invokes the
13
+ // callback directly, analogous to sendDeletionRequestedEmail.
14
14
  //
15
- // WICHTIG (Timing-Oracle): Der Callback MUSS non-blocking sein (enqueue, z.B.
16
- // delivery.notify / Job), NICHT synchron senden. Er läuft nur im
17
- // existierenden-aktiven-User-Pfad; ein synchroner Send macht die
18
- // Response-Latenz für reale Accounts messbar länger als für nicht-existente
19
- // → Enumeration über Timing. Ein schnelles enqueue gleicht das praktisch aus.
20
- // Vollständige Timing-Angleichung (immer äquivalente Arbeit) ist v1-Follow-up.
15
+ // IMPORTANT (timing oracle): The callback MUST be non-blocking (enqueue, e.g.
16
+ // delivery.notify / Job), not send synchronously. It runs only for an
17
+ // existing active user; a synchronous send makes response latency measurably
18
+ // longer for real accounts than for nonexistent ones, enabling enumeration by
19
+ // timing. A fast enqueue effectively equalizes this.
20
+ // Complete timing equalization (always performing equivalent work) is deferred.
21
21
  export type SendDeletionVerificationEmailFn = (args: {
22
22
  readonly email: string;
23
23
  readonly verifyUrl: string;
@@ -25,12 +25,12 @@ export type SendDeletionVerificationEmailFn = (args: {
25
25
  }) => Promise<void>;
26
26
 
27
27
  export type RequestDeletionByEmailOptions = {
28
- /** HMAC-Secret zum Signieren des Verify-Tokens. Ohne Secret ist der Flow
29
- * deaktiviert (Handler antwortet still mit success, kein Link). */
28
+ /** HMAC secret used to sign the verification token. Without a secret, the flow
29
+ * is disabled (the handler silently responds with success and no link). */
30
30
  readonly deletionTokenSecret?: string;
31
- /** Basis-URL des Apex-Confirm-Screens, z.B.
32
- * "https://app.example.com/delete-account/confirm". Der Handler hängt das
33
- * Token als URL-Fragment an (`#token=<token>`, fw#1554). Ohne URL kein Link. */
31
+ /** Base URL of the Apex confirmation screen, e.g.
32
+ * "https://app.example.com/delete-account/confirm". The handler appends the
33
+ * token as a URL fragment (`#token=<token>`). Without a URL, no link is sent. */
34
34
  readonly deletionVerifyUrl?: string;
35
35
  readonly sendDeletionVerificationEmail?: SendDeletionVerificationEmailFn;
36
36
  };
@@ -38,7 +38,7 @@ export type RequestDeletionByEmailOptions = {
38
38
  // Token goes in the URL fragment, not a query param — fragments never leave
39
39
  // the browser (not sent to the server, so they never land in proxy/access
40
40
  // logs, unlike `?token=`). Same convention as the export-download link
41
- // (feature.ts, issue #1271). Preserves any existing query params on `base`
41
+ // (feature.ts). Preserves any existing query params on `base`
42
42
  // (`?lang=de` stays a query param; only the token is a fragment).
43
43
  const APPEND_LIFECYCLE_EVENT_REASON =
44
44
  "appends the user lifecycle event on the SYSTEM_TENANT_ID user stream";
@@ -49,14 +49,14 @@ export function buildDeletionVerifyUrl(base: string, token: string): string {
49
49
  return url.toString();
50
50
  }
51
51
 
52
- // Anonymer Apex-Flow Schritt 1: "Account-Löschung beantragen" per Email.
53
- // DSGVO-relevant gerade wenn der User sich NICHT mehr einloggen kann
54
- // (Lockout). Email → Magic-Link → confirm-deletion-by-token.
52
+ // Anonymous Apex flow step 1: request account deletion by email.
53
+ // This is especially relevant for GDPR when the user can no longer log in
54
+ // (lockout). Email → magic link → confirm-deletion-by-token.
55
55
  //
56
- // Enumeration-safe (content): antwortet IMMER mit derselben success-Shape,
57
- // egal ob die Email existiert, der User aktiv ist oder der Flow konfiguriert
58
- // ist. Ein Link wird nur für einen existierenden, aktiven User generiert.
59
- // Timing-Safety hängt am non-blocking Callback (siehe Type-Doc oben).
56
+ // Enumeration-safe (content): always returns the same success shape,
57
+ // regardless of whether the email exists, the user is active, or the flow is
58
+ // configured. A link is generated only for an existing, active user.
59
+ // Timing safety depends on the non-blocking callback (see the type doc above).
60
60
  export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmailOptions = {}) {
61
61
  return defineWriteHandler({
62
62
  name: "request-deletion-by-email",
@@ -66,7 +66,7 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
66
66
  "Mails an account-deletion verification link to the given address, for a user who wants to be erased but can no longer log in; it answers identically whether or not the address belongs to an active account and never returns the link.",
67
67
  // Mails a deletion link to any address the caller names.
68
68
  agent: { risk: "high" },
69
- // Defense-in-depth gegen Email-Probing auf dem anonymen Endpoint.
69
+ // Defense in depth against email probing on the anonymous endpoint.
70
70
  rateLimit: { per: "ip+handler", limit: 10, windowSeconds: 60 },
71
71
  escapeHatch: {
72
72
  grants: ["unsafeRaw", "globalWrites"],
@@ -75,7 +75,8 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
75
75
  handler: async (event, ctx) => {
76
76
  const success = { isSuccess: true as const, data: { kind: "requested" as const } };
77
77
 
78
- // not-configured-safe: ohne Secret/URL kein Link, aber gleiche Antwort.
78
+ // Safe when not configured: without a secret or URL, no link is sent, but the response is
79
+ // unchanged.
79
80
  if (!opts.deletionTokenSecret || !opts.deletionVerifyUrl) return success;
80
81
 
81
82
  const userRow = await ctx.db
@@ -88,9 +89,9 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
88
89
  return success;
89
90
  }
90
91
 
91
- // Replay-Schutz (#354/1): pro Antrag eine frische requestId, die auf der
92
- // user-Row landet und in die Token-HMAC-Purpose gefaltet wird. cancel
93
- // nullt sie → ein nach Cancel nachgespieltes Token verifiziert nicht mehr.
92
+ // Replay protection: each request gets a fresh requestId, which is stored on
93
+ // the user row and incorporated into the token HMAC purpose. cancel
94
+ // nulls it, so a token replayed after cancellation no longer verifies.
94
95
  const requestId = crypto.randomUUID();
95
96
  await updateUserLifecycle(ctx.db.unsafeRaw(), userRow["id"], {
96
97
  pendingDeletionRequestId: requestId,
@@ -9,13 +9,12 @@ import {
9
9
  } from "../lib/default-mailers.ts";
10
10
  import { resolveGracePeriod, startDeletionGracePeriod } from "./deletion-grace-period.ts";
11
11
 
12
- // Atom 5b — Email-Notification beim deletion-requested-flip. Pattern:
13
- // password-reset-Callback aus auth-routes.ts. Best-effort — Throw beim
14
- // Send wird gefangen + per console.warn geloggt, der Status-Flip selbst
15
- // bleibt erfolgreich. Reasoning: user-Aktion ist abgeschlossen sobald
16
- // die DB-Row geflipt ist; Email-Versand ist Beleg, keine Vorbedingung.
17
- // Wenn Email broken ist soll der User nicht erneut "Account löschen"
18
- // klicken muessen.
12
+ // Sends a best-effort email after the deletion-requested status flip, following the
13
+ // password-reset callback pattern in auth-routes.ts. Send failures are caught and logged
14
+ // with console.warn so the status change remains successful: the user's action is complete
15
+ // once the database row is updated, while email delivery is confirmation rather than a
16
+ // prerequisite. This prevents users from having to click "Delete account" again when
17
+ // email delivery fails.
19
18
  export type SendDeletionRequestedEmailFn = (args: {
20
19
  readonly userId: string;
21
20
  readonly userEmail: string;
@@ -28,16 +27,15 @@ export type SendDeletionRequestedEmailFn = (args: {
28
27
 
29
28
  export type RequestDeletionOptions = {
30
29
  readonly sendDeletionRequestedEmail?: SendDeletionRequestedEmailFn;
31
- /** Branding fuer die Default-Mail wenn kein sendDeletionRequestedEmail
32
- * gesetzt ist + mail-foundation gemountet ist. */
30
+ /** Branding for the default email when sendDeletionRequestedEmail is not set
31
+ * and mail-foundation is mounted. */
33
32
  readonly mailDefaults?: GdprMailDefaults;
34
33
  };
35
34
 
36
- // POST /api/user/request-deletion (S2.U5a) — DSGVO Art. 17 Forget-Antrag.
37
- // Flippt status=Active → deletionRequested, setzt gracePeriodEnd aus dem
38
- // Compliance-Profile (geteilte Logik: startDeletionGracePeriod). Account-
39
- // weite Semantik (1 User-Row global), siehe docs/plans/architecture/
40
- // user-data-rights.md "Cross-Tenant-Semantik".
35
+ // POST /api/user/request-deletion — GDPR Art. 17 erasure request.
36
+ // Changes status=Active → deletionRequested and sets gracePeriodEnd from the compliance
37
+ // profile using the shared startDeletionGracePeriod logic. The operation applies to the
38
+ // account-wide user row, so the user identity is global across tenants.
41
39
  export function createRequestDeletionHandler(opts: RequestDeletionOptions = {}) {
42
40
  return defineWriteHandler({
43
41
  name: "request-deletion",
@@ -78,8 +76,7 @@ export function createRequestDeletionHandler(opts: RequestDeletionOptions = {})
78
76
  ? makeDefaultDeletionRequestedEmail(ctx.notify, opts.mailDefaults)
79
77
  : undefined);
80
78
 
81
- // Best-effort Email-Notification. Send-Failure darf das Write nicht
82
- // killen — siehe Type-Doc oben.
79
+ // Best-effort email notification: a send failure must not abort the write.
83
80
  if (send && userEmail.length > 0) {
84
81
  try {
85
82
  await send({
@@ -97,8 +94,8 @@ export function createRequestDeletionHandler(opts: RequestDeletionOptions = {})
97
94
  }
98
95
  }
99
96
 
100
- // Response liefert den absoluten gracePeriodEnd-Timestamp damit
101
- // Frontend/Audit/Cleanup-Runner alle denselben Wert lesen.
97
+ // Returns the absolute gracePeriodEnd timestamp so the frontend, audit, and cleanup
98
+ // runner all use the same value.
102
99
  return {
103
100
  isSuccess: true as const,
104
101
  data: {
@@ -1,24 +1,25 @@
1
- // POST /api/user/request-export (S2.U3 Atom 2) — DSGVO Art. 15 + 20 Trigger.
1
+ // POST /api/user/request-export — GDPR Art. 15 and 20 trigger.
2
2
  //
3
- // User triggert Export-Job. Persistenz via createEventStoreExecutor
4
- // (ES-Pattern, Memory: ES kein CRUD). Idempotency-Strategie zweistufig:
3
+ // The user triggers an export job, persisted through
4
+ // createEventStoreExecutor (event-sourced pattern; ES has no CRUD).
5
+ // Idempotency uses two stages:
5
6
  //
6
- // 1) **App-side-Pre-Check (primaerer Pfad):** fetchOne aktive Jobs
7
- // (status pending|running) fuer den userId. Wenn existing,
8
- // return `{jobId, isExisting: true}` — KEIN neues Event,
9
- // sauberer Audit-Trail (1 Klick-Storm = 1 Event, nicht N).
7
+ // 1) **App-side pre-check (primary path):** fetch active jobs with
8
+ // (status pending|running) for the userId. If one exists, return
9
+ // `{jobId, isExisting: true}` without creating an event, so a click
10
+ // storm produces one audit event rather than N.
10
11
  //
11
- // 2) **DB-Constraint als Race-Schutz (Sekundaerpfad):** Bei parallelem
12
- // Klick zwischen fetchOne + crud.create wirft die Tx 23505 mit
13
- // constraint_name === "read_export_jobs_one_active_per_user".
14
- // Catch + re-fetch + return existing als isExisting=true. Race-
15
- // Window <1ms, in Production extrem selten.
12
+ // 2) **DB constraint as race protection (secondary path):** if a parallel
13
+ // click occurs between fetchOne and crud.create, the transaction raises
14
+ // 23505 with constraint_name === "read_export_jobs_one_active_per_user".
15
+ // Catch, re-fetch, and return the existing job with isExisting=true.
16
16
  //
17
- // **Cross-Tenant-Semantik:** ExportJob is tenant-agnostic (1 job per userId
18
- // across all memberships) — the pre-check uses ctx.db.unsafeRaw (no TenantDb filter) so a click from tenant B still finds the job from tenant A.
19
- // `requestedFromTenantId` persistiert den Initial-Tenant aus dem
20
- // 1. Klick — Worker liest sein Compliance-Profile aus DIESEM Tenant
21
- // fuer Job-TTL/Stale/Cleanup.
17
+ // **Cross-tenant semantics:** ExportJob is tenant-agnostic (one job per
18
+ // userId across all memberships), so the pre-check uses ctx.db.unsafeRaw
19
+ // (no TenantDb filter) and a click from tenant B can find the job from tenant
20
+ // A. `requestedFromTenantId` persists the tenant from the first click; the
21
+ // worker reads its compliance profile from this tenant for job TTL, stale
22
+ // handling, and cleanup.
22
23
 
23
24
  import { createEventStoreExecutor, fetchOne } from "@cosmicdrift/kumiko-framework/db";
24
25
  import { defineWriteHandler, type SaveContext } from "@cosmicdrift/kumiko-framework/engine";
@@ -40,11 +41,11 @@ const REQUEST_EXPORT_REASON =
40
41
  "export jobs are keyed by userId across all of the user's tenant memberships, not the caller's current tenant";
41
42
 
42
43
  /**
43
- * Race-Loss-Detection: createEventStoreExecutor.create catched 23505
44
- * intern + returnt `WriteFailure(UniqueViolationError)`, **kein throw**.
45
- * Wir checken den Failure-Code + constraintName um den App-side-vs-Race-
46
- * Pfad zu unterscheiden. Andere Failures (validation, version-conflict,
47
- * ...) propagieren unveraendert.
44
+ * `createEventStoreExecutor.create` catches 23505 internally and returns
45
+ * `WriteFailure(UniqueViolationError)` rather than throwing. Check the
46
+ * failure code and constraintName to distinguish the app-side and race
47
+ * paths; propagate other failures (validation, version conflicts, ...)
48
+ * unchanged.
48
49
  */
49
50
  function isActiveJobConflict(failure: WriteFailure): boolean {
50
51
  const error = failure.error as {
@@ -82,9 +83,9 @@ export const requestExportWrite = defineWriteHandler({
82
83
  // TenantDb-Wrapper would hide cross-tenant jobs.
83
84
  const existing = await findActiveJob(exportJobRunner, userId);
84
85
  if (existing) {
85
- // Snapshot-Status (kann zwischen fetchOne + Response stale werden
86
- // wenn Worker parallel den State flippt; window minimal). User
87
- // pollt fuer Live-Wahrheit ueber export-status.query.
86
+ // The returned status is a snapshot and can become stale between fetchOne
87
+ // and the response if the worker changes the state concurrently. The
88
+ // window is minimal; clients poll export-status.query for live status.
88
89
  return {
89
90
  isSuccess: true as const,
90
91
  data: {
@@ -95,9 +96,9 @@ export const requestExportWrite = defineWriteHandler({
95
96
  };
96
97
  }
97
98
 
98
- // Kein active Job — neuen anlegen via crud.create. requestedFromTenantId
99
- // = aktueller Tenant des Users; Worker liest sein Compliance-Profile
100
- // aus diesem Tenant.
99
+ // No active job exists, so create one through crud.create. Persist the
100
+ // user's current tenant in requestedFromTenantId so the worker reads the
101
+ // compliance profile from that tenant.
101
102
  const result = await crud.create(
102
103
  {
103
104
  userId,
@@ -110,16 +111,16 @@ export const requestExportWrite = defineWriteHandler({
110
111
  );
111
112
 
112
113
  if (!result.isSuccess) {
113
- // Race-Pfad: paralleler Klick hat zwischen findActiveJob + crud.create
114
- // einen aktiven Job angelegt → UniqueViolationError mit unserem
115
- // Constraint. Re-fetch + return existing als isExisting=true.
116
- // Andere Failures (validation, version-conflict, ...) propagieren.
114
+ // A parallel click may create an active job between findActiveJob and
115
+ // crud.create, causing UniqueViolationError for our constraint. Re-fetch
116
+ // and return the existing job with isExisting=true. Propagate other
117
+ // failures (validation, version conflicts, ...) unchanged.
117
118
  if (isActiveJobConflict(result)) {
118
119
  const winner = await findActiveJob(exportJobRunner, userId);
119
120
  if (!winner) {
120
- // Sollte nie passieren — Constraint-Violation ohne existing
121
- // active Job hiesse der Constraint matcht etwas anderes.
122
- // Original-Failure zurueckgeben damit das auffaellt.
121
+ // This should be impossible: a constraint violation without an existing
122
+ // active job would mean the constraint matched something else.
123
+ // Return the original failure so the inconsistency is visible.
123
124
  return result;
124
125
  }
125
126
  return {
@@ -134,17 +135,17 @@ export const requestExportWrite = defineWriteHandler({
134
135
  return result;
135
136
  }
136
137
 
137
- // Happy path: neuer Job. SaveContext.id ist EntityId (number | string);
138
- // exportJobEntity hat idType:"uuid" → garantiert string, String()
139
- // schuetzt vor Drift falls jemand die Entity auf serial migriert.
138
+ // SaveContext.id is EntityId (number | string), while exportJobEntity has
139
+ // idType:"uuid" and therefore guarantees a string. String() protects
140
+ // against drift if the entity is migrated to serial.
140
141
  const created = result.data as SaveContext; // @cast-boundary engine-payload
141
142
  return {
142
143
  isSuccess: true as const,
143
144
  data: {
144
145
  jobId: String(created.id),
145
- // Snapshot: just-created → pending. Live-Wahrheit kommt ueber
146
- // export-status.query, status hier ist nice-to-have fuer das
147
- // initial UI-Render ("Anfrage angenommen, currently pending").
146
+ // This is a snapshot: a just-created job is pending. Live status comes
147
+ // from export-status.query; this value is only a convenience for the
148
+ // initial UI render ("Anfrage angenommen, currently pending").
148
149
  status: EXPORT_JOB_STATUS.Pending,
149
150
  isExisting: false,
150
151
  },