@cosmicdrift/kumiko-samples 0.385.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 (395) 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/agent-tools/tool-dispatch.ts +0 -2
  7. package/packages/bundled-features/src/audit/screens.ts +1 -1
  8. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
  9. package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
  10. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
  11. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  12. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
  13. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
  14. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
  16. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
  17. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
  18. package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
  19. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
  20. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
  21. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
  22. package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
  23. package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
  24. package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
  25. package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
  26. package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
  27. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
  28. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
  29. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
  30. package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
  31. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
  32. package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
  33. package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
  34. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
  35. package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
  36. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
  37. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
  38. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
  39. package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
  40. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
  41. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
  42. package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
  43. package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
  44. package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
  45. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
  46. package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
  47. package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
  48. package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
  49. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
  50. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
  51. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
  52. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  53. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
  54. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
  55. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
  56. package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
  57. package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
  58. package/packages/bundled-features/src/auth-mfa/web/i18n.ts +3 -3
  59. package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
  60. package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
  61. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +6 -6
  62. package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
  63. package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
  64. package/packages/bundled-features/src/billing-foundation/events.ts +22 -21
  65. package/packages/bundled-features/src/billing-foundation/feature.ts +42 -43
  66. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
  67. package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +18 -17
  68. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
  69. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
  70. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +20 -20
  71. package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
  72. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
  73. package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
  74. package/packages/bundled-features/src/billing-foundation/types.ts +64 -65
  75. package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +4 -4
  76. package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
  77. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
  78. package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
  79. package/packages/bundled-features/src/cap-counter/enforce-cap.ts +64 -67
  80. package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
  81. package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
  82. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
  83. package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
  84. package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +1 -1
  85. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
  86. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
  87. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  88. package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
  89. package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
  90. package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
  91. package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
  92. package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
  93. package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
  94. package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
  95. package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
  96. package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
  97. package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
  98. package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
  99. package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
  100. package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
  101. package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
  102. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
  103. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  104. package/packages/bundled-features/src/config/resolver.ts +4 -4
  105. package/packages/bundled-features/src/config/table.ts +10 -10
  106. package/packages/bundled-features/src/config/write-helpers.ts +1 -1
  107. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +13 -12
  108. package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
  109. package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
  110. package/packages/bundled-features/src/custom-fields/events.ts +7 -8
  111. package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
  112. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
  113. package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
  114. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
  115. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
  116. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
  117. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
  118. package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
  119. package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
  120. package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
  121. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
  122. package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
  123. package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
  124. package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
  125. package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
  126. package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
  127. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
  128. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
  129. package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
  130. package/packages/bundled-features/src/data-retention/feature.ts +11 -18
  131. package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
  132. package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
  133. package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
  134. package/packages/bundled-features/src/data-retention/presets.ts +18 -23
  135. package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
  136. package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
  137. package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
  138. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
  139. package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
  140. package/packages/bundled-features/src/delivery/feature.ts +3 -3
  141. package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
  142. package/packages/bundled-features/src/delivery/tables.ts +9 -8
  143. package/packages/bundled-features/src/delivery/unsubscribe.ts +4 -4
  144. package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
  145. package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
  146. package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
  147. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
  148. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
  149. package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
  150. package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
  151. package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
  152. package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
  153. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
  154. package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
  155. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  156. package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
  157. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  158. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  159. package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
  160. package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
  161. package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
  162. package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
  163. package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
  164. package/packages/bundled-features/src/files/feature.ts +6 -6
  165. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
  166. package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
  167. package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
  168. package/packages/bundled-features/src/folders/feature.ts +1 -1
  169. package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
  170. package/packages/bundled-features/src/folders/web/tree.ts +5 -5
  171. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +1 -1
  172. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
  173. package/packages/bundled-features/src/form-draft/entity.ts +3 -3
  174. package/packages/bundled-features/src/form-draft/feature.ts +4 -5
  175. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
  176. package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
  177. package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
  178. package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
  179. package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
  180. package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
  181. package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
  182. package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
  183. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
  184. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
  185. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
  186. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
  187. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
  188. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
  189. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
  190. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
  191. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
  192. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +11 -11
  193. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
  194. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
  195. package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
  196. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
  197. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
  198. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +66 -67
  199. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +69 -68
  200. package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
  201. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
  202. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
  203. package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
  204. package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
  205. package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
  206. package/packages/bundled-features/src/jobs/events.ts +1 -1
  207. package/packages/bundled-features/src/jobs/feature.ts +6 -7
  208. package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
  209. package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
  210. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  211. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
  212. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  213. package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
  214. package/packages/bundled-features/src/jobs/job-run-logger.ts +14 -15
  215. package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
  216. package/packages/bundled-features/src/jobs/screens.ts +1 -1
  217. package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
  218. package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
  219. package/packages/bundled-features/src/ledger/entity.ts +5 -5
  220. package/packages/bundled-features/src/ledger/feature.ts +3 -3
  221. package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
  222. package/packages/bundled-features/src/ledger/recurring.ts +12 -12
  223. package/packages/bundled-features/src/ledger/schemas.ts +1 -1
  224. package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
  225. package/packages/bundled-features/src/ledger/web/index.ts +7 -7
  226. package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
  227. package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
  228. package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
  229. package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
  230. package/packages/bundled-features/src/mail-foundation/feature.ts +26 -27
  231. package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
  232. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
  233. package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
  234. package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
  235. package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
  236. package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
  237. package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
  238. package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
  239. package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
  240. package/packages/bundled-features/src/managed-pages/table.ts +16 -15
  241. package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
  242. package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
  243. package/packages/bundled-features/src/notes-history/entity.ts +4 -4
  244. package/packages/bundled-features/src/notes-history/feature.ts +2 -2
  245. package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
  246. package/packages/bundled-features/src/page-render/layout.ts +9 -9
  247. package/packages/bundled-features/src/page-render/markdown.ts +11 -11
  248. package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
  249. package/packages/bundled-features/src/personal-access-tokens/feature.ts +5 -5
  250. package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
  251. package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
  252. package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
  253. package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
  254. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  255. package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
  256. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  257. package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
  258. package/packages/bundled-features/src/seo/feature.ts +3 -3
  259. package/packages/bundled-features/src/sessions/constants.ts +1 -1
  260. package/packages/bundled-features/src/sessions/feature.ts +9 -9
  261. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
  262. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
  263. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  264. package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
  265. package/packages/bundled-features/src/sessions/screens.ts +2 -2
  266. package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
  267. package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
  268. package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
  269. package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +1 -1
  270. package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
  271. package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
  272. package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
  273. package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
  274. package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
  275. package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
  276. package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
  277. package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
  278. package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
  279. package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
  280. package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
  281. package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
  282. package/packages/bundled-features/src/subscription-stripe/feature.ts +44 -43
  283. package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
  284. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
  285. package/packages/bundled-features/src/subscription-stripe/runtime.ts +2 -2
  286. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
  287. package/packages/bundled-features/src/tags/feature.ts +3 -3
  288. package/packages/bundled-features/src/tags/screens.ts +1 -1
  289. package/packages/bundled-features/src/template-resolver/api.ts +10 -10
  290. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +5 -5
  291. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +1 -1
  292. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
  293. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
  294. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
  295. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
  296. package/packages/bundled-features/src/template-resolver/seeding.ts +4 -4
  297. package/packages/bundled-features/src/template-resolver/table.ts +13 -14
  298. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
  299. package/packages/bundled-features/src/tenant/feature.ts +7 -7
  300. package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +1 -1
  301. package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
  302. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  303. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
  304. package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +1 -1
  305. package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
  306. package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
  307. package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
  308. package/packages/bundled-features/src/tenant/screens.ts +1 -1
  309. package/packages/bundled-features/src/tenant/seeding.ts +19 -18
  310. package/packages/bundled-features/src/tenant/testing.ts +5 -6
  311. package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
  312. package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
  313. package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
  314. package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
  315. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  316. package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
  317. package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
  318. package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
  319. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  320. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
  321. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +2 -2
  322. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
  323. package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
  324. package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
  325. package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
  326. package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
  327. package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
  328. package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
  329. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
  330. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
  331. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -8
  332. package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
  333. package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
  334. package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
  335. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
  336. package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
  337. package/packages/bundled-features/src/user/schema/user.ts +45 -44
  338. package/packages/bundled-features/src/user/seeding.ts +40 -40
  339. package/packages/bundled-features/src/user/testing.ts +3 -3
  340. package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
  341. package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
  342. package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
  343. package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
  344. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
  345. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
  346. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
  347. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
  348. package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
  349. package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
  350. package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
  351. package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
  352. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
  353. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
  354. package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
  355. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
  356. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
  357. package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
  358. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
  359. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
  360. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +166 -276
  361. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +85 -107
  362. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
  363. package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
  364. package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
  365. package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
  366. package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
  367. package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
  368. package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
  369. package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
  370. package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
  371. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
  372. package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
  373. package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
  374. package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
  375. package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
  376. package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
  377. package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
  378. package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
  379. package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
  380. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
  381. package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
  382. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
  383. package/packages/bundled-features/src/user-profile/index.ts +3 -3
  384. package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
  385. package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
  386. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
  387. package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
  388. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
  389. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
  390. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  391. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
  392. package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
  393. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
  394. package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
  395. package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
