@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
@@ -1,32 +1,28 @@
1
- // Retention-Presets — vorgefertigte Bündel von per-Entity-Aufbewahrungs-
2
- // Konfigurationen pro Compliance-Regime. Tenant-Admin wählt EINES (analog
3
- // zu compliance-profiles); Entity-Default + Tenant-Override liegen als
4
- // Schichten 1+3 darüber (siehe resolver.ts).
1
+ // Retention presets — predefined bundles of per-entity retention
2
+ // configurations for each compliance regime. The tenant admin selects ONE
3
+ // (analogous to compliance profiles); the entity default and tenant override
4
+ // are layered above it as layers 1 and 3 (see resolver.ts).
5
5
  //
6
- // Plan-Roadmap docs/plans/datenschutz/core-data-retention.md hat 8 Presets
7
- // vorgeschlagen. Sprint-2-MVP: 3 Production-Presets + 1 dev-Default,
8
- // rest folgt on-demand wenn Customer fragt.
9
- //
10
- // Presets sind pure Daten — keine Logik. Erweitern = Constant erweitern,
11
- // kein Code-Eingriff.
6
+ // The presets are pure data with no logic. Extending them means extending the
7
+ // constant; no code changes are required.
12
8
  /**
13
- * MVP-Set für Sprint 2. Erweiterungen (hipaa, ccpa, aggressive-gdpr,
14
- * pipeda-default, ca-quebec-l25) kommen on-demand.
9
+ * Available presets. Additional options (hipaa, ccpa, aggressive-gdpr,
10
+ * pipeda-default, ca-quebec-l25) can be added on demand.
15
11
  */
