@cosmicdrift/kumiko-bundled-features 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 (469) hide show
  1. package/LICENSE +21 -57
  2. package/dist/agent-tools/tool-catalog.js +1 -1
  3. package/dist/audit/screens.js +1 -1
  4. package/dist/auth-email-password/feature.js +1 -1
  5. package/dist/auth-email-password/handlers/change-password.write.js +3 -3
  6. package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
  7. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
  8. package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
  9. package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
  10. package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
  11. package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
  12. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
  13. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  14. package/dist/auth-email-password/handlers/login.write.js +1 -1
  15. package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
  16. package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
  17. package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
  18. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
  19. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
  20. package/dist/auth-email-password/index.js +5 -5
  21. package/dist/auth-email-password/lockout-store.js +1 -1
  22. package/dist/auth-email-password/seeding.d.ts +42 -45
  23. package/dist/auth-email-password/seeding.js +51 -53
  24. package/dist/auth-email-password/signup-token-store.js +8 -8
  25. package/dist/auth-email-password/testing.js +3 -3
  26. package/dist/auth-email-password/web/auth-client.d.ts +2 -2
  27. package/dist/auth-email-password/web/auth-client.js +36 -37
  28. package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
  29. package/dist/auth-email-password/web/auth-gate.js +15 -16
  30. package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
  31. package/dist/auth-email-password/web/client-plugin.js +4 -5
  32. package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
  33. package/dist/auth-email-password/web/constants.js +3 -3
  34. package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
  35. package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
  36. package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
  37. package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
  38. package/dist/auth-email-password/web/index.js +5 -5
  39. package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
  40. package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
  41. package/dist/auth-email-password/web/login-screen.d.ts +1 -1
  42. package/dist/auth-email-password/web/login-screen.js +2 -3
  43. package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
  44. package/dist/auth-email-password/web/reset-password-screen.js +12 -13
  45. package/dist/auth-email-password/web/session.d.ts +10 -9
  46. package/dist/auth-email-password/web/session.js +28 -29
  47. package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
  48. package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
  49. package/dist/auth-email-password/web/signup-screen.js +13 -13
  50. package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
  51. package/dist/auth-email-password/web/tenant-switcher.js +21 -22
  52. package/dist/auth-email-password/web/use-shell-user.js +12 -13
  53. package/dist/auth-email-password/web/user-menu.d.ts +4 -4
  54. package/dist/auth-email-password/web/user-menu.js +12 -13
  55. package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
  56. package/dist/auth-email-password/web/verify-email-screen.js +9 -10
  57. package/dist/auth-foundation/boot-checks.js +4 -4
  58. package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
  59. package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
  60. package/dist/auth-foundation/types.js +2 -2
  61. package/dist/auth-mfa/handlers/disable.write.js +3 -3
  62. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
  63. package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
  64. package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
  65. package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
  66. package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
  67. package/dist/auth-mfa/handlers/verify.write.js +1 -1
  68. package/dist/auth-mfa/token-secrets.js +1 -1
  69. package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
  70. package/dist/auth-mfa/web/client-plugin.js +1 -1
  71. package/dist/auth-mfa/web/i18n.js +3 -3
  72. package/dist/auth-mfa/web/index.js +4 -4
  73. package/dist/auth-mfa/web/mfa-client.js +17 -16
  74. package/dist/auth-mfa/web/mfa-verify-screen.js +6 -6
  75. package/dist/billing-foundation/aggregate-id.d.ts +5 -5
  76. package/dist/billing-foundation/aggregate-id.js +10 -11
  77. package/dist/billing-foundation/entities.js +27 -27
  78. package/dist/billing-foundation/events.js +18 -17
  79. package/dist/billing-foundation/feature.js +42 -43
  80. package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
  81. package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
  82. package/dist/billing-foundation/handlers/create-checkout-session.write.js +18 -17
  83. package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
  84. package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
  85. package/dist/billing-foundation/handlers/process-event.write.js +20 -20
  86. package/dist/billing-foundation/projection.d.ts +15 -11
  87. package/dist/billing-foundation/projection.js +38 -37
  88. package/dist/billing-foundation/subscription-tier-sync.js +3 -3
  89. package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
  90. package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
  91. package/dist/billing-foundation/types.d.ts +40 -41
  92. package/dist/billing-foundation/types.js +20 -20
  93. package/dist/billing-foundation/web/billing-plans-panel.js +4 -4
  94. package/dist/cap-counter/aggregate-id.js +8 -9
  95. package/dist/cap-counter/constants.js +11 -12
  96. package/dist/cap-counter/enforce-cap.d.ts +59 -62
  97. package/dist/cap-counter/enforce-cap.js +47 -48
  98. package/dist/cap-counter/entity.js +29 -29
  99. package/dist/cap-counter/feature.js +34 -35
  100. package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
  101. package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
  102. package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
  103. package/dist/cap-counter/stock-cap-guard.js +1 -1
  104. package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
  105. package/dist/channel-email/smtp-transport.d.ts +18 -17
  106. package/dist/channel-email/smtp-transport.js +24 -21
  107. package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
  108. package/dist/compliance-profiles/feature.js +4 -4
  109. package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
  110. package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
  111. package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
  112. package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
  113. package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
  114. package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
  115. package/dist/compliance-profiles/schema/profile-selection.js +18 -23
  116. package/dist/compliance-profiles/seeding.js +14 -15
  117. package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
  118. package/dist/compliance-profiles-ops/index.js +2 -2
  119. package/dist/config/handlers/readiness.query.d.ts +4 -4
  120. package/dist/config/handlers/readiness.query.js +1 -1
  121. package/dist/config/handlers/reencrypt.job.js +4 -5
  122. package/dist/config/handlers/set.write.js +4 -4
  123. package/dist/config/resolver.js +4 -4
  124. package/dist/config/table.js +10 -10
  125. package/dist/config/write-helpers.js +1 -1
  126. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
  127. package/dist/crypto-shredding/handlers/forget-subject.write.js +12 -11
  128. package/dist/custom-fields/aggregate-id.d.ts +12 -13
  129. package/dist/custom-fields/aggregate-id.js +16 -17
  130. package/dist/custom-fields/entity.js +49 -47
  131. package/dist/custom-fields/events.js +7 -8
  132. package/dist/custom-fields/feature.d.ts +14 -14
  133. package/dist/custom-fields/feature.js +53 -54
  134. package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
  135. package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
  136. package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
  137. package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
  138. package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
  139. package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
  140. package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
  141. package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
  142. package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
  143. package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
  144. package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
  145. package/dist/custom-fields/lib/field-access.js +2 -2
  146. package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
  147. package/dist/custom-fields/lib/quota.js +1 -1
  148. package/dist/custom-fields/lib/value-schema.js +10 -12
  149. package/dist/custom-fields/run-retention.js +3 -3
  150. package/dist/custom-fields/schemas.js +14 -14
  151. package/dist/custom-fields/web/client-plugin.js +2 -5
  152. package/dist/custom-fields/web/constants.js +2 -2
  153. package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
  154. package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
  155. package/dist/custom-fields/wire-for-entity.js +65 -67
  156. package/dist/custom-fields/wire-user-data-rights.js +3 -3
  157. package/dist/data-retention/_internal/parse-override.js +2 -3
  158. package/dist/data-retention/feature.js +11 -18
  159. package/dist/data-retention/handlers/policy-for.query.js +6 -6
  160. package/dist/data-retention/keep-for.d.ts +7 -7
  161. package/dist/data-retention/keep-for.js +15 -15
  162. package/dist/data-retention/override-schema.d.ts +3 -4
  163. package/dist/data-retention/override-schema.js +13 -15
  164. package/dist/data-retention/presets.d.ts +4 -5
  165. package/dist/data-retention/presets.js +18 -23
  166. package/dist/data-retention/resolve-tenant-preset.js +17 -16
  167. package/dist/data-retention/resolver.d.ts +12 -12
  168. package/dist/data-retention/resolver.js +24 -23
  169. package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
  170. package/dist/data-retention/run-retention-cleanup.js +61 -51
  171. package/dist/data-retention/schema/tenant-retention-override.js +13 -11
  172. package/dist/delivery/feature.js +2 -2
  173. package/dist/delivery/handlers/log.query.js +1 -1
  174. package/dist/delivery/tables.js +9 -8
  175. package/dist/delivery/unsubscribe.js +4 -4
  176. package/dist/document-ingest-foundation/entity.js +4 -4
  177. package/dist/document-ingest-foundation/events.js +2 -2
  178. package/dist/document-ingest-foundation/feature.js +2 -2
  179. package/dist/document-ingest-foundation/pages.js +1 -1
  180. package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
  181. package/dist/feature-toggles/compose-tier-resolver.js +1 -1
  182. package/dist/feature-toggles/feature.js +1 -1
  183. package/dist/feature-toggles/global-feature-state-table.js +1 -1
  184. package/dist/feature-toggles/handlers/registered.query.js +5 -5
  185. package/dist/feature-toggles/toggle-sync-signal.js +1 -1
  186. package/dist/feature-toggles/web/constants.js +1 -1
  187. package/dist/file-derivatives/feature.d.ts +1 -1
  188. package/dist/file-derivatives/feature.js +1 -1
  189. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  190. package/dist/file-derivatives/presets.js +1 -1
  191. package/dist/file-foundation/feature.js +6 -6
  192. package/dist/file-provider-inmemory/feature.d.ts +1 -1
  193. package/dist/file-provider-inmemory/feature.js +16 -16
  194. package/dist/file-provider-s3/feature.js +16 -16
  195. package/dist/file-provider-s3/lib/s3-provider.js +6 -6
  196. package/dist/files/feature.js +6 -6
  197. package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
  198. package/dist/files-tenant-data/hooks.js +24 -26
  199. package/dist/files-tenant-data/index.js +1 -1
  200. package/dist/folders/feature.d.ts +1 -1
  201. package/dist/folders/web/folder-manager.js +2 -2
  202. package/dist/folders/web/tree.js +5 -5
  203. package/dist/form-draft/db/queries/cleanup.js +1 -1
  204. package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
  205. package/dist/form-draft/entity.js +3 -3
  206. package/dist/form-draft/feature.js +4 -5
  207. package/dist/form-draft/handlers/discard.write.js +1 -1
  208. package/dist/form-draft/release-file-refs.js +6 -6
  209. package/dist/form-draft/schemas.js +3 -3
  210. package/dist/form-draft/web/constants.js +0 -2
  211. package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
  212. package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
  213. package/dist/inbound-mail-foundation/connect-routes.js +4 -3
  214. package/dist/inbound-mail-foundation/constants.d.ts +22 -24
  215. package/dist/inbound-mail-foundation/constants.js +30 -33
  216. package/dist/inbound-mail-foundation/entities.d.ts +12 -12
  217. package/dist/inbound-mail-foundation/entities.js +47 -48
  218. package/dist/inbound-mail-foundation/events.d.ts +4 -3
  219. package/dist/inbound-mail-foundation/events.js +50 -47
  220. package/dist/inbound-mail-foundation/feature.js +39 -39
  221. package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
  222. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
  223. package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
  224. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
  225. package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
  226. package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
  227. package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
  228. package/dist/inbound-mail-foundation/handlers/update-account.write.js +11 -11
  229. package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
  230. package/dist/inbound-mail-foundation/oauth-state.js +11 -10
  231. package/dist/inbound-mail-foundation/projection.d.ts +4 -4
  232. package/dist/inbound-mail-foundation/projection.js +31 -29
  233. package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
  234. package/dist/inbound-mail-foundation/provider-factory.js +9 -9
  235. package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
  236. package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
  237. package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
  238. package/dist/inbound-mail-foundation/types.d.ts +45 -46
  239. package/dist/inbound-mail-foundation/types.js +21 -21
  240. package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
  241. package/dist/inbound-mail-foundation/watch-supervisor.js +57 -56
  242. package/dist/inbound-provider-imap/credential-document.js +13 -13
  243. package/dist/inbound-provider-imap/feature.d.ts +2 -2
  244. package/dist/inbound-provider-imap/feature.js +42 -40
  245. package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
  246. package/dist/inbound-provider-imap/imap-client.js +20 -19
  247. package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
  248. package/dist/inbound-provider-inmemory/feature.js +19 -19
  249. package/dist/jobs/db/queries/retention.js +2 -2
  250. package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
  251. package/dist/jobs/events.js +1 -1
  252. package/dist/jobs/feature.js +5 -6
  253. package/dist/jobs/handlers/detail.query.js +4 -4
  254. package/dist/jobs/handlers/list.query.js +1 -1
  255. package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
  256. package/dist/jobs/handlers/reindex-entity.job.js +1 -1
  257. package/dist/jobs/handlers/retry.write.js +1 -1
  258. package/dist/jobs/job-instant.js +1 -1
  259. package/dist/jobs/job-run-logger.js +10 -11
  260. package/dist/jobs/job-run-table.js +3 -3
  261. package/dist/jobs/screens.js +1 -1
  262. package/dist/jobs/tenant-job-failure-table.js +1 -1
  263. package/dist/jobs/tenant-job-run-table.js +1 -1
  264. package/dist/ledger/entity.js +5 -5
  265. package/dist/ledger/feature.js +3 -3
  266. package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
  267. package/dist/ledger/recurring.js +12 -12
  268. package/dist/ledger/schemas.js +1 -1
  269. package/dist/ledger/web/constants.js +2 -2
  270. package/dist/ledger/web/index.js +7 -7
  271. package/dist/legal-pages/feature.d.ts +4 -4
  272. package/dist/legal-pages/feature.js +25 -26
  273. package/dist/legal-pages/markdown.js +3 -3
  274. package/dist/legal-pages/web/client-plugin.js +23 -24
  275. package/dist/legal-pages/web/index.js +0 -1
  276. package/dist/mail-foundation/feature.d.ts +2 -2
  277. package/dist/mail-foundation/feature.js +24 -25
  278. package/dist/mail-transport-inmemory/feature.d.ts +4 -4
  279. package/dist/mail-transport-inmemory/feature.js +27 -28
  280. package/dist/mail-transport-smtp/feature.js +20 -20
  281. package/dist/managed-pages/branding.js +6 -6
  282. package/dist/managed-pages/feature.d.ts +26 -26
  283. package/dist/managed-pages/feature.js +34 -34
  284. package/dist/managed-pages/handlers/by-slug.query.js +5 -6
  285. package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
  286. package/dist/managed-pages/handlers/set.write.js +5 -7
  287. package/dist/managed-pages/screens/page-screens.js +11 -11
  288. package/dist/managed-pages/seeding.js +8 -8
  289. package/dist/managed-pages/table.js +13 -12
  290. package/dist/managed-pages/web/client-plugin.d.ts +1 -1
  291. package/dist/managed-pages/web/client-plugin.js +5 -5
  292. package/dist/managed-pages/web/i18n.js +1 -1
  293. package/dist/notes-history/entity.js +4 -4
  294. package/dist/notes-history/feature.d.ts +2 -2
  295. package/dist/notes-history-user-data/hooks.js +1 -1
  296. package/dist/page-render/layout.js +9 -9
  297. package/dist/page-render/markdown.js +11 -11
  298. package/dist/page-render/security-headers.js +7 -6
  299. package/dist/personal-access-tokens/feature.js +5 -5
  300. package/dist/personal-access-tokens/roles.js +2 -2
  301. package/dist/readiness/handlers/status.query.js +3 -3
  302. package/dist/renderer-foundation/api.js +12 -13
  303. package/dist/renderer-foundation/constants.js +2 -2
  304. package/dist/renderer-foundation/feature.js +7 -7
  305. package/dist/secrets/feature.js +9 -9
  306. package/dist/secrets/web/constants.js +2 -2
  307. package/dist/seo/feature.js +3 -3
  308. package/dist/sessions/constants.d.ts +1 -1
  309. package/dist/sessions/constants.js +1 -1
  310. package/dist/sessions/feature.js +8 -8
  311. package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
  312. package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
  313. package/dist/sessions/handlers/revoke.write.js +1 -1
  314. package/dist/sessions/schema/user-session.js +4 -4
  315. package/dist/sessions/screens.js +2 -2
  316. package/dist/sessions/session-callbacks.js +2 -2
  317. package/dist/sessions/session-revoked-event.js +2 -2
  318. package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
  319. package/dist/shared/decrypt-stored-pii.js +1 -1
  320. package/dist/shared/encrypt-for-direct-write.js +1 -1
  321. package/dist/shared/map-with-concurrency.js +5 -4
  322. package/dist/shared/parent-visibility.js +2 -2
  323. package/dist/shared/password-hashing.js +1 -1
  324. package/dist/shared/signup-handover.js +1 -2
  325. package/dist/shared/single-use-token-store.js +1 -1
  326. package/dist/subscription-mollie/constants.js +15 -17
  327. package/dist/subscription-mollie/feature.d.ts +17 -18
  328. package/dist/subscription-mollie/feature.js +24 -24
  329. package/dist/subscription-mollie/index.js +3 -3
  330. package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
  331. package/dist/subscription-mollie/plugin-methods.js +15 -14
  332. package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
  333. package/dist/subscription-mollie/verify-webhook.js +27 -27
  334. package/dist/subscription-stripe/constants.js +4 -6
  335. package/dist/subscription-stripe/feature.d.ts +14 -14
  336. package/dist/subscription-stripe/feature.js +35 -34
  337. package/dist/subscription-stripe/index.js +6 -6
  338. package/dist/subscription-stripe/plugin-methods.js +18 -18
  339. package/dist/subscription-stripe/runtime.d.ts +1 -1
  340. package/dist/subscription-stripe/runtime.js +1 -1
  341. package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
  342. package/dist/subscription-stripe/verify-webhook.js +59 -61
  343. package/dist/tags/feature.d.ts +3 -3
  344. package/dist/tags/screens.js +1 -1
  345. package/dist/template-resolver/api.js +10 -10
  346. package/dist/template-resolver/handlers/find-by-id.query.js +5 -5
  347. package/dist/template-resolver/handlers/list.query.js +1 -1
  348. package/dist/template-resolver/handlers/shared.js +2 -2
  349. package/dist/template-resolver/handlers/template-write.js +3 -3
  350. package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
  351. package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
  352. package/dist/template-resolver/seeding.js +4 -4
  353. package/dist/template-resolver/table.js +10 -11
  354. package/dist/template-resolver/web/client-plugin.js +2 -2
  355. package/dist/tenant/feature.js +7 -7
  356. package/dist/tenant/handlers/member-directory.query.js +1 -1
  357. package/dist/tenant/handlers/members.query.js +1 -1
  358. package/dist/tenant/handlers/team-list.query.js +1 -1
  359. package/dist/tenant/handlers/tenant-directory.query.js +1 -1
  360. package/dist/tenant/invitation-table.js +37 -40
  361. package/dist/tenant/membership-table.js +3 -3
  362. package/dist/tenant/schema/tenant.js +7 -9
  363. package/dist/tenant/screens.js +1 -1
  364. package/dist/tenant/seeding.d.ts +6 -5
  365. package/dist/tenant/seeding.js +12 -12
  366. package/dist/tenant/testing.js +5 -6
  367. package/dist/tenant/web/constants.js +2 -2
  368. package/dist/tenant-handover/events.js +5 -7
  369. package/dist/tenant-handover/feature.js +1 -1
  370. package/dist/tenant-handover/grant.js +1 -1
  371. package/dist/tenant-handover/handlers/claim.write.js +1 -1
  372. package/dist/tenant-handover/move-entity-graph.js +8 -9
  373. package/dist/tenant-handover/signup-handover-provider.js +1 -2
  374. package/dist/tenant-handover/transfer-graph.js +4 -5
  375. package/dist/tenant-lifecycle/boot-checks.js +1 -1
  376. package/dist/tenant-lifecycle/feature.js +1 -1
  377. package/dist/tenant-settings/constants.js +1 -1
  378. package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
  379. package/dist/tenant-settings/tenant-defaults.js +9 -2
  380. package/dist/tier-engine/aggregate-id.d.ts +11 -11
  381. package/dist/tier-engine/aggregate-id.js +15 -16
  382. package/dist/tier-engine/compose-app.d.ts +14 -14
  383. package/dist/tier-engine/compose-app.js +1 -1
  384. package/dist/tier-engine/entity.js +18 -19
  385. package/dist/tier-engine/feature.d.ts +19 -17
  386. package/dist/tier-engine/feature.js +68 -69
  387. package/dist/tier-engine/handlers/active-tier.query.js +6 -7
  388. package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
  389. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +5 -5
  390. package/dist/tier-engine/handlers/set-tenant-tier.write.js +3 -3
  391. package/dist/tier-engine/tier-resolver.js +2 -2
  392. package/dist/tier-engine/trial.js +8 -8
  393. package/dist/tier-engine/web/index.js +4 -4
  394. package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
  395. package/dist/user/handlers/create.write.js +4 -4
  396. package/dist/user/schema/user.d.ts +11 -11
  397. package/dist/user/schema/user.js +45 -44
  398. package/dist/user/seeding.d.ts +15 -15
  399. package/dist/user/seeding.js +28 -28
  400. package/dist/user/testing.js +3 -3
  401. package/dist/user-data-rights/audit-download.d.ts +8 -8
  402. package/dist/user-data-rights/audit-download.js +19 -20
  403. package/dist/user-data-rights/boot-checks.js +1 -1
  404. package/dist/user-data-rights/deletion-token.js +1 -1
  405. package/dist/user-data-rights/feature.d.ts +49 -50
  406. package/dist/user-data-rights/feature.js +54 -53
  407. package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
  408. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
  409. package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
  410. package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
  411. package/dist/user-data-rights/handlers/export-status.query.js +8 -9
  412. package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
  413. package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
  414. package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
  415. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
  416. package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
  417. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
  418. package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
  419. package/dist/user-data-rights/handlers/request-export.write.js +42 -41
  420. package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
  421. package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
  422. package/dist/user-data-rights/index.js +8 -10
  423. package/dist/user-data-rights/lib/default-mailers.js +3 -3
  424. package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
  425. package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
  426. package/dist/user-data-rights/run-export-jobs.js +127 -233
  427. package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
  428. package/dist/user-data-rights/run-forget-cleanup.js +63 -82
  429. package/dist/user-data-rights/run-user-export.d.ts +34 -33
  430. package/dist/user-data-rights/run-user-export.js +55 -57
  431. package/dist/user-data-rights/schema/download-attempt.js +11 -11
  432. package/dist/user-data-rights/schema/download-token.js +37 -41
  433. package/dist/user-data-rights/schema/export-job.d.ts +4 -4
  434. package/dist/user-data-rights/schema/export-job.js +65 -67
  435. package/dist/user-data-rights/token-helpers.d.ts +8 -9
  436. package/dist/user-data-rights/token-helpers.js +21 -25
  437. package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
  438. package/dist/user-data-rights/web/client-plugin.js +1 -1
  439. package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
  440. package/dist/user-data-rights/web/constants.js +1 -1
  441. package/dist/user-data-rights/web/index.js +5 -5
  442. package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
  443. package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
  444. package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
  445. package/dist/user-data-rights/zip-path.d.ts +15 -15
  446. package/dist/user-data-rights/zip-path.js +41 -40
  447. package/dist/user-data-rights-defaults/feature.js +15 -15
  448. package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
  449. package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
  450. package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
  451. package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
  452. package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
  453. package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
  454. package/dist/user-profile/handlers/change-email.write.js +13 -13
  455. package/dist/user-profile/index.js +3 -3
  456. package/dist/user-profile/web/client-plugin.js +1 -1
  457. package/dist/user-profile/web/constants.js +1 -1
  458. package/dist/user-profile/web/profile-screen.js +1 -1
  459. package/dist/workflow-runner/aggregate-id.d.ts +1 -1
  460. package/dist/workflow-runner/aggregate-id.js +1 -1
  461. package/dist/workflow-runner/event-subscriber.js +4 -5
  462. package/dist/workflow-runner/event-trigger.js +4 -17
  463. package/dist/workflow-runner/feature.js +2 -2
  464. package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
  465. package/dist/workflow-runner/pending-projection.js +6 -7
  466. package/dist/workflow-runner/runner.js +6 -6
  467. package/dist/workflow-runner/tables.js +6 -6
  468. package/dist/workflow-runner/workflow-registry.js +1 -1
  469. package/package.json +10 -10