@@ -1,39 +1,32 @@
1
1
  import { createFallbackLogger, type Logger } from "@cosmicdrift/kumiko-framework/logging";
2
- // Forget-Cleanup-Runner (S2.U5b) — pure-Function Pipeline.
2
+ // Forget-cleanup runner — pure-function pipeline.
3
3
  //
4
- // After the grace period (S2.U5a sets gracePeriodEnd) this runner iterates
5
- // over all users in DeletionRequested state and triggers the EXT_USER_DATA
6
- // delete hooks in every tenant the user is or was a member of (membership
7
- // history from kumiko_events).
4
+ // After the grace period, this runner processes users in DeletionRequested
5
+ // state and triggers EXT_USER_DATA delete hooks in every tenant where the user
6
+ // is or was a member, using membership history from kumiko_events.
8
7
  //
9
- // **Cross-tenant iteration:** a forget request in tenant A must remove the
10
- // user's data in ALL of their tenants, including tenants they left before
11
- // the request — see
12
- // docs/plans/architecture/user-data-rights.md "Cross-Tenant-Semantik".
8
+ // **Cross-tenant iteration:** a request in tenant A must remove the user's data
9
+ // in all of their tenants, including tenants they left before the request.
13
10
  //
14
- // **Strategy-Dispatch:** Pro Entity entscheidet die data-retention-
15
- // policy (per-Tenant Override moeglich), ob "delete" oder "anonymize"
16
- // gefahren wird. blockDelete (Aufbewahrungs-Pflicht) ergibt zwingend
17
- // "anonymize" — Daten-Objekt bleibt physisch da, Personen-Bezug raus.
11
+ // **Strategy dispatch:** each entity's data-retention policy determines whether
12
+ // to run "delete" or "anonymize". A blockDelete policy always produces
13
+ // "anonymize", leaving the data object in place while removing its association
14
+ // with the person.
18
15
  //