16
12
  export const RETENTION_PRESETS = {
17
- // Default — keine Auto-Aktion. Nur sinnvoll für Dev/Staging.
18
- // Production-Tenant der "default" stehen lässt → Cleanup-Job
19
- // schreibt Audit-Eintrag "skipped: no preset selected".
13
+ // Default — no automatic action; intended only for Dev/Staging.
14
+ // A production tenant that leaves `default` selected causes the cleanup job
15
+ // to write the audit entry "skipped: no preset selected".
20
16
  default: {},
21
- // DSGVO Basic — Datenminimierung ohne Buchhaltungspflichten.
17
+ // DSGVO Basic — data minimization without accounting retention obligations.
22
18
  "dsgvo-basic": {
23
19
  "audit-log": { keepFor: "1y", strategy: "hardDelete", reference: "createdAt" },
24
20
  session: { keepFor: "30d", strategy: "hardDelete", reference: "lastSeenAt" },
25
21
  "http-log": { keepFor: "90d", strategy: "hardDelete", reference: "createdAt" },
26
22
  },
27
- // DSGVO + HGB — deutsche Aufbewahrungspflichten überlagert. Order
28
- // wird anonymisiert (PII raus, Geschäftsdaten bleiben), Invoice +
29
- // Booking sind blockDelete bis 10 Jahre, dann Anonymize.
23
+ // DSGVO + HGB — German statutory retention obligations take precedence.
24
+ // Orders are anonymized (PII removed while business data remains); invoices
25
+ // and bookings are blockDelete for up to 10 years, then anonymized.
30
26
  "dsgvo-hgb": {
31
27
  "audit-log": { keepFor: "1y", strategy: "hardDelete", reference: "createdAt" },
32
28
  session: { keepFor: "30d", strategy: "hardDelete", reference: "lastSeenAt" },
@@ -36,7 +32,7 @@ export const RETENTION_PRESETS = {
36
32
  contract: { keepFor: "6y", strategy: "blockDelete", reference: "createdAt" },
37
33
  order: { keepFor: "6y", strategy: "anonymize", reference: "completedAt" },
38
34
  },
39
- // Schweizer DSG — ähnlich DSGVO mit OR Art. 958f Aufbewahrung.
35
+ // Swiss DSG — similar to DSGVO, with retention under OR Art. 958f.
40
36
  "swiss-dsg": {
41
37
  "audit-log": { keepFor: "1y", strategy: "hardDelete", reference: "createdAt" },
42
38
  session: { keepFor: "30d", strategy: "hardDelete", reference: "lastSeenAt" },
@@ -44,9 +40,8 @@ export const RETENTION_PRESETS = {
44
40
  },
45
41
  };
46
42
  /**
47
- * Auswählbare Presets für den Onboarding-Banner. "default" ist Migration-
48
- * Edge-Case und wird nicht angezeigt — Production-Tenants wählen ein
49
- * echtes Preset.
43
+ * Presets available in the onboarding banner. `default` is a migration edge
44
+ * case and is not displayed; production tenants must choose an actual preset.
50
45
  */
51
46
  export const SELECTABLE_RETENTION_PRESETS = [
52
47
  "dsgvo-basic",
@@ -1,18 +1,19 @@
1
- // Leitet den Layer-2 Retention-Preset eines Tenants aus seinem Compliance-
2
- // Profile ab. Der retention-cleanup-Cron ruft das pro fan-out-Tenant.
1
+ // Derives a tenant's Layer-2 retention preset from its compliance profile.
2
+ // The retention-cleanup cron calls this once per fan-out tenant.
3
3
  //
4
- // **Soft-Dependency:** data-retention bleibt standalone-mountbar. Nur wenn
5
- // compliance-profiles mit-gemountet ist (seine Entity registriert), wird ein
6
- // Preset abgeleitet — sonst null, dann greifen ausschliesslich Entity-Defaults
7
- // (Layer 1) + per-Entity Tenant-Overrides (Layer 3). Kein r.requires, damit
8
- // eine App data-retention auch ohne Compliance-Profiles nutzen kann.
4
+ // **Soft dependency:** data-retention remains mountable standalone. A preset is
5
+ // derived only when compliance-profiles is mounted as well (its entity is
6
+ // registered); otherwise this returns null, leaving only entity defaults
7
+ // (Layer 1) and per-entity tenant overrides (Layer 3). No r.requires is used,
8
+ // so an app can use data-retention without compliance-profiles.
9
9
  //
10
- // Die Map ist intentional: Compliance-Profile (Region) und Retention-Preset
11
- // sind beide um dieselben drei Regimes gebaut (EU/CH/DE-HGB). swiss-dsg ist
12
- // sogar namensgleich. minimal-no-region → "default" (No-Op-Preset).
10
+ // The mapping is intentional: compliance profiles (regions) and retention
11
+ // presets are both built around the same three regimes (EU/CH/DE-HGB).
12
+ // swiss-dsg even has the same name. minimal-no-region maps to "default"
13
+ // (a no-op preset).
13
14
  import { resolveProfileForTenant } from "../compliance-profiles/index.js";
14
- // r.entity-Name aus compliance-profiles/feature.ts — Probe ob das Feature
15
- // gemountet ist, bevor wir seine Tabelle lesen (sonst wirft fetchOne).
15
+ // Entity name from compliance-profiles/feature.ts — check that the feature is
16
+ // mounted before reading its table; otherwise fetchOne throws.
16
17
  const COMPLIANCE_PROFILE_ENTITY = "tenant-compliance-profile";
17
18
  const PROFILE_TO_PRESET = {
18
19
  "eu-dsgvo": "dsgvo-basic",
@@ -24,11 +25,11 @@ export async function resolveTenantRetentionPreset(args) {
24
25
  if (!args.registry.getEntity(COMPLIANCE_PROFILE_ENTITY)) {
25
26
  return null;
26
27
  }
27
- // resolveProfileForTenant liest die 1:1-Profile-Row des Tenants (boot-DB,
28
- // by tenantId gefiltert) und faellt auf minimal-no-region zurueck wenn der
29
- // Tenant noch kein Profile gewaehlt hat.
28
+ // resolveProfileForTenant reads the tenant's 1:1 profile row from the boot DB,
29
+ // filtered by tenantId, and falls back to minimal-no-region when the tenant
30
+ // has not selected a profile yet.
30
31
  const effective = await resolveProfileForTenant({ db: args.db, tenantId: args.tenantId });
31
32
  return PROFILE_TO_PRESET[effective.profile.key] ?? null;
32
33
  }
33
- // Exportiert fuer den Unit-Test (Map-Vollstaendigkeit gegen ComplianceProfileKey).
34
+ // Exported for the unit test that checks completeness against ComplianceProfileKey.
34
35
  export { PROFILE_TO_PRESET };
@@ -1,9 +1,9 @@
1
1
  import type { EntityDefinition, RetentionDef } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { type RetentionPresetKey } from "./presets.ts";
3
3
  /**
4
- * Roh-Override aus der DB-Tabelle (config-Spalte als JSON-String).
5
- * Nicht das gleiche wie RetentionDef weil hier alles optional ist —
6
- * Override darf einzelne Properties überschreiben.
4
+ * Raw override from the database table (the config column as a JSON string).
5
+ * Unlike RetentionDef, every property is optional because an override may
6
+ * replace individual properties.
7
7
  */
8
8
  export interface RetentionOverride {
9
9
  readonly keepFor?: string;
@@ -11,13 +11,13 @@ export interface RetentionOverride {
11
11
  readonly reference?: string;
12
12
  }
13
13
  /**
14
- * Effektive Policy nach Resolver-Lauf. Source dokumentiert WELCHE
15
- * Schicht den Wert geliefert hat — Audit-Trail für DPO.
14
+ * Effective policy after the resolver ran. `source` records WHICH layer
15
+ * supplied the value, as an audit trail for the DPO.
16
16
  *
17
- * `override-incomplete` bedeutet: Tenant-Override ist gesetzt, aber
18
- * füllt keepFor weder selbst noch via Preset-/Entity-Fallback.
19
- * Cleanup-Job logt eine Warning + skippt — anstatt mit Default-"0d"
20
- * sofort alles zu löschen.
17
+ * `override-incomplete` means a tenant override is set but fills keepFor
18
+ * neither itself nor through the preset/entity fallback. The cleanup job
19
+ * logs a warning and skips instead of deleting everything at once with a
20
+ * default "0d".
21
21
  */
22
22
  export interface EffectiveRetentionPolicy {
23
23
  readonly entityName: string;
@@ -31,8 +31,8 @@ export interface ResolveRetentionPolicyArgs {
31
31
  readonly tenantOverride: RetentionOverride | null;
32
32
  }
33
33
  /**
34
- * Auswertung der drei Schichten. Pure Function — kein DB-Access, alle
35
- * Inputs werden vom Caller besorgt (Cleanup-Job aggregiert pro Tenant
36
- * vorher).
34
+ * Resolves the three layers. This is a pure function with no database
35
+ * access; the caller supplies all inputs (the cleanup job aggregates them
36
+ * per tenant beforehand).
37
37
  */
38
38
  export declare function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): EffectiveRetentionPolicy;
@@ -1,42 +1,43 @@
1
- // 3-Schicht-Retention-Resolver.
1
+ // Three-layer retention resolver.
2
2
  //
3
- // Layer 1: Entity-Default — Feature-Author setzt r.entity({retention})
4
- // Layer 2: Tenant-Preset — Tenant-Admin wählt Bundle (RETENTION_PRESETS)
5
- // Layer 3: Tenant-Override — per (tenantId, entityName) JSON-Override
3
+ // Layer 1: Entity default — Feature author sets r.entity({retention})
4
+ // Layer 2: Tenant preset — Tenant admin selects a bundle (RETENTION_PRESETS)
5
+ // Layer 3: Tenant override — per (tenantId, entityName) JSON override
6
6
  //
7
- // Resolver-Reihenfolge:
7
+ // Resolution order:
8
8
  // effective = override(tenantId, entityName)
9
9
  // ?? preset[tenant.retentionPreset][entityName]
10
- // ?? entity.retention (Code-Default aus EntityDefinition)
11
- // ?? null (keine Auto-Aktion)
10
+ // ?? entity.retention (code default from EntityDefinition)
11
+ // ?? null (no automatic action)
12
12
  //
13
- // Cleanup-Job in S2.D2 ruft das pro (tenantId, entityName) und
14
- // entscheidet was zu tun ist (hardDelete / softDelete / anonymize /
15
- // blockDelete-mit-Frist-Check).
13
+ // The cleanup job resolves this for each (tenantId, entityName) and
14
+ // decides what to do (hardDelete / softDelete / anonymize /
15
+ // blockDelete with a grace-period check).
16
16
  //
17
- // Cross-Feature-API r.exposesApi("retention.policyFor") (S2.D3)
18
- // macht das aus user-data-rights heraus konsumierbar — Forget-Flow
19
- // fragt blockDelete-Felder ab + anonymisiert sie statt zu löschen.
17
+ // The cross-feature API r.exposesApi("retention.policyFor")
18
+ // makes this consumable by user-data-rights — the forget flow
19
+ // queries blockDelete fields and anonymizes them instead of deleting them.
20
20
  import { RETENTION_PRESETS } from "./presets.js";
21
21
  /**
22
- * Auswertung der drei Schichten. Pure Function — kein DB-Access, alle
23
- * Inputs werden vom Caller besorgt (Cleanup-Job aggregiert pro Tenant
24
- * vorher).
22
+ * Resolves the three layers. This is a pure function with no database
23
+ * access; the caller supplies all inputs (the cleanup job aggregates them
24
+ * per tenant beforehand).
25
25
  */
26
26
  export function resolveRetentionPolicy(args) {
27
27
  const { entityName, entityDef, tenantPreset, tenantOverride } = args;
28
- // Layer 3: Override wins, aber Override darf Felder weglassen — dann
29
- // fallen die einzelnen Properties auf Layer 2/1 zurück.
28
+ // Layer 3: Override wins, but it may omit fields, which then
29
+ // fall back to the individual properties from Layer 2 or 1.
30
30
  if (tenantOverride !== null) {
31
31
  const baseFromPreset = tenantPreset !== null ? (RETENTION_PRESETS[tenantPreset]?.[entityName] ?? null) : null;
32
32
  const baseFromEntity = entityDef?.retention ?? null;
33
33
  const base = baseFromPreset ?? baseFromEntity;
34
34
  const keepFor = tenantOverride.keepFor ?? base?.keepFor;
35
35
  const strategy = tenantOverride.strategy ?? base?.strategy;
36
- // keepFor + strategy sind Pflicht für jede aktive Policy. Wenn
37
- // weder Override noch Base sie liefert, ist das Override semantisch
38
- // unvollständig — Cleanup-Job soll WARNEN statt mit Default-"0d"
39
- // sofort alles löschen. Source-Marker dokumentiert das.
36
+ // keepFor + strategy are required for every active policy. If
37
+ // neither the override nor the base supplies them, the override is
38
+ // semantically incomplete — the cleanup job must warn instead of
39
+ // immediately deleting everything with the default "0d". The source
40
+ // marker documents this.
40
41
  if (keepFor === undefined || strategy === undefined) {
41
42
  return { entityName, policy: null, source: "override-incomplete" };
42
43
  }
@@ -58,6 +59,6 @@ export function resolveRetentionPolicy(args) {
58
59
  if (entityDef?.retention) {
59
60
  return { entityName, policy: entityDef.retention, source: "entity-default" };
60
61
  }
61
- // Nichts da — Cleanup-Job überspringt diese Entity für diesen Tenant.
62
+ // No policy is defined, so the cleanup job skips this entity for this tenant.
62
63
  return { entityName, policy: null, source: "none" };
63
64
  }
@@ -11,14 +11,16 @@ export interface RunRetentionCleanupArgs {
11
11
  /** Layer-2 preset from resolveTenantRetentionPreset, resolved once per tenant
12
12
  * by the caller rather than per entity here. null = layers 1 and 3 only. */
13
13
  readonly preloadedTenantPreset: RetentionPresetKey | null;
14
- /** Now-Injection — Tests pinnen den Wert ohne Date-Mock (Pattern keep-for.ts). */
14
+ /**
15
+ * Injects the current time so tests can pin it without mocking the date (pattern keep-for.ts).
16
+ */
15
17
  readonly now: Instant;
16
18
  readonly batchLimit?: number;
17
19
  /** Storage access for hardDelete entities with file/image/files/images fields.
18
20
  * Missing while fileRefs are due → the row stays (skipped "missing_file_storage"),
19
21
  * fail-closed instead of orphaning bytes. */
20
22
  readonly files?: FileContext;
21
- /** KMS for erasing a hardDeleted row's own subject key (Refs #2057). Defaults
23
+ /** KMS for erasing a hardDeleted row's own subject key. Defaults
22
24
  * to configuredPiiSubjectKms() (same fallback as run-forget-cleanup.ts);
23
25
  * undefined/no-KMS-configured skips erasure entirely — the row is still
24
26
  * hard-deleted, only crypto-shredding of its DEK is not attempted. */
@@ -33,7 +35,10 @@ export interface RunRetentionCleanupResult {
33
35
  readonly softDeleted: number;
34
36
  /** Rows deren anonymize-Feld-Funktionen angewendet wurden (anonymize + abgelaufene blockDelete). */
35
37
  readonly anonymized: number;
36
- /** Anomalien: Policy referenziert eine Spalte die die Tabelle nicht hat, oder anonymize ohne anonymize-Felder. */
38
+ /**
39
+ * Anomalies where the policy references a missing table column or anonymize has no anonymize
40
+ * fields.
41
+ */
37
42
  readonly skipped: readonly RetentionCleanupSkip[];
38
43
  }
39
44
  export declare function runRetentionCleanup(args: RunRetentionCleanupArgs): Promise<RunRetentionCleanupResult>;
@@ -1,46 +1,46 @@
1
- // Retention-Cleanup-Runner (S2.D2b) — pure Function, vom retention-cleanup-Cron
2
- // pro fan-out-Tenant aufgerufen.
1
+ // Retention-Cleanup-Runner — pure function, called by the retention-cleanup cron
2
+ // for each fan-out tenant.
3
3
  //
4
- // Iteriert alle implicit-Entity-Projektionen, loest pro Entity die effektive
5
- // Retention-Policy (3-Schicht-Resolver, siehe resolver.ts) und wendet die
6
- // Strategy auf Rows an deren reference-Timestamp aelter als der keepFor-Cutoff
7
- // ist:
4
+ // Iterates all implicit entity projections, resolves the effective retention
5
+ // policy per entity (3-layer resolver, see resolver.ts), and applies the
6
+ // strategy to rows whose reference timestamp is older than the keepFor cutoff:
8
7
  //
9
- // - hardDelete → executor.forget pro Row (Event → rebuild-safe hard purge).
8
+ // - hardDelete → executor.forget per row (event → rebuild-safe hard purge).
10
9
  // Entities with file/image/files/images fields: bytes first
11
10
  // (incl. derivatives), then the fileRef row(s), then the row;
12
11
  // shared fileRefs (other owner, other row of the same table)
13
12
  // stay untouched.
14
- // - softDelete → executor.delete pro Row (Event → rebuild-safe soft-delete),
15
- // nur auf noch-nicht-geloeschte (isDeleted:false-Filter)
16
- // - anonymize → executor.update pro Row mit den Werten der per-Feld
17
- // anonymize-Funktionen; Row bleibt (fields.ts-Contract)
18
- // - blockDelete → waehrend keepFor unangetastet (Aufbewahrungs-Pflicht;
19
- // user-forget loest anonymize aus). NACH Ablauf laufen die
20
- // anonymize-Funktionen — Geschaeftsdaten bleiben, Personen-
21
- // Bezug raus (fields.ts:44-47).
13
+ // - softDelete → executor.delete per row (event → rebuild-safe soft-delete),
14
+ // only on not-yet-deleted rows (isDeleted:false filter)
15
+ // - anonymize → executor.update per row with the values of the per-field
16
+ // anonymize functions; row remains (fields.ts contract)
17
+ // - blockDelete → untouched during keepFor (retention requirement;
18
+ // user-forget triggers anonymize). AFTER expiration, the
19
+ // anonymize functions run — business data remains, the person-
20
+ // identifying link is removed (fields.ts:44-47).
22
21
  //
23
- // Über den Executor statt Batch-deleteMany/updateMany: ein eventloser Batch-Write
24
- // auf die (erased) Projektions-Tabelle wird beim Projection-Rebuild
25
- // gewischt/resurrektiert — das #648-Loch. Kosten: N Events pro Cleanup statt ein
26
- // Batch-Statement (per-Row-Ceiling, Batch-Event-Variante als Follow-up).
22
+ // Via the executor instead of batch-deleteMany/updateMany: an eventless batch write
23
+ // to the (erased) projection table is wiped/resurrected during a projection rebuild.
24
+ // Cost: N events per cleanup instead of one batch statement (per-row ceiling, with
25
+ // a batch-event variant later).
27
26
  //
28
- // **Schaerfer als soft-delete-cleanup:** dieser Cron hardDeleted LIVE Rows
29
- // (keyed auf reference, Default createdAt), nicht bereits-soft-geloeschte.
30
- // Darum die Spalten-Existenz-Pruefung vor jedem WHERE — eine fehlende/vertippte
31
- // reference-Spalte wuerde sonst ein malformed/all-matching WHERE ergeben und
32
- // pauschal loeschen.
27
+ // **Stricter than soft-delete-cleanup:** this cron hard-deletes LIVE rows
28
+ // (keyed on reference, default createdAt), not already-soft-deleted ones.
29
+ // Therefore the column-existence check comes before every WHERE — a missing/mistyped
30
+ // reference column would otherwise produce a malformed/all-matching WHERE and
31
+ // delete everything.
33
32
  //
34
- // **anonymize-Idempotenz ohne Marker-Spalte:** die anonymize-Funktionen sind
35
- // row-unabhaengig (`() => unknown`) — einmal pro Entity ausgewertet ergeben
36
- // sie die Ziel-Werte. Eine Row die sie schon traegt wird uebersprungen, der
37
- // taegliche Re-Lauf appended also null Events. Der WHERE matcht anonymisierte
38
- // Rows weiter (reference bleibt alt), deshalb id-Cursor-Paging statt einer
39
- // einzelnen Page — sonst verstopfen erledigte Rows das batchLimit-Fenster.
33
+ // **anonymize idempotence without a marker column:** the anonymize functions are
34
+ // row-independent (`() => unknown`) — evaluated once per entity, they produce
35
+ // the target values. A row that already carries them is skipped, so the
36
+ // daily rerun appends zero events. The WHERE continues to match anonymized
37
+ // rows (reference remains old), hence id-cursor paging instead of a single
38
+ // page — otherwise completed rows clog the batchLimit window.
40
39
  import { collectPiiSubjectFields, configuredPiiSubjectKms, isRecordKeyedField, isSelfPiiField, resolveSubjectForField, subjectIdToKey, } from "@cosmicdrift/kumiko-framework/crypto";
41
40
  import { createEventStoreExecutor, createTenantDb, nullBlindIndexesForSubject, selectMany, WritePurposes, } from "@cosmicdrift/kumiko-framework/db";
42
41
  import { deleteFileRefBytes } from "@cosmicdrift/kumiko-framework/derivatives";
43
42
  import { createSystemUser, entityHasAnonymizableSubjectField, } from "@cosmicdrift/kumiko-framework/engine";
43
+ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
44
44
  import { fileRefEntity, fileRefsTable, } from "@cosmicdrift/kumiko-framework/files";
45
45
  import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
46
46
  import { collectSingleFileHolders, planFileFields, purgeRowFileRefs, runInSubTransaction, } from "../shared/index.js";
@@ -49,13 +49,13 @@ import { resolveRetentionPolicyForTenant } from "./resolve-for-tenant.js";
49
49
  import { tenantRetentionOverrideTable } from "./schema/tenant-retention-override.js";
50
50
  const DEFAULT_BATCH_LIMIT = 1000;
51
51
  const DEFAULT_REFERENCE_FIELD = "createdAt";
52
- // Der Boot-Validator (boot-validator/pii-retention.ts FRAMEWORK_TIMESTAMP_FIELDS)
53
- // erlaubt diese Aliase als retention.reference, auch wenn sie nicht in
54
- // entity.fields deklariert sind — die physischen Spalten heissen aber anders
55
- // (table-builder.ts: inserted_at/modified_at). Hier zur Cleanup-Zeit auf das
56
- // echte Entity-Feld mappen, sonst trifft die Spalten-Existenz-Pruefung unten
57
- // und der Cron wuerde lautlos nichts tun. deletedAt/lastSeenAt sind echte
58
- // Felder (softDelete bzw. session) und brauchen keine Uebersetzung.
52
+ // The boot validator (boot-validator/pii-retention.ts FRAMEWORK_TIMESTAMP_FIELDS)
53
+ // permits these aliases as retention.reference even when they are not declared in
54
+ // entity.fields, but the physical columns use different names
55
+ // (table-builder.ts: inserted_at/modified_at). Map them to the actual entity field
56
+ // during cleanup; otherwise the column existence check below would cause the cron
57
+ // to do nothing silently. deletedAt/lastSeenAt are real fields (softDelete and
58
+ // session respectively) and need no translation.
59
59
  const FRAMEWORK_REFERENCE_ALIAS = {
60
60
  createdAt: "insertedAt",
61
61
  updatedAt: "modifiedAt",
@@ -72,7 +72,7 @@ async function purgeMatchingRows(db, table, where, batchLimit, op) {
72
72
  }
73
73
  return count;
74
74
  }
75
- // --- hardDelete crypto-shredding (Refs #2057) ---
75
+ // --- hardDelete crypto-shredding ---
76
76
  //
77
77
  // recordOwned/self-pii resolution (subject-resolver.ts) only ever reads
78
78
  // `row.id` for these two kinds — resolveRecordSubject uses opts.entityName +
@@ -137,7 +137,7 @@ async function eraseHardDeletedRowKeys(args) {
137
137
  async function forgetRowAndEraseKeys(args) {
138
138
  return runInSubTransaction(args.db, async (sp) => {
139
139
  const tdb = createTenantDb({ db: sp, tenantId: args.tenantId, mode: "system" });
140
- const result = await args.executor.forget({ id: args.row["id"] }, args.systemUser, tdb);
140
+ const result = await args.executor.forget({ id: args.rowId }, args.systemUser, tdb);
141
141
  if (!result.isSuccess)
142
142
  return { isSuccess: false };
143
143
  await eraseHardDeletedRowKeys({
@@ -152,7 +152,7 @@ async function forgetRowAndEraseKeys(args) {
152
152
  return { isSuccess: true };
153
153
  });
154
154
  }
155
- // --- hardDelete + file-fields (kumiko-framework#3089) ---
155
+ // --- hardDelete + file-fields ---
156
156
  // Throws (unsafe key, missing list permission) must fail this row closed,
157
157
  // not abort the whole tenant run.
158
158
  async function deleteFileFailingClosed(key, files, logContext) {
@@ -169,8 +169,7 @@ async function deleteFileFailingClosed(key, files, logContext) {
169
169
  // fields: purge the un-shared fileRefs (bytes + derivatives, then the fileRef
170
170
  // rows), then the entity row itself.
171
171
  async function purgeHardDeleteRowWithFiles(args) {
172
- const rowId = String(args.row["id"]);
173
- const logContext = `tenant=${args.tenantId} entity=${args.entityName} row=${rowId}`;
172
+ const logContext = `tenant=${args.tenantId} entity=${args.entityName} row=${String(args.rowId)}`;
174
173
  const files = args.files;
175
174
  const outcome = await purgeRowFileRefs({
176
175
  db: args.db,
@@ -198,6 +197,7 @@ async function purgeHardDeleteRowWithFiles(args) {
198
197
  entityName: args.entityName,
199
198
  tenantId: args.tenantId,
200
199
  systemUser: args.systemUser,
200
+ rowId: args.rowId,
201
201
  row: args.row,
202
202
  kms: args.kms,
203
203
  registry: args.registry,
@@ -230,7 +230,14 @@ async function purgeHardDeleteRowsWithFiles(args) {
230
230
  if (rows.length === 0)
231
231
  break;
232
232
  for (const row of rows) {
233
- cursor = row["id"]; // @cast-boundary db-row
233
+ const rawId = row["id"];
234
+ if (typeof rawId !== "string" && typeof rawId !== "number") {
235
+ throw new InternalError({
236
+ message: `data-retention: ${args.entityName} row has no usable id (got ${typeof rawId})`,
237
+ });
238
+ }
239
+ const rowId = rawId;
240
+ cursor = rowId;
234
241
  const ok = await purgeHardDeleteRowWithFiles({
235
242
  db: args.db,
236
243
  table: args.table,
@@ -238,6 +245,7 @@ async function purgeHardDeleteRowsWithFiles(args) {
238
245
  tenantId: args.tenantId,
239
246
  entityName: args.entityName,
240
247
  entity: args.entity,
248
+ rowId,
241
249
  row,
242
250
  files: args.files,
243
251
  entityExecutor: args.entityExecutor,
@@ -311,9 +319,9 @@ export async function runRetentionCleanup(args) {
311
319
  const skipped = [];
312
320
  // Retention writes go through the executor (events) so a projection rebuild
313
321
  // replays the cleanup — a batch deleteMany/updateMany on the (erased)
314
- // projection table is eventless and gets wiped/resurrected on rebuild (the
315
- // #648 hole this closes). The cron acts as the system actor; the WHERE already
316
- // tenant-scopes, so a system-mode TenantDb (no extra filter) is correct.
322
+ // projection table is eventless and gets wiped/resurrected on rebuild.
323
+ // The cron acts as the system actor; the WHERE already tenant-scopes, so a
324
+ // system-mode TenantDb (no extra filter) is correct.
317
325
  // ponytail: per-row events, one batchLimit-sized page per entity per run —
318
326
  // the daily cron converges (hardDelete removes rows, softDelete's isDeleted
319
327
  // filter shrinks the set). A single batched forget/delete event is a follow-up.
@@ -326,8 +334,9 @@ export async function runRetentionCleanup(args) {
326
334
  const overrideRows = await selectMany(db, tenantRetentionOverrideTable, { tenantId });
327
335
  const overrideByEntity = new Map(overrideRows.map((r) => [r.entityName, { config: r.config }]));
328
336
  for (const proj of registry.getAllProjections().values()) {
329
- // Nur implicit-Entity-Projektionen mit Tabelle — wie soft-delete-cleanup.
330
- // Custom-Projektionen + unmanaged-Tables (z.B. sessions) sind kein Target.
337
+ // Only implicit entity projections with a table are targets, as in
338
+ // soft-delete-cleanup. Custom projections and unmanaged tables (such as
339
+ // sessions) are excluded.
331
340
  if (proj.isImplicit !== true || typeof proj.source !== "string" || !proj.table)
332
341
  continue;
333
342
  const entityName = proj.source;
@@ -353,9 +362,9 @@ export async function runRetentionCleanup(args) {
353
362
  }
354
363
  const cutoff = computeCutoff(policy.keepFor, now);
355
364
  const where = { [referenceField]: { lt: cutoff } };
356
- // Tenant-Scope nur wenn die Tabelle eine tenantId-Spalte hat — identisch zu
357
- // soft-delete-cleanup. Ohne diesen Filter wuerde ein Tenant die Rows eines
358
- // anderen treffen.
365
+ // Apply tenant scoping only when the table has a tenantId column, as in
366
+ // soft-delete-cleanup. Without this filter, one tenant could affect another
367
+ // tenant's rows.
359
368
  if (propertyOf(proj.table, "tenantId") !== undefined) {
360
369
  where["tenantId"] = tenantId;
361
370
  }
@@ -371,6 +380,7 @@ export async function runRetentionCleanup(args) {
371
380
  entityName,
372
381
  tenantId,
373
382
  systemUser,
383
+ rowId: id,
374
384
  row: { id },
375
385
  kms,
376
386
  registry,
@@ -1,20 +1,22 @@
1
1
  import { buildEntityTable } from "@cosmicdrift/kumiko-framework/db";
2
2
  import { createEntity, createLongTextField, createTextField, } from "@cosmicdrift/kumiko-framework/engine";
3
- // tenantRetentionOverride — Layer 3 im 3-Schicht-Resolver.
3
+ // tenantRetentionOverride — Layer 3 in the three-layer resolver.
4
4
  //
5
- // Per (tenantId, entityName) eine optionale Override-Config die das
6
- // Preset (Layer 2) für genau diese eine Entity überschreibt. Use-Cases:
7
- // - Anwaltskanzlei in DE: caseFile 6y blockDelete (nicht im Preset)
8
- // - Pilot-Tenant der länger speichert für Test
9
- // - Branchenspezifische verkürzte Fristen
5
+ // One optional override config per (tenantId, entityName) replaces the
6
+ // preset (Layer 2) for that entity only. Use cases:
7
+ // - Law firm in DE: caseFile 6y blockDelete (not in the preset)
8
+ // - Pilot tenant retaining data longer for testing
9
+ // - Industry-specific shorter retention periods
10
10
  //
11
- // reason ist Pflicht — Audit für DPO + Aufsichtsbehörde nachvollziehbar.
11
+ // reason is required so the decision is auditable by the DPO and supervisory
12
+ // authorities.
12
13
  //
13
- // config ist JSON-String mit `{ keepFor, strategy, reference? }`. Zod-
14
- // Schema validiert beim set-override-Call (S2.D2 ggf. erweitert).
14
+ // config is a JSON string with `{ keepFor, strategy, reference? }`. The Zod
15
+ // schema validates it when set-override is called.
15
16
  //
16
- // Tenant-1-zu-N: pro Tenant beliebig viele Entity-Overrides. UNIQUE-
17
- // Index auf (tenantId, entityName) damit pro Entity max ein Override.
17
+ // Tenant one-to-many: each tenant can have any number of entity overrides.
18
+ // A UNIQUE index on (tenantId, entityName) allows at most one override per
19
+ // entity.
18
20
  export const tenantRetentionOverrideEntity = createEntity({
19
21
  table: "read_tenant_retention_overrides",
20
22
  fields: {
@@ -55,10 +55,10 @@ export function createDeliveryFeature(options) {
55
55
  // boot-validator accepts the projection below because at least one
56
56
  // apply-key is a registered domain-event (DELIVERY_ATTEMPT_EVENT).
57
57
  // recipientAddress is the real PII (email address); recipientId stays
58
- // plaintext — pseudonymous fk, same line as config.userId (#821).
58
+ // plaintext — pseudonymous fk, same line as config.userId.
59
59
  // recipientId is nullable (system-triggered sends to non-users), so the
60
60
  // address falls back to the tenant key instead of the plaintext it used
61
- // to be — still shreddable, one tenant wide (fw#2776).
61
+ // to be — still shreddable, one tenant wide.
62
62
  r.defineEvent("attempt", deliveryAttemptSchema, {
63
63
  piiFields: {
64
64
  recipientAddress: { personal: { of: "recipientId", whenAbsent: "tenant" } },
@@ -135,7 +135,7 @@ export const logQuery = definePagedQueryHandler({
135
135
  const rows = [...tiedRows, ...beyondRows];
136
136
  const lastRow = rows[rows.length - 1];
137
137
  const nextCursor = rows.length === limit && lastRow ? encodeSortCursor(sortColumn, lastRow) : null;
138
- // recipientAddress is stored encrypted under the recipient's DEK (#799)
138
+ // recipientAddress is stored encrypted under the recipient's DEK
139
139
  // — decrypt for the admin log view; forgotten subjects show [[erased]].
140
140
  // The notificationType/recipientAddress → type/recipient rename happens
141
141
  // here, not in the client: a projectionList screen has no entity to
@@ -30,14 +30,15 @@ export const deliveryAttemptsTable = pgTable("store_delivery_attempts", {
30
30
  priority: text("priority").notNull().default("normal").$type(),
31
31
  createdAt: instant("created_at").default(sql `now()`).notNull(),
32
32
  }, (t) => [index(DELIVERY_ATTEMPTS_TENANT_CREATED_INDEX).on(t.tenantId, t.createdAt)]);
33
- // **Unmanaged table** — bewusst KEIN createEntity. Begründung:
34
- // - id kommt aus dem Aggregate-Stream (kein gen_random_uuid()-DEFAULT)
35
- // - kein version/inserted_by/modified_by/modified_at — keine in-place-
36
- // Edits, keine Audit-Spalten nötig (idempotent-on-replay via PK-Konflikt)
37
- // - created_at statt inserted_at — historischer Naming-Drift, kein Bug
38
- // App trägt Verantwortung für tenant-scoping in Queries + replay-idempotency.
39
- // pgTable bleibt source-of-truth für Query-API; Phase 4 leitet das pgTable
40
- // aus dieser Meta ab.
33
+ // **Unmanaged table** — deliberately has no createEntity because:
34
+ // - id comes from the aggregate stream (no gen_random_uuid()-DEFAULT)
35
+ // - no version/inserted_by/modified_by/modified_at: no in-place edits or
36
+ // audit columns are needed (idempotent-on-replay via PK conflict)
37
+ // - created_at rather than inserted_at reflects historical naming drift, not
38
+ // a bug
39
+ // The app is responsible for tenant scoping in queries and replay idempotency.
40
+ // pgTable remains the source of truth for the query API; the pgTable is derived
41
+ // from this metadata.
41
42
  export const deliveryAttemptsTableMeta = defineUnmanagedTable({
42
43
  tableName: "store_delivery_attempts",
43
44
  indexes: [{ name: DELIVERY_ATTEMPTS_TENANT_CREATED_INDEX, columns: ["tenant_id", "created_at"] }],
@@ -13,13 +13,13 @@ const localeClaimSchema = z
13
13
  .string()
14
14
  .regex(/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{1,8})*$/)
15
15
  .optional();
16
- // Shape des verified-JWT-payloads. tenantId kommt als string aus jose und
17
- // wird NACH erfolgreichem parse() zur Branded TenantId — kein blind-cast.
16
+ // Shape of verified JWT payloads. `tenantId` arrives from jose as a string and
17
+ // becomes a branded `TenantId` only after successful `parse()`—never via a blind cast.
18
18
  //
19
19
  // `kind` is explicitly typed as "absent or undefined, nothing else" here
20
20
  // (not bare `.optional()`, which would also accept any other value on an
21
- // unrelated key) so an address-token payload — which always carries
22
- // `kind: "address"` — fails this schema instead of silently parsing with
21
+ // unrelated key) so an address-token payload—which always carries
22
+ // `kind: "address"`—fails this schema instead of silently parsing with
23
23
  // `addressHash` mistaken for a userId.
24
24
  const unsubscribeJwtPayloadSchema = z.object({
25
25
  kind: z.undefined().optional(),