@@ -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
  import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
29
28
  import { defineQueryHandler, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
30
29
  import { NotFoundError, UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
@@ -35,7 +34,7 @@ import { makeTenantStorageProviderResolver } from "../lib/storage-provider-resol
35
34
  import { exportDownloadTokensTable } from "../schema/download-token.js";
36
35
  import { EXPORT_JOB_STATUS, exportJobsTable } from "../schema/export-job.js";
37
36
  import { hashDownloadToken } from "../token-helpers.js";
38
- const SIGNED_URL_TTL_SECONDS = 300; // 5 min — kurz genug fuer Replay-Schutz, lang genug fuer slow connections
37
+ const SIGNED_URL_TTL_SECONDS = 300; // 5 min — short enough to limit replay, long enough for slow connections
39
38
  const DOWNLOAD_BY_TOKEN_REASON = "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";
40
39
  export const downloadByTokenQuery = defineQueryHandler({
41
40
  name: "download-by-token",
@@ -54,12 +53,11 @@ export const downloadByTokenQuery = defineQueryHandler({
54
53
  personalData: "public-intake",
55
54
  },
56
55
  agent: { expose: false },
57
- // Brute-Force-Schutz fuer Token-Hash-Probing. Anonymous-Endpoint mit
58
- // 32-byte-Random-Token = 256 Bit Search-Space, aber rate-limit als
59
- // defense-in-depth + Schutz gegen Storm-Patterns die DB-Last erzeugen.
60
- // 30 attempts/min/IP reicht fuer legitime User (mehrere Klicks bei
61
- // Connection-Abbruch); blockiert automatisierte Probing-Loops.
62
- // Memory `feedback_security_default_on`.
56
+ // Brute-force protection for token-hash probing. This anonymous endpoint uses a
57
+ // 32-byte random token with a 256-bit search space, but rate limiting provides
58
+ // defense in depth and protects against storm patterns that generate database load.
59
+ // 30 attempts/min/IP allows legitimate users several retries after connection
60
+ // failures while blocking automated probing loops.
63
61
  rateLimit: { per: "ip+handler", limit: 30, windowSeconds: 60 },
64
62
  escapeHatch: {
65
63
  grants: ["unsafeRaw"],
@@ -75,26 +73,26 @@ export const downloadByTokenQuery = defineQueryHandler({
75
73
  tokenHash: hash,
76
74
  });
77
75
  if (!tokenRow) {
78
- // Invalid token — 404 ohne Existenz-Leak. Generic NotFoundError
79
- // damit alle Failure-Pfade die gleiche externe Shape haben (kein
80
- // Probing zwischen "Token existiert nicht" vs "Job ist failed").
76
+ // Invalid token — 404 without leaking existence. Use a generic NotFoundError
77
+ // so all failure paths have the same external shape, preventing probing that
78
+ // distinguishes "Token does not exist" from "Job has failed".
81
79
  throw new NotFoundError("export-download", undefined, {
82
80
  i18nKey: "userDataRights.errors.download.notFound",
83
81
  });
84
82
  }
85
83
  const auditIp = query.payload.auditMeta?.ip ?? null;
86
84
  const auditUa = query.payload.auditMeta?.userAgent ?? null;
87
- // Step 2: TTL-check.
85
+ // Step 2: TTL check.
88
86
  //
89
- // **Pragma:** semantisch waere 410 Gone richtig (war mal da, jetzt
90
- // nicht mehr). Framework hat keine GoneError-Class; wir nutzen
91
- // NotFoundError + i18nKey "expired" als Kompromiss. UI rendert
92
- // anhand des i18nKeys, nicht des HTTP-Status — also User sieht
93
- // "Dein Download ist abgelaufen", nicht generic "not found".
87
+ // 410 Gone would be semantically right (the download existed and is now
88
+ // gone), but the framework has no GoneError class. NotFoundError with
89
+ // i18nKey "expired" is the compromise: the UI renders by i18nKey, not by
90
+ // HTTP status, so the user sees "Your download has expired" instead of a
91
+ // generic "not found".
94
92
  if (tokenRow.expiresAt.epochMilliseconds <= now.epochMilliseconds) {
95
- // Audit-Skip noch nicht moeglich — jobRow noch nicht geladen,
96
- // tenantId unbekannt. Wir laden den Job hier noch fuer Audit-Context
97
- // (best-effort — wenn Job auch fehlt, audit-skip ist akzeptabel).
93
+ // Auditing cannot be skipped yet because jobRow has not been loaded and the
94
+ // tenantId is unknown. Load the job to obtain audit context; if it is also
95
+ // missing, skipping the audit is acceptable (best-effort).
98
96
  const jobForAudit = await fetchOne(runner, exportJobsTable, {
99
97
  id: tokenRow.jobId,
100
98
  });
@@ -193,9 +191,9 @@ export const downloadByTokenQuery = defineQueryHandler({
193
191
  contentDisposition: `attachment; filename="user-data-export-${jobRow.id}.zip"`,
194
192
  });
195
193
  const signedUrlExpiresAt = T.Instant.fromEpochMilliseconds(now.epochMilliseconds + SIGNED_URL_TTL_SECONDS * 1000);
196
- // Step 6: Audit-Update best-effort. auditMeta kommt vom httpRoute-
197
- // Wrapper (trusted-source). Direct-API-caller koennen luegen, aber
198
- // Audit ist nicht security-relevant.
194
+ // Step 6: Best-effort audit update. auditMeta comes from the httpRoute wrapper
195
+ // (trusted source). Direct API callers can falsify it, but the audit data is not
196
+ // security-sensitive.
199
197
  await recordDownloadUse({
200
198
  db: runner,
201
199
  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
  import { selectMany } from "@cosmicdrift/kumiko-framework/db";
14
13
  import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
15
14
  import * as z from "zod";
@@ -5,7 +5,7 @@ import { USER_STATUS, userTable } from "../../user/index.js";
5
5
  import { denyIfTargetOutsideAdminTenant } from "../lib/deny-if-target-outside-admin-tenant.js";
6
6
  import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
7
7
  const LIFT_RESTRICTION_ESCAPE_HATCH_REASON = "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.";
8
- // POST /api/user/lift-restriction (S2.U6) — DSGVO Art. 18 Reverse.
8
+ // POST /api/user/lift-restriction — GDPR Art. 18 Reverse.
9
9
  //
10
10
  // Operator-only. A Restricted user's own session is unconditionally
11
11
  // rejected by sessionChecker (BLOCKED_STATUSES) the moment their status
@@ -13,16 +13,15 @@ const LIFT_RESTRICTION_ESCAPE_HATCH_REASON = "checks the target user's membershi
13
13
  // endpoint. There is no self-service path today, so lifting a restriction
14
14
  // always targets someone else's account by id.
15
15
  //
16
- // Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even
17
- // though the User-entity is global — see user-data-rights.md "Cross-Tenant-Semantik". Without a membership check, a TenantAdmin
18
- // from tenant A could unrestrict/reactivate a user who has never been a
19
- // member of tenant A. Only SystemAdmin (platform-wide) skips the check;
20
- // the target must have a membership row in the acting admin's
21
- // `event.user.tenantId` (row existence only — no active-status field).
16
+ // Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even though the User-entity is
17
+ // global — see user-data-rights.md "Cross-Tenant Semantics". Without a membership check, a
18
+ // TenantAdmin from tenant A could unrestrict/reactivate a user who has never been a member of
19
+ // tenant A. Only SystemAdmin (platform-wide) skips the check; the target must have a membership row
20
+ // in the acting admin's `event.user.tenantId` (row existence only — no active-status field).
22
21
  //
23
22
  // State-Transitions:
24
23
  // Restricted → Active ✓
25
- // Active → ... ✗ 422 not_restricted (Idempotenz-Guard)
24
+ // Active → ... ✗ 422 not_restricted (Idempotency Guard)
26
25
  // DeletionRequested → ... ✗ 422 not_restricted
27
26
  // Deleted → ... ✗ 422 not_restricted
28
27
  export const liftRestrictionWrite = defineWriteHandler({
@@ -4,8 +4,8 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
4
  import * as z from "zod";
5
5
  import { decryptStoredPii } from "../../shared/index.js";
6
6
  import { downloadAttemptsTable } from "../schema/download-attempt.js";
7
- // Operator-Query: invalid Download-Attempts (S2.U7).
8
- // DPO-Sicht fuer Brute-Force-Detection. Tenant-isolated via WHERE.
7
+ // Operator query for invalid download attempts. Provides tenant-isolated visibility for data
8
+ // protection officers investigating brute-force probing.
9
9
  const MAX_LIMIT = 100;
10
10
  const REQUEST_ID = "user-data-rights:list-download-attempts";
11
11
  export const listDownloadAttemptsQuery = defineQueryHandler({
@@ -4,10 +4,10 @@ import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
4
4
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
5
5
  import * as z from "zod";
6
6
  import { redactPiiCiphertext } from "../../shared/index.js";
7
- // DSGVO Art. 15 Selbstauskunft — User reads HIS OWN audit-log.
8
- // WHERE createdBy = ctx.user.id ist hard-coded (kein userId-Param,
9
- // anti-cross-user-Snooping). KEIN tenantId-Filter: User hat Anspruch
10
- // auf account-weite Sicht ueber alle Memberships — analog Forget-Pfad.
7
+ // GDPR Art. 15 self-disclosure: users may read only their own audit log.
8
+ // `createdBy = ctx.user.id` is hard-coded to prevent cross-user snooping;
9
+ // no `tenantId` filter is applied because the user is entitled to an
10
+ // account-wide view across all memberships, as in the forget path.
11
11
  const MAX_LIMIT = 100;
12
12
  const MY_AUDIT_LOG_REASON = "reads the caller's own events across all tenant memberships, bounded by createdBy = caller";
13
13
  export const myAuditLogQuery = defineQueryHandler({
@@ -5,12 +5,12 @@ export type SendDeletionVerificationEmailFn = (args: {
5
5
  readonly expiresAt: string;
6
6
  }) => Promise<void>;
7
7
  export type RequestDeletionByEmailOptions = {
8
- /** HMAC-Secret zum Signieren des Verify-Tokens. Ohne Secret ist der Flow
9
- * deaktiviert (Handler antwortet still mit success, kein Link). */
8
+ /** HMAC secret used to sign the verification token. Without a secret, the flow
9
+ * is disabled (the handler silently responds with success and no link). */
10
10
  readonly deletionTokenSecret?: string;
11
- /** Basis-URL des Apex-Confirm-Screens, z.B.
12
- * "https://app.example.com/delete-account/confirm". Der Handler hängt das
13
- * Token als URL-Fragment an (`#token=<token>`, fw#1554). Ohne URL kein Link. */
11
+ /** Base URL of the Apex confirmation screen, e.g.
12
+ * "https://app.example.com/delete-account/confirm". The handler appends the
13
+ * token as a URL fragment (`#token=<token>`). Without a URL, no link is sent. */
14
14
  readonly deletionVerifyUrl?: string;
15
15
  readonly sendDeletionVerificationEmail?: SendDeletionVerificationEmailFn;
16
16
  };
@@ -3,13 +3,13 @@ import * as z from "zod";
3
3
  import { USER_STATUS, userTable } from "../../user/index.js";
4
4
  import { signDeletionToken } from "../deletion-token.js";
5
5
  import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
6
- // TTL des Verify-Links. 60 min — lang genug für einen Mail-Roundtrip,
7
- // kurz genug dass ein abgefangener Link nicht ewig gültig ist.
6
+ // Verification-link TTL: 60 minutes — long enough for an email round trip,
7
+ // but short enough that an intercepted link does not remain valid indefinitely.
8
8
  const DELETION_VERIFY_TTL_MINUTES = 60;
9
9
  // Token goes in the URL fragment, not a query param — fragments never leave
10
10
  // the browser (not sent to the server, so they never land in proxy/access
11
11
  // logs, unlike `?token=`). Same convention as the export-download link
12
- // (feature.ts, issue #1271). Preserves any existing query params on `base`
12
+ // (feature.ts). Preserves any existing query params on `base`
13
13
  // (`?lang=de` stays a query param; only the token is a fragment).
14
14
  const APPEND_LIFECYCLE_EVENT_REASON = "appends the user lifecycle event on the SYSTEM_TENANT_ID user stream";
15
15
  export function buildDeletionVerifyUrl(base, token) {
@@ -17,14 +17,14 @@ export function buildDeletionVerifyUrl(base, token) {
17
17
  url.hash = `token=${encodeURIComponent(token)}`;
18
18
  return url.toString();
19
19
  }
20
- // Anonymer Apex-Flow Schritt 1: "Account-Löschung beantragen" per Email.
21
- // DSGVO-relevant gerade wenn der User sich NICHT mehr einloggen kann
22
- // (Lockout). Email → Magic-Link → confirm-deletion-by-token.
20
+ // Anonymous Apex flow step 1: request account deletion by email.
21
+ // This is especially relevant for GDPR when the user can no longer log in
22
+ // (lockout). Email → magic link → confirm-deletion-by-token.
23
23
  //
24
- // Enumeration-safe (content): antwortet IMMER mit derselben success-Shape,
25
- // egal ob die Email existiert, der User aktiv ist oder der Flow konfiguriert
26
- // ist. Ein Link wird nur für einen existierenden, aktiven User generiert.
27
- // Timing-Safety hängt am non-blocking Callback (siehe Type-Doc oben).
24
+ // Enumeration-safe (content): always returns the same success shape,
25
+ // regardless of whether the email exists, the user is active, or the flow is
26
+ // configured. A link is generated only for an existing, active user.
27
+ // Timing safety depends on the non-blocking callback (see the type doc above).
28
28
  export function createRequestDeletionByEmailHandler(opts = {}) {
29
29
  return defineWriteHandler({
30
30
  name: "request-deletion-by-email",
@@ -33,7 +33,7 @@ export function createRequestDeletionByEmailHandler(opts = {}) {
33
33
  description: "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.",
34
34
  // Mails a deletion link to any address the caller names.
35
35
  agent: { risk: "high" },
36
- // Defense-in-depth gegen Email-Probing auf dem anonymen Endpoint.
36
+ // Defense in depth against email probing on the anonymous endpoint.
37
37
  rateLimit: { per: "ip+handler", limit: 10, windowSeconds: 60 },
38
38
  escapeHatch: {
39
39
  grants: ["unsafeRaw", "globalWrites"],
@@ -41,7 +41,8 @@ export function createRequestDeletionByEmailHandler(opts = {}) {
41
41
  },
42
42
  handler: async (event, ctx) => {
43
43
  const success = { isSuccess: true, data: { kind: "requested" } };
44
- // not-configured-safe: ohne Secret/URL kein Link, aber gleiche Antwort.
44
+ // Safe when not configured: without a secret or URL, no link is sent, but the response is
45
+ // unchanged.
45
46
  if (!opts.deletionTokenSecret || !opts.deletionVerifyUrl)
46
47
  return success;
47
48
  const userRow = await ctx.db
@@ -53,9 +54,9 @@ export function createRequestDeletionByEmailHandler(opts = {}) {
53
54
  if (!userRow || userRow["status"] !== USER_STATUS.Active || !userRow["email"]) {
54
55
  return success;
55
56
  }
56
- // Replay-Schutz (#354/1): pro Antrag eine frische requestId, die auf der
57
- // user-Row landet und in die Token-HMAC-Purpose gefaltet wird. cancel
58
- // nullt sie → ein nach Cancel nachgespieltes Token verifiziert nicht mehr.
57
+ // Replay protection: each request gets a fresh requestId, which is stored on
58
+ // the user row and incorporated into the token HMAC purpose. cancel
59
+ // nulls it, so a token replayed after cancellation no longer verifies.
59
60
  const requestId = crypto.randomUUID();
60
61
  await updateUserLifecycle(ctx.db.unsafeRaw(), userRow["id"], {
61
62
  pendingDeletionRequestId: requestId,
@@ -11,8 +11,8 @@ export type SendDeletionRequestedEmailFn = (args: {
11
11
  }) => Promise<void>;
12
12
  export type RequestDeletionOptions = {
13
13
  readonly sendDeletionRequestedEmail?: SendDeletionRequestedEmailFn;
14
- /** Branding fuer die Default-Mail wenn kein sendDeletionRequestedEmail
15
- * gesetzt ist + mail-foundation gemountet ist. */
14
+ /** Branding for the default email when sendDeletionRequestedEmail is not set
15
+ * and mail-foundation is mounted. */
16
16
  readonly mailDefaults?: GdprMailDefaults;
17
17
  };
18
18
  export declare function createRequestDeletionHandler(opts?: RequestDeletionOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"request-deletion", z.ZodObject<{}, z.core.$strip>, {
@@ -4,11 +4,10 @@ import * as z from "zod";
4
4
  import { USER_STATUS } from "../../user/index.js";
5
5
  import { isGdprMailDeliveryAvailable, makeDefaultDeletionRequestedEmail, } from "../lib/default-mailers.js";
6
6
  import { resolveGracePeriod, startDeletionGracePeriod } from "./deletion-grace-period.js";
7
- // POST /api/user/request-deletion (S2.U5a) — DSGVO Art. 17 Forget-Antrag.
8
- // Flippt status=Active → deletionRequested, setzt gracePeriodEnd aus dem
9
- // Compliance-Profile (geteilte Logik: startDeletionGracePeriod). Account-
10
- // weite Semantik (1 User-Row global), siehe docs/plans/architecture/
11
- // user-data-rights.md "Cross-Tenant-Semantik".
7
+ // POST /api/user/request-deletion — GDPR Art. 17 erasure request.
8
+ // Changes status=Active → deletionRequested and sets gracePeriodEnd from the compliance
9
+ // profile using the shared startDeletionGracePeriod logic. The operation applies to the
10
+ // account-wide user row, so the user identity is global across tenants.
12
11
  export function createRequestDeletionHandler(opts = {}) {
13
12
  return defineWriteHandler({
14
13
  name: "request-deletion",
@@ -39,8 +38,7 @@ export function createRequestDeletionHandler(opts = {}) {
39
38
  (ctx.notify !== undefined && isGdprMailDeliveryAvailable(ctx.registry)
40
39
  ? makeDefaultDeletionRequestedEmail(ctx.notify, opts.mailDefaults)
41
40
  : undefined);
42
- // Best-effort Email-Notification. Send-Failure darf das Write nicht
43
- // killen — siehe Type-Doc oben.
41
+ // Best-effort email notification: a send failure must not abort the write.
44
42
  if (send && userEmail.length > 0) {
45
43
  try {
46
44
  await send({
@@ -56,8 +54,8 @@ export function createRequestDeletionHandler(opts = {}) {
56
54
  console.warn(`[user-data-rights:request-deletion] sendDeletionRequestedEmail failed userId=${event.user.id} tenantId=${event.user.tenantId} err=${err instanceof Error ? err.message : String(err)}`);
57
55
  }
58
56
  }
59
- // Response liefert den absoluten gracePeriodEnd-Timestamp damit
60
- // Frontend/Audit/Cleanup-Runner alle denselben Wert lesen.
57
+ // Returns the absolute gracePeriodEnd timestamp so the frontend, audit, and cleanup
58
+ // runner all use the same value.
61
59
  return {
62
60
  isSuccess: true,
63
61
  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
  import { createEventStoreExecutor, fetchOne } from "@cosmicdrift/kumiko-framework/db";
23
24
  import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
24
25
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
@@ -29,11 +30,11 @@ const crud = createEventStoreExecutor(exportJobsTable, exportJobEntity, {
29
30
  });
30
31
  const REQUEST_EXPORT_REASON = "export jobs are keyed by userId across all of the user's tenant memberships, not the caller's current tenant";
31
32
  /**
32
- * Race-Loss-Detection: createEventStoreExecutor.create catched 23505
33
- * intern + returnt `WriteFailure(UniqueViolationError)`, **kein throw**.
34
- * Wir checken den Failure-Code + constraintName um den App-side-vs-Race-
35
- * Pfad zu unterscheiden. Andere Failures (validation, version-conflict,
36
- * ...) propagieren unveraendert.
33
+ * `createEventStoreExecutor.create` catches 23505 internally and returns
34
+ * `WriteFailure(UniqueViolationError)` rather than throwing. Check the
35
+ * failure code and constraintName to distinguish the app-side and race
36
+ * paths; propagate other failures (validation, version conflicts, ...)
37
+ * unchanged.
37
38
  */
38
39
  function isActiveJobConflict(failure) {
39
40
  const error = failure.error; // @cast-boundary error-details
@@ -62,9 +63,9 @@ export const requestExportWrite = defineWriteHandler({
62
63
  // TenantDb-Wrapper would hide cross-tenant jobs.
63
64
  const existing = await findActiveJob(exportJobRunner, userId);
64
65
  if (existing) {
65
- // Snapshot-Status (kann zwischen fetchOne + Response stale werden
66
- // wenn Worker parallel den State flippt; window minimal). User
67
- // pollt fuer Live-Wahrheit ueber export-status.query.
66
+ // The returned status is a snapshot and can become stale between fetchOne
67
+ // and the response if the worker changes the state concurrently. The
68
+ // window is minimal; clients poll export-status.query for live status.
68
69
  return {
69
70
  isSuccess: true,
70
71
  data: {
@@ -74,9 +75,9 @@ export const requestExportWrite = defineWriteHandler({
74
75
  },
75
76
  };
76
77
  }
77
- // Kein active Job — neuen anlegen via crud.create. requestedFromTenantId
78
- // = aktueller Tenant des Users; Worker liest sein Compliance-Profile
79
- // aus diesem Tenant.
78
+ // No active job exists, so create one through crud.create. Persist the
79
+ // user's current tenant in requestedFromTenantId so the worker reads the
80
+ // compliance profile from that tenant.
80
81
  const result = await crud.create({
81
82
  userId,
82
83
  requestedFromTenantId: event.user.tenantId,
@@ -84,16 +85,16 @@ export const requestExportWrite = defineWriteHandler({
84
85
  tenantId: event.user.tenantId,
85
86
  }, event.user, ctx.db);
86
87
  if (!result.isSuccess) {
87
- // Race-Pfad: paralleler Klick hat zwischen findActiveJob + crud.create
88
- // einen aktiven Job angelegt → UniqueViolationError mit unserem
89
- // Constraint. Re-fetch + return existing als isExisting=true.
90
- // Andere Failures (validation, version-conflict, ...) propagieren.
88
+ // A parallel click may create an active job between findActiveJob and
89
+ // crud.create, causing UniqueViolationError for our constraint. Re-fetch
90
+ // and return the existing job with isExisting=true. Propagate other
91
+ // failures (validation, version conflicts, ...) unchanged.
91
92
  if (isActiveJobConflict(result)) {
92
93
  const winner = await findActiveJob(exportJobRunner, userId);
93
94
  if (!winner) {
94
- // Sollte nie passieren — Constraint-Violation ohne existing
95
- // active Job hiesse der Constraint matcht etwas anderes.
96
- // Original-Failure zurueckgeben damit das auffaellt.
95
+ // This should be impossible: a constraint violation without an existing
96
+ // active job would mean the constraint matched something else.
97
+ // Return the original failure so the inconsistency is visible.
97
98
  return result;
98
99
  }
99
100
  return {
@@ -107,17 +108,17 @@ export const requestExportWrite = defineWriteHandler({
107
108
  }
108
109
  return result;
109
110
  }
110
- // Happy path: neuer Job. SaveContext.id ist EntityId (number | string);
111
- // exportJobEntity hat idType:"uuid" → garantiert string, String()
112
- // schuetzt vor Drift falls jemand die Entity auf serial migriert.
111
+ // SaveContext.id is EntityId (number | string), while exportJobEntity has
112
+ // idType:"uuid" and therefore guarantees a string. String() protects
113
+ // against drift if the entity is migrated to serial.
113
114
  const created = result.data; // @cast-boundary engine-payload
114
115
  return {
115
116
  isSuccess: true,
116
117
  data: {
117
118
  jobId: String(created.id),
118
- // Snapshot: just-created → pending. Live-Wahrheit kommt ueber
119
- // export-status.query, status hier ist nice-to-have fuer das
120
- // initial UI-Render ("Anfrage angenommen, currently pending").
119
+ // This is a snapshot: a just-created job is pending. Live status comes
120
+ // from export-status.query; this value is only a convenience for the
121
+ // initial UI render ("Anfrage angenommen, currently pending").
121
122
  status: EXPORT_JOB_STATUS.Pending,
122
123
  isExisting: false,
123
124
  },
@@ -5,15 +5,16 @@ import { USER_STATUS, userTable } from "../../user/index.js";
5
5
  import { denyIfTargetOutsideAdminTenant } from "../lib/deny-if-target-outside-admin-tenant.js";
6
6
  import { isAdminActor } from "../lib/is-admin-actor.js";
7
7
  import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
8
- // POST /api/user/restrict (S2.U6) — DSGVO Art. 18 Account-Freeze.
9
- // Flippt status=Active → Restricted und revoked alle live sessions
10
- // des Users via cross-feature ctx.writeAs(sessions.revokeAllForUser).
8
+ // POST /api/user/restrict — GDPR Art. 18 account freeze.
9
+ // Flips status=Active → Restricted and revokes all live sessions
10
+ // for the user via cross-feature ctx.writeAs(sessions.revokeAllForUser).
11
11
  //
12
- // Plan-Doc-Verhalten ("Schreib-API geblockt"):
13
- // - Login geblockt: login.write.ts checked status=Restricted (Atom 3).
14
- // - Active sessions: revoked durch cross-feature-call (sessions-feature
15
- // muss gemountet sein). App-Author ohne sessions-feature kriegt einen
16
- // Boot-Resolver-Error via r.usesApi("sessions.revokeAllForUser").
12
+ // Write API behaviour:
13
+ // - Login is blocked: login.write.ts checks status=Restricted (Atom 3).
14
+ // - Active sessions are revoked through a cross-feature call (the
15
+ // sessions feature must be mounted). An app author without the
16
+ // sessions feature gets a boot resolver error via
17
+ // r.usesApi("sessions.revokeAllForUser").
17
18
  //
18
19
  // Self-service by default (userId omitted → event.user.id). An admin can
19
20
  // also target someone else's account — needed once a user is already
@@ -22,14 +23,16 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
22
23
  // themselves; only an operator path reaches it at that point.
23
24
  //
24
25
  // Tenant scope: `isAdminActor` accepts access.admin, which includes TenantAdmin
25
- // (tenant-scoped) even though the User-entity is global — see user-data-rights.md "Cross-Tenant-Semantik". Without a membership check, a TenantAdmin from tenant A could restrict/unrestrict a user who
26
- // has never been a member of tenant A. Only SystemAdmin (platform-wide)
27
- // skips the check; the target must have a membership row in the acting
28
- // admin's `event.user.tenantId` (row existence only — no active-status field).
26
+ // (tenant-scoped) even though the User entity is global — see user-data-rights.md
27
+ // "Cross-Tenant-Semantik". Without a membership check, a TenantAdmin from tenant A
28
+ // could restrict or unrestrict a user who has never been a member of tenant A.
29
+ // Only SystemAdmin (platform-wide) skips the check; the target must have a
30
+ // membership row in the acting admin's `event.user.tenantId` (row existence only —
31
+ // no active-status field).
29
32
  //
30
- // State-Transitions:
31
- // Active → Restricted ✓ (dieser Handler)
32
- // Restricted → Restricted ✗ 422 already_restricted (Idempotenz-Guard)
33
+ // State transitions:
34
+ // Active → Restricted ✓ (this handler)
35
+ // Restricted → Restricted ✗ 422 already_restricted (idempotency guard)
33
36
  // DeletionRequested → ... ✗ 422 user_not_in_active_state
34
37
  // Deleted → ... ✗ 422 user_not_in_active_state
35
38
  export const restrictAccountWrite = defineWriteHandler({
@@ -83,10 +86,10 @@ export const restrictAccountWrite = defineWriteHandler({
83
86
  }));
84
87
  }
85
88
  await updateUserLifecycle(ctx.db.unsafeRaw(), targetUserId, { status: USER_STATUS.Restricted });
86
- // Cross-Feature: alle live sessions revoken — sonst koennte der User
87
- // mit existierendem JWT bis zur Token-Expiry weiter schreiben.
88
- // ctx.writeAs(systemUser, ...) damit der privileged-Handler die
89
- // System-User-Roles im access-gate hat.
89
+ // Cross-feature: all live sessions are revoked; otherwise the user could
90
+ // continue writing with an existing JWT until token expiry.
91
+ // ctx.writeAs(systemUser, ...) gives the privileged handler the
92
+ // System user roles required by its access gate.
90
93
  const systemUser = createSystemUser(event.user.tenantId);
91
94
  await ctx.writeAs(systemUser, "sessions:write:user-session:revoke-all-for-user", {
92
95
  userId: targetUserId,
@@ -1,14 +1,14 @@
1
- // POST /userDataRights/run-forget-cleanup (S2.U5b).
1
+ // POST /userDataRights/run-forget-cleanup.
2
2
  //
3
- // Cron-Trigger fuer den Forget-Cleanup-Pipeline. Der Handler wrapt nur
4
- // `runForgetCleanup` damit er via dispatcher (System-User) aufrufbar
5
- // wird — die Pipeline-Logik selbst lebt im pure-function Modul
6
- // `run-forget-cleanup.ts` (testbar ohne dispatcher).
3
+ // Cron trigger for the forget-cleanup pipeline. The handler only wraps
4
+ // `runForgetCleanup` so the dispatcher can invoke it as the system user;
5
+ // the pipeline logic itself lives in the pure-function module
6
+ // `run-forget-cleanup.ts`, which can be tested without the dispatcher.
7
7
  //
8
- // Access: privileged — nur System-Caller (cron, ops-script). Im Cron-
9
- // Setup laeuft der Job mit `createSystemUser(...)` als executor.
8
+ // Access: privileged — only system callers (cron, ops script). In the cron
9
+ // setup, the job runs with `createSystemUser(...)` as executor.
10
10
  //
11
- // Rueckgabe: Stats fuer Operator-Monitoring (processed-count, error-list).
11
+ // Returns stats for operator monitoring (processed count, error list).
12
12
  import { access, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
13
13
  import { InternalError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
14
14
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";