19
- // **Per-User-Atomicity (advisor-pinned):** Jeder User wird in einer
20
- // eigenen Sub-Transaction abgewickelt (db.transaction → SAVEPOINT wenn
21
- // Outer-Tx aktiv, BEGIN sonst). Folge: ein failing Hook bei User A
22
- // rollt nur dessen Sub-Tx zurueck, User B + bisherige User-Status-Flips
23
- // bleiben commit-able. Ohne diese Sub-Tx wuerde der Outer-Dispatcher-Tx
24
- // (alle writeHandler laufen in `db.begin(...)`) den ganzen
25
- // Cleanup-Run beim ersten Hook-Throw zurueckrollen.
16
+ // **Per-user atomicity:** each user runs in its own sub-transaction
17
+ // (db.transaction → SAVEPOINT when an outer transaction is active, BEGIN
18
+ // otherwise). A failing hook rolls back only that user's transaction, while
19
+ // other users and previously completed status changes remain commit-able.
20
+ // Without this isolation, the outer dispatcher transaction would roll back the
21
+ // entire cleanup run on the first hook failure.
26
22
  //
27
- // **Idempotenz:** Hooks sind idempotent designed (siehe
28
- // engine/extensions/user-data.ts). Doppellauf nach Crash-Recovery muss
29
- // safe sein. Status-Flip auf "Deleted" am Ende sorgt dafuer, dass next
30
- // Lauf den User nicht mehr findet.
23
+ // **Idempotency:** hooks are designed to be idempotent, so rerunning after
24
+ // crash recovery is safe. Flipping the status to "Deleted" at the end keeps
25
+ // the user out of subsequent runs.
31
26
  //
32
- // **Error-Handling:** Ein hook der wirft soll den Lauf NICHT stoppen —
33
- // andere User sollen weiter abgearbeitet werden. Errors werden
34
- // gesammelt + zurueckgegeben fuer Operator-Visibility. Ein User mit
35
- // gefailten Hooks bleibt im DeletionRequested-Status (next Lauf
36
- // retried automatisch).
27
+ // **Error handling:** a throwing hook must not stop the run. Errors are
28
+ // collected and returned for operator visibility, while a user with failed
29
+ // hooks remains in DeletionRequested for automatic retry.
37
30
 
