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