38
31
  import {
39
32
  configuredPiiSubjectKms,
@@ -90,18 +83,15 @@ import { updateUserLifecycle } from "./lib/update-user-lifecycle.ts";
90
83
  type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
91
84
 
92
85
  /**
93
- * Notification-Callback fuer den Forget-Cleanup-Pfad (Atom 5b). Pattern
94
- * matched Atom 5 (Export). Throw bubbelt zum r.job-Wrap; jobs-feature
95
- * persistiert den failed-Run in jobRunsTable (siehe
96
- * jobs/__tests__/jobs-feature.integration.ts Scenario 2 — der throw-
97
- * Pfad eines r.job-handlers wird dort gepinnt).
86
+ * Callback for the forget-cleanup path. A callback failure propagates to the
87
+ * r.job wrapper so the jobs feature can persist the failed run.
98
88
  *
99
- * **executedAt:** Zeitpunkt des delete-Flips. Wird als ISO-String
100
- * uebergeben damit App-Author den frei in Email-Template einbauen kann.
89
+ * **executedAt:** The time of the delete flip, passed as an ISO string so the
90
+ * app author can insert it into an email template.
101
91
  *
102
- * **tenantIds:** alle Memberships die der User vor dem Delete hatte.
103
- * Email-Template kann das nutzen ("dein Account in Tenant X+Y wurde
104
- * geloescht"). Bei orphan-User (0 Memberships) ist die Liste leer.
92
+ * **tenantIds:** All memberships the user had before deletion. An email
93
+ * template can use this to describe which tenant accounts were deleted. The
94
+ * list is empty for an orphan user with no memberships.
105
95
  */
106
96
  export type SendDeletionExecutedEmailFn = (args: {
107
97
  readonly userId: string;
@@ -117,14 +107,13 @@ export interface RunForgetCleanupArgs {
117
107
  readonly db: DbRunner;
118
108
  readonly registry: Registry;
119
109
  /**
120
- * Now-Injection — Tests koennen den Wert pinnen ohne Date-Mock.
121
- * Pattern aus data-retention/keep-for.ts (advisor-pinned).
110
+ * Injects the current time so tests can control it without mocking the date.
122
111
  */
123
112
  readonly now: Instant;
124
113
 
125
- /** Atom 5b — Email-Notification beim delete-flip. Optional;
126
- * ohne Callback laeuft Worker still (User hatte schon
127
- * request-deletion-Email + grace-period-Erinnerung). */
114
+ /** Sends an optional email notification after the delete flip. Without a
115
+ * callback, the worker remains silent because the user already received the
116
+ * request-deletion email and grace-period reminder. */
128
117
  readonly sendDeletionExecutedEmail?: SendDeletionExecutedEmailFn;
129
118
 
130
119
  /**
@@ -147,8 +136,8 @@ export interface RunForgetCleanupArgs {
147
136
  readonly kms?: KmsAdapter;
148
137
 
149
138
  /**
150
- * Search adapter for purging derived Meili docs of searchable subject-PII
151
- * (#1610). Same seam as forget-subject. Omitted → no search purge (apps
139
+ * Search adapter for purging derived Meili docs of searchable subject-PII.
140
+ * Same seam as forget-subject. Omitted → no search purge (apps
152
141
  * without Meili).
153
142
  */
154
143
  readonly searchAdapter?: SearchAdapter;
@@ -162,7 +151,7 @@ export interface RunForgetCleanupArgs {
162
151
  */
163
152
  readonly tenantModel?: TenantUserModel;
164
153
 
165
- // fw#2914 — sourced from the owning job's ctx (_escapeHatchAuditSink,
154
+ // Sourced from the owning job's ctx (_escapeHatchAuditSink,
166
155
  // systemUser.id); attributes+audits any EXT_USER_DATA usage's declared
167
156
  // escapeHatch when its delete hook calls ctx.db.unsafeRaw().
168
157
  readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
@@ -196,11 +185,11 @@ export interface ForgetCleanupIncomplete {
196
185
  }
197
186
 
198
187
  export interface RunForgetCleanupResult {
199
- /** User die in diesem Lauf von DeletionRequested → Deleted geflippt wurden. */
188
+ /** Users flipped from DeletionRequested to Deleted during this run. */
200
189
  readonly processedUserIds: readonly string[];
201
- /** Anzahl entity-hook-calls die wirklich gelaufen sind (success oder fail). */
190
+ /** Number of entity-hook calls that actually ran, whether successful or failed. */
202
191
  readonly hookCallsAttempted: number;
203
- /** Hook-Errors fuer Operator-Visibility. Lauf bricht nicht ab — siehe Header. */
192
+ /** Hook errors collected for operator visibility; the run continues. */
204
193
  readonly errors: readonly ForgetCleanupError[];
205
194
  /** Hooks that reported `{status:"incomplete"}` — partial success, user still Deleted. */
206
195
  readonly incomplete: readonly ForgetCleanupIncomplete[];
@@ -276,8 +265,8 @@ export async function runForgetCleanup(
276
265
  return { processedUserIds: [], hookCallsAttempted: 0, errors: [], incomplete: [] };
277
266
  }
278
267
 
279
- // Step 2: Sammle alle EXT_USER_DATA-Usages einmalig — Liste der
280
- // (entityName, deleteHook)-Pairs aller registrierten Provider-Features.
268
+ // Step 2: collect all EXT_USER_DATA usages once, i.e. the
269
+ // (entityName, deleteHook) pairs of all registered provider features.
281
270
  const usages = registry.getExtensionUsages(EXT_USER_DATA);
282
271
  const hookEntries: HookEntry[] = usages
283
272
  .map((u): HookEntry | null => {
@@ -304,7 +293,7 @@ export async function runForgetCleanup(
304
293
  const processedUserIds: string[] = [];
305
294
  let hookCallsAttempted = 0;
306
295
 
307
- // Step 3: Pro User iterieren — eigene Sub-Tx pro User (siehe Header).
296
+ // Step 3: iterate per user, each in its own sub-transaction (see header).
308
297
  for (const user of dueUsers) {
309
298
  const userResult = await processUser({
310
299
  db,
@@ -325,17 +314,14 @@ export async function runForgetCleanup(
325
314
  if (userResult.success) {
326
315
  processedUserIds.push(user.id);
327
316
 
328
- // Atom 5b — Email-Notification nach success-flip. userEmail wurde
329
- // VOR der Tx gecacht (user-Hook anonymisiert in der Tx).
317
+ // userEmail was cached BEFORE the transaction because the user hook
318
+ // anonymizes it inside the transaction.
330
319
  //
331
- // Best-effort: ein Email-Throw fuer User A darf nicht den Batch
332
- // killen — User A ist bereits geloescht (Sub-Tx committed), und
333
- // die Users B, C, ... muessen noch verarbeitet werden. Throw waere
334
- // hier ein Bug: r.job-Wrap markiert den Run failed, retry findet
335
- // keine User mehr im DeletionRequested+grace-expired-Status (alle
336
- // schon Deleted) → silent miss. console.warn ist die einzige
337
- // Operator-Sichtbarkeit — runForgetCleanup-args fuehren AppContext.
338
- // log aktuell nicht durch (pure-function-Pattern).
320
+ // The email is best-effort: a send failure must not abort the batch after the
321
+ // user's sub-transaction has committed. Otherwise the job would be marked
322
+ // failed, and a retry would find no eligible users, causing a silent miss.
323
+ // console.warn is the only operator-visible reporting path because the
324
+ // runForgetCleanup args do not carry AppContext.log (pure-function pattern).
339
325
  if (sendDeletionExecutedEmail && userResult.userEmailBeforeDelete) {
340
326
  try {
341
327
  await sendDeletionExecutedEmail({
@@ -363,13 +349,14 @@ interface ProcessUserResult {
363
349
  readonly hookCallsAttempted: number;
364
350
  readonly errors: readonly ForgetCleanupError[];
365
351
  readonly incomplete: readonly ForgetCleanupIncomplete[];
366
- /** Atom 5b: userEmail VOR Tx gecacht (user-Hook anonymisiert in Tx).
367
- * null wenn user-Row beim Pre-Tx-Lookup nicht (mehr) existiert oder
368
- * email leer ist. */
352
+ /** The email address cached before the transaction because the user hook
353
+ * anonymizes it in the transaction. Null means the row was absent or the
354
+ * email was empty during the pre-transaction lookup. */
369
355
  readonly userEmailBeforeDelete: string | null;
370
356
  /** user.locale VOR Tx gecacht — Default-Mailer rendert in Empfaenger-Sprache. */
371
357
  readonly userLocaleBeforeDelete: string | null;
372
- /** Tenant-Memberships VOR Tx — Email-Template kann das nutzen. */
358
+ /** Tenant memberships captured before the transaction deletes them, for use by
359
+ * the email template. */
373
360
  readonly tenantIdsBeforeDelete: readonly TenantId[];
374
361
  }
375
362
 
@@ -391,11 +378,9 @@ async function processUser(args: {
391
378
  const incomplete: Omit<ForgetCleanupIncomplete, "rolledBack">[] = [];
392
379
  let hookCallsAttempted = 0;
393
380
 
394
- // Atom 5b — userEmail VOR der Tx cachen. user-Hook (user-data-rights-
395
- // defaults) anonymisiert email/displayName/passwordHash IN der Tx.
396
- // Nach der Tx ist email = "deleted-{id}@{tenant}.example" oder NULL.
397
- // Memory-cache laesst Atom-5b-Callback nach success-flip den
398
- // ORIGINAL-email an App-Author-Callback geben.
381
+ // Cache the email before the transaction because the user hook anonymizes
382
+ // email/displayName/passwordHash inside it. The cache lets the callback after
383
+ // a successful status flip receive the original email address.
399
384
  const userPreTx = await fetchOne<{ email: string | null; locale: string | null }>(db, userTable, {
400
385
  id: userId,
401
386
  });
@@ -405,13 +390,12 @@ async function processUser(args: {
405
390
  : null;
406
391
  const userLocaleBeforeDelete = userPreTx?.locale ?? null;
407
392
 
408
- // Memberships fuer diesen User holen — alle Tenants in denen er Mitglied ist.
409
393
  const memberships = await selectMany<{ tenantId: TenantId }>(db, tenantMembershipsTable, {
410
394
  userId,
411
395
  });
412
- // tenant-Liste fuer Atom 5b Email — Memberships VOR Tx, weil hooks
413
- // memberships in der Tx loeschen. Orphan-User (0 memberships) liefert
414
- // [] in Email-args; App-Author-Template kann das case-handlen.
396
+ // Capture memberships before the transaction deletes them so the email
397
+ // callback receives the original tenant list. Orphan users provide an empty
398
+ // list.
415
399
  const tenantIdsBeforeDelete: readonly TenantId[] = memberships.map((m) => m.tenantId);
416
400
 
417
401
  // Departed tenants: removeMember deletes only the projection row, so data
@@ -427,19 +411,17 @@ async function processUser(args: {
427
411
  const tenantList: TenantId[] =
428
412
  hookTenantIds.length > 0 ? hookTenantIds : [SYSTEM_TENANT_ID_FOR_ORPHANS];
429
413
 
430
- // Per-User-Sub-Tx: hooks + status-flip atomar. Bei Hook-Throw rollt
431
- // nur dieser User zurueck, andere User bleiben commit-fest. Die Sub-Tx
432
- // nestet korrekt: eine Top-Level-Connection oeffnet sie via `.begin`
433
- // (BEGIN), eine TransactionSql — der Fall im Dispatcher, wo jeder
434
- // writeHandler bereits IN der Outer-Tx laeuft — via `.savepoint`
435
- // (SAVEPOINT). Siehe runInSubTransaction.
414
+ // Hooks and the status flip run atomically per user. A hook failure rolls back
415
+ // only that user, while other users remain commit-able. runInSubTransaction
416
+ // uses `.begin` (BEGIN) for a top-level connection and `.savepoint`
417
+ // (SAVEPOINT) when the dispatcher is already inside an outer transaction.
436
418
  let txSucceeded = false;
437
419
  let currentTenantId: TenantId | null = null;
438
420
  let currentEntityName: string | null = null;
439
421
  try {
440
422
  await runInSubTransaction(db, async (tx) => {
441
423
  // Discover + purge search docs for this subject BEFORE any delete
442
- // hook runs (fw#1611). purgeSearchDocumentsForSubject finds candidate
424
+ // hook runs. purgeSearchDocumentsForSubject finds candidate
443
425
  // rows via a live SELECT on each entity's read-table — a hook that
444
426
  // hard-deletes a row (UserDataDeleteStrategy "delete") makes that row
445
427
  // invisible to a purge running afterward, permanently stranding its
@@ -548,10 +530,10 @@ async function processUser(args: {
548
530
  eraseReason: "user-data-rights:forget",
549
531
  },
550
532
  );
551
- // Blind-Index-Sweep (#818): bidx-Spalten des erased Subjects sofort
552
- // nullen — sonst bliebe der deterministische HMAC bis zum nächsten
553
- // Rebuild equality-matchbar (Linkage-Fenster). In der Sub-Tx, damit
554
- // ein Rollback auch den Sweep zurücknimmt.
533
+ // Null blind-index columns immediately after erasing the subject key; otherwise
534
+ // the deterministic HMAC would remain equality-matchable until the next
535
+ // rebuild. Keeping this in the sub-transaction makes the sweep rollback
536
+ // with the rest of the user's cleanup.
555
537
  await nullBlindIndexesForSubject(
556
538
  tx,
557
539
  registry.features,
@@ -559,10 +541,9 @@ async function processUser(args: {
559
541
  );
560
542
  }
561
543
 
562
- // Status-Flip in derselben Sub-Tx. Falls einer der Hooks oben
563
- // geworfen hat, kommen wir hier nicht an — die Tx rollback'd
564
- // alles, der User bleibt im DeletionRequested-Status, naechster
565
- // Run retried.
544
+ // Flip the status in the same sub-transaction. If a hook throws, execution
545
+ // never reaches this point, so the user remains in DeletionRequested for
546
+ // the next retry.
566
547
  await updateUserLifecycle(
567
548
  tx,
568
549
  userId,
@@ -572,9 +553,8 @@ async function processUser(args: {
572
553
  txSucceeded = true;
573
554
  });
574
555
  } catch (e) {
575
- // currentTenantId/currentEntityName tracken den Failing-Hook —
576
- // Operator sieht "Hook fileRef in Tenant A failed for user X" statt
577
- // generisches "<sub-transaction>".
556
+ // Track the failing hook so operators see its tenant and entity instead of a
557
+ // generic sub-transaction error.
578
558
  errors.push({
579
559
  userId,
580
560
  tenantId: currentTenantId ?? ("" as TenantId),
@@ -621,10 +601,9 @@ function resolveForgetFileBytesDeleter(
621
601
  };
622
602
  }
623
603
 
624
- // Pseudo-Tenant fuer User ohne aktive Memberships. RFC4122-konforme
625
- // Null-UUID. Tenant-scoped Hooks finden hier nichts (no-op),
626
- // tenant-agnostische Hooks (z.B. user) operieren auf der globalen
627
- // User-Row und ignorieren tenantId.
604
+ // Use a pseudo-tenant for users with no active or historical memberships.
605
+ // Tenant-scoped hooks find nothing there, while tenant-agnostic hooks such as
606
+ // the user hook still operate on the global user row and ignore tenantId.
628
607
  const SYSTEM_TENANT_ID_FOR_ORPHANS = "00000000-0000-0000-0000-000000000000" as TenantId;
629
608
 
630
609
  // "single-user" requires BOTH the app config AND a runtime sole-member check —
@@ -653,7 +632,7 @@ async function resolveEffectiveTenantModel(
653
632
  // removeMemberWrite only deletes the tenant-membership PROJECTION row — the
654
633
  // event history survives forever, and a departed member's own tenant-scoped
655
634
  // data rows (contributor-style records with no per-user column) can
656
- // outlive their membership (ent#346). A live count of 1 is therefore not
635
+ // outlive their membership. A live count of 1 is therefore not
657
636
  // enough: a tenant that ever had >1 distinct member must stay multi-user for
658
637
  // the forget path even after everyone but one is removed, or the sole
659
638
  // remaining member's forget request wipes the departed co-member's leftover
@@ -663,7 +642,7 @@ async function resolveEffectiveTenantModel(
663
642
  // tenant SystemAdmin adds land under the actor's tenant_id.
664
643
  //
665
644
  // Counting created-ROWS against distinct userIds latched a tenant to
666
- // multi-user as soon as its sole member was removed and re-added (#2608) —
645
+ // multi-user as soon as its sole member was removed and re-added —
667
646
  // two created-events, one identity, no co-member ever. The fail-safe only
668
647
  // needs to cover payloads whose userId cannot be READ, so the unreadable
669
648
  // remainder is measured directly instead of inferred from the row count.
@@ -714,14 +693,13 @@ async function readHistoricalMembershipTenantIds(
714
693
  return rows.map((row) => row.tenant_id as TenantId); // @cast-boundary db-row
715
694
  }
716
695
 
717
- // Mapping retention.strategy → user-data-rights.UserDataDeleteStrategy.
718
- // - "anonymize" / "blockDelete" → "anonymize" (Aufbewahrungs-Pflicht
719
- // blockDelete: Daten muessen physisch bleiben, nur Personen-Bezug raus)
720
- // - "hardDelete" / "softDelete" / null → "delete" (Default)
696
+ // Map retention.strategy to user-data-rights.UserDataDeleteStrategy.
697
+ // - "anonymize" / "blockDelete" → "anonymize": retention requirements keep
698
+ // the data physically present while removing its association with the person.
699
+ // - "hardDelete" / "softDelete" / null → "delete" (default).
721
700
  //
722
- // Eigene Funktion damit Strategie-Drift zwischen retention-strategies
723
- // und user-data-rights-Hooks an EINER Stelle dokumentiert + getestet
724
- // werden kann (siehe run-forget-cleanup.test.ts).
701
+ // Keeping this mapping in one function makes drift between retention strategies
702
+ // and user-data-rights hooks explicit and testable.
725
703
  export function policyToStrategy(
726
704
  retentionStrategy: "hardDelete" | "softDelete" | "anonymize" | "blockDelete" | null,
727
705
  ): UserDataDeleteStrategy {
@@ -1,25 +1,24 @@
1
1
  import { createFallbackLogger, type Logger } from "@cosmicdrift/kumiko-framework/logging";
2
- // User-Data-Export-Pipeline (S2.U3) — DSGVO Art. 15 (Auskunft) +
3
- // Art. 20 (Datenportabilität).
2
+ // User data export pipeline — GDPR Art. 15 (access) +
3
+ // Art. 20 (data portability).
4
4
  //
5
- // Pure Pipeline-Function: ruft alle EXT_USER_DATA-export-Hooks ueber
6
- // alle Tenant-Memberships eines Users + sammelt das Ergebnis als
7
- // strukturiertes Bundle.
5
+ // Pure pipeline function: invokes all EXT_USER_DATA export hooks for
6
+ // all of a user's tenant memberships and collects the results into a
7
+ // structured bundle.
8
8
  //
9
- // **Async ZIP + Storage (S2.U3-ext) bewusst spaeter:** Diese Foundation
10
- // gibt das Bundle inline zurueck (JSON-Object). Apps mit grossen
11
- // File-Mengen brauchen einen Job-Wrap der das Bundle nach S3 / lokalem
12
- // Storage schreibt + signed-URLs ergibt — kommt drauf wenn ein realer
13
- // User-Case >10MB Output produziert. Bis dahin reicht inline fuer
14
- // 99% der Apps (User-Profil + Files-Metadata + Aktivitaeten-History).
9
+ // **Async ZIP + storage deferred:** This foundation returns the bundle
10
+ // inline as a JSON object. Apps with large file collections need a job
11
+ // wrapper that writes the bundle to S3 or local storage and produces
12
+ // signed URLs; that can be added when a real use case produces more than
13
+ // 10 MB of output. Until then, inline output covers 99% of apps
14
+ // (user profiles, file metadata, and activity history).
15
15
  //
16
- // **Cross-Tenant-Iteration:** Wie beim Forget-Pfad — User-Daten in
17
- // Tenant A + B kommen in dasselbe Bundle. Plan-Doc:
18
- // docs/plans/architecture/user-data-rights.md "Cross-Tenant-Semantik".
16
+ // **Cross-tenant iteration:** As with the forget path, data from Tenant A
17
+ // and Tenant B is included in the same bundle.
19
18
  //
20
- // **PII-Surface:** Hooks definieren selbst welche Felder ins Bundle
21
- // landen. user-data-rights-defaults/hooks/user.userdata-hook expose
22
- // expliziert KEIN passwordHash + KEINE roles (privileged columns).
19
+ // **PII surface:** Hooks decide which fields enter the bundle.
20
+ // user-data-rights-defaults/hooks/user.userdata-hook explicitly exposes
21
+ // neither passwordHash nor roles (privileged columns).
23
22
 
24
23
  import {
25
24
  configuredPiiSubjectKms,
@@ -59,7 +58,7 @@ export interface RunUserExportArgs {
59
58
  readonly registry: Registry;
60
59
  readonly userId: string;
61
60
  readonly now: Instant;
62
- // fw#2914 — sourced from the owning job's ctx (_escapeHatchAuditSink,
61
+ // Sourced from the owning job's ctx (_escapeHatchAuditSink,
63
62
  // systemUser.id); attributes+audits any EXT_USER_DATA usage's declared
64
63
  // escapeHatch when its export hook calls ctx.db.unsafeRaw().
65
64
  readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
@@ -71,33 +70,33 @@ export interface UserExportFileRef {
71
70
  readonly fileRefId: string;
72
71
  readonly storageKey: string;
73
72
  readonly fileName: string;
74
- /** Tenant in dem die Datei haengt — gleicher fileRefId kann nicht ueber Tenants geteilt sein. */
73
+ /** Tenant containing the file; the same fileRefId cannot be shared across tenants. */
75
74
  readonly tenantId: TenantId;
76
75
  /**
77
- * ZIP-internal Pfad unter dem die Datei im Export-ZIP landet. Reader-
78
- * Tools (Compliance-Audit, Self-Service-Portal) verlinken bundle.json
79
- * fileRefs[] auf die files/-Pfade ueber dieses Feld. Garantiert
80
- * path-traversal-frei via sanitizeZipFilename.
76
+ * ZIP-internal path under which the file is placed in the export ZIP.
77
+ * Reader tools (compliance audit, self-service portal) use this field
78
+ * to link bundle.json fileRefs[] to the files/-paths. Guaranteed to be
79
+ * safe from path traversal via sanitizeZipFilename.
81
80
  */
82
81
  readonly zipPath: string;
83
82
  }
84
83
 
85
84
  export interface UserExportTenantSection {
86
85
  readonly tenantId: TenantId;
87
- /** Pro Entity ein Snippet ({entity, rows[]}). Empty wenn Hook null returned. */
86
+ /** One snippet per entity ({entity, rows[]}); empty when the hook returns null. */
88
87
  readonly entities: ReadonlyArray<UserDataExportSnippet>;
89
88
  }
90
89
 
91
90
  export interface UserExportBundle {
92
91
  readonly userId: string;
93
- /** ISO-8601 Generation-Timestamp — fuer Audit-Trail. */
92
+ /** ISO-8601 generation timestamp for the audit trail. */
94
93
  readonly generatedAt: string;
95
- /** Pro Tenant in dem User Mitglied ist eine Section. Orphan-User → tenants=[]. */
94
+ /** One section for each tenant the user belongs to; orphan users have tenants=[]. */
96
95
  readonly tenants: ReadonlyArray<UserExportTenantSection>;
97
96
  /**
98
- * Flat-Liste aller fileRefs aus allen Tenant-Sections — der spaetere
99
- * ZIP-Bau-Job iteriert hier durch + zieht Binaries aus dem Storage-
100
- * Provider. Bis dahin ist das die Stueckliste fuer den Operator.
97
+ * Flat list of all fileRefs from every tenant section. The later ZIP
98
+ * construction job iterates over this list and retrieves binaries from
99
+ * the storage provider. Until then, it is the operator's manifest.
101
100
  */
102
101
  readonly fileRefs: ReadonlyArray<UserExportFileRef>;
103
102
  }
@@ -134,39 +133,39 @@ function buildHookDb(
134
133
  }
135
134
 
136
135
  /**
137
- * Pure function: iteriert alle EXT_USER_DATA-Hooks pro Tenant (Cross-
138
- * Tenant-Memberships) + sammelt Snippets in ein UserExportBundle.
136
+ * Pure function: invokes all EXT_USER_DATA hooks for each tenant
137
+ * membership and collects snippets into a UserExportBundle.
139
138
  *
140
- * **Memory-Footprint:**
139
+ * **Memory footprint:**
141
140
  *
142
- * Der gesamte Storage-Pfad ist streaming-bound:
143
- * - File-Binaries via provider.readStream → chunk-streaming, skaliert
144
- * auf beliebige File-Sizes ohne Heap-Spike.
145
- * - ZIP-Schreiben via provider.writeStream — local nutzt fs.createWriteStream,
146
- * S3 nutzt lib-storage.Upload (multipart, ~20MB Heap-Bound bei 4
147
- * concurrent parts).
141
+ * The entire storage path is streaming-bound:
142
+ * - File binaries use provider.readStream and chunk streaming, scaling
143
+ * to arbitrary file sizes without a heap spike.
144
+ * - ZIP writing uses provider.writeStream; local storage uses
145
+ * fs.createWriteStream, while S3 uses lib-storage.Upload (multipart,
146
+ * with an approximately 20 MB heap bound at 4 concurrent parts).
148
147
  *
149
- * **EINZIGER nicht-streaming Pfad:** das Bundle-Object selbst (bundle.json
150
- * Inhalt). Es wird komplett in-memory gebaut bevor `bundleToZipEntries`
151
- * es als ZIP-Entry yieldet. Hooks geben Snippets als Plain-Objects
152
- * zurueck (siehe UserDataExportSnippet).
148
+ * **Only non-streaming path:** the bundle object itself (the bundle.json
149
+ * content). It is built entirely in memory before `bundleToZipEntries`
150
+ * yields it as a ZIP entry. Hooks return snippets as plain objects
151
+ * (see UserDataExportSnippet).
153
152
  *
154
- * **Threshold:** Web-App mit ~500 Tabellen-Rows pro User ≈ 500 KB JSON.
155
- * 50k Rows ≈ 50 MB. 100k+ Rows pro User (z.B. langjaehrige Mietportal-
156
- * Logs) macht Heap-Druck merkbar.
153
+ * **Threshold:** A web app with approximately 500 table rows per user
154
+ * produces about 500 KB of JSON. 50k rows produce about 50 MB. 100k+
155
+ * rows per user (for example, long-running rental-portal logs) create
156
+ * noticeable heap pressure.
157
157
  *
158
- * **Wenn das knapp wird:** runUserExport auf AsyncIterable-Form refactoren —
159
- * Hooks yielden snippets statt Object-Returns; Bundle-Schema bekommt
160
- * JSON-Lines-Format. bundleToZipEntries wuerde line-by-line streamen.
161
- * Eigener Sprint, nicht-trivialer Schema-Bruch.
158
+ * **If this becomes a problem:** refactor runUserExport to use an
159
+ * AsyncIterable; hooks would yield snippets instead of returning objects,
160
+ * and the bundle schema would use JSON Lines. bundleToZipEntries would
161
+ * stream entries line by line. This is a non-trivial schema change.
162
162
  *
163
- * **Operator-Signal:** wenn bundle.json im ZIP > 100 MB ist, sollte
164
- * Telemetry triggern + Schema-Refactor evaluieren.
163
+ * **Operator signal:** if bundle.json exceeds 100 MB in the ZIP, trigger
164
+ * telemetry and evaluate a schema refactor.
165
165
  */
166
166
  export async function runUserExport(args: RunUserExportArgs): Promise<UserExportBundle> {
167
167
  const { db, registry, userId, now } = args;
168
168
 
169
- // Memberships → Tenant-Liste fuer Hook-Iteration.
170
169
  const memberships = await selectMany<{ tenantId: TenantId }>(db, tenantMembershipsTable, {
171
170
  userId,
172
171
  });
@@ -220,15 +219,15 @@ export async function runUserExport(args: RunUserExportArgs): Promise<UserExport
220
219
  tenants.push({ tenantId, entities });
221
220
  }
222
221
 
223
- // Edge-Case "0 Memberships": Tenant-agnostische Hooks (z.B. user-Hook)
224
- // wuerden bei Cross-Tenant-Iteration mehrfach laufen. Bei orphan-User
225
- // (kein Membership) wuerden sie gar nicht laufen. Loesung wie beim
226
- // Forget-Runner: einen Sonder-Lauf mit einem Pseudo-Tenant ergaenzen
227
- // damit die globale User-Row IM Bundle landet. Tenant-scoped Hooks
228
- // sind no-op.
222
+ // Edge case "0 memberships": tenant-agnostic hooks (for example, the
223
+ // user hook) would run multiple times during cross-tenant iteration.
224
+ // An orphan user with no membership would otherwise skip them entirely.
225
+ // As in the forget runner, add a special run with a pseudo-tenant so
226
+ // the global user row is included in the bundle. Tenant-scoped hooks
227
+ // are no-ops.
229
228
  //
230
- // Das Pattern matched run-forget-cleanup.ts; Memory: Cross-Tenant-
231
- // Konsistenz darf nicht silent kippen wenn Memberships leer sind.
229
+ // Cross-tenant consistency must not silently break when memberships are
230
+ // empty.
232
231
  if (tenantList.length === 0 && hookEntries.length > 0) {
233
232
  const orphanEntities: UserDataExportSnippet[] = [];
234
233
  for (const entry of hookEntries) {
@@ -268,15 +267,15 @@ export async function runUserExport(args: RunUserExportArgs): Promise<UserExport
268
267
  };
269
268
  }
270
269
 
271
- // Art. 20 verlangt die DATEN, nicht deren Ciphertext: Export-Hooks lesen
272
- // raw rows an der Executor-Decrypt-Schicht vorbei, darum entschluesselt
273
- // dieser zentrale Pass encrypted entity fields nach jedem Hook. Ohne
274
- // konfigurierten Cipher wird ein expliziter Marker exportiert statt den
275
- // base64-Blob als "Wert" auszuliefern (leak-by-confusion).
270
+ // Art. 20 requires the data, not its ciphertext: export hooks read raw
271
+ // rows outside the executor's decryption layer, so this central pass
272
+ // decrypts encrypted entity fields after each hook. Without a configured
273
+ // cipher, it exports an explicit marker instead of presenting the base64
274
+ // blob as a value (leak by confusion).
276
275
  const ENCRYPTED_UNAVAILABLE = "[encrypted:unavailable]";
277
276
 
278
277
  // Shared decrypt sweep, used for both `snippet.rows` and `snippet.fileRefs`
279
- // (#1955) — `encryptedFields` is keyed by field NAME, so it stays a no-op
278
+ // `encryptedFields` is keyed by field NAME, so it stays a no-op
280
279
  // for today's `fileRef` entity but covers a future generically-encrypted
281
280
  // side-channel field too.
282
281
  async function decryptRecords(
@@ -348,7 +347,6 @@ async function decryptSnippetFields(
348
347
  return { ...snippet, rows, fileRefs };
349
348
  }
350
349
 
351
- // Pseudo-Tenant fuer User ohne aktive Memberships. Identisch zum
352
- // Pattern in run-forget-cleanup.ts — RFC4122-Null-UUID. Tenant-scoped
353
- // Hooks finden hier nichts (no-op).
350
+ // Pseudo-tenant for users without active memberships. It uses an
351
+ // RFC4122 null UUID; tenant-scoped hooks find no data here (no-op).
354
352
  const SYSTEM_TENANT_ID_FOR_ORPHANS = "00000000-0000-0000-0000-000000000000" as TenantId;