@cosmicdrift/kumiko-bundled-features 0.386.0 → 0.388.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 (643) hide show
  1. package/LICENSE +21 -57
  2. package/dist/agent-tools/tool-catalog.js +1 -1
  3. package/dist/agent-tools/tool-dispatch.d.ts +7 -7
  4. package/dist/agent-tools/tool-dispatch.js +9 -9
  5. package/dist/audit/handlers/audit-events.js +1 -1
  6. package/dist/audit/screens.js +1 -1
  7. package/dist/auth-email-password/feature.js +1 -1
  8. package/dist/auth-email-password/handlers/change-password.write.js +3 -3
  9. package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
  10. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
  11. package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
  12. package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
  13. package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
  14. package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
  15. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
  16. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  17. package/dist/auth-email-password/handlers/login.write.js +1 -1
  18. package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
  19. package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
  20. package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
  21. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
  22. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
  23. package/dist/auth-email-password/index.js +5 -5
  24. package/dist/auth-email-password/lockout-store.js +1 -1
  25. package/dist/auth-email-password/seeding.d.ts +42 -45
  26. package/dist/auth-email-password/seeding.js +51 -53
  27. package/dist/auth-email-password/signup-token-store.js +8 -8
  28. package/dist/auth-email-password/testing.js +3 -3
  29. package/dist/auth-email-password/web/auth-client.d.ts +2 -2
  30. package/dist/auth-email-password/web/auth-client.js +36 -37
  31. package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
  32. package/dist/auth-email-password/web/auth-gate.js +15 -16
  33. package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
  34. package/dist/auth-email-password/web/client-plugin.js +4 -5
  35. package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
  36. package/dist/auth-email-password/web/constants.js +3 -3
  37. package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
  38. package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
  39. package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
  40. package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
  41. package/dist/auth-email-password/web/index.js +5 -5
  42. package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
  43. package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
  44. package/dist/auth-email-password/web/login-screen.d.ts +1 -1
  45. package/dist/auth-email-password/web/login-screen.js +2 -3
  46. package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
  47. package/dist/auth-email-password/web/reset-password-screen.js +12 -13
  48. package/dist/auth-email-password/web/session.d.ts +10 -9
  49. package/dist/auth-email-password/web/session.js +28 -29
  50. package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
  51. package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
  52. package/dist/auth-email-password/web/signup-screen.js +13 -13
  53. package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
  54. package/dist/auth-email-password/web/tenant-switcher.js +21 -22
  55. package/dist/auth-email-password/web/use-shell-user.js +12 -13
  56. package/dist/auth-email-password/web/user-menu.d.ts +4 -4
  57. package/dist/auth-email-password/web/user-menu.js +12 -13
  58. package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
  59. package/dist/auth-email-password/web/verify-email-screen.js +9 -10
  60. package/dist/auth-foundation/boot-checks.js +4 -4
  61. package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
  62. package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
  63. package/dist/auth-foundation/types.js +2 -2
  64. package/dist/auth-mfa/feature-exports.d.ts +2 -2
  65. package/dist/auth-mfa/feature.js +8 -0
  66. package/dist/auth-mfa/handlers/disable.write.js +3 -3
  67. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
  68. package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
  69. package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
  70. package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
  71. package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
  72. package/dist/auth-mfa/handlers/verify.write.js +1 -1
  73. package/dist/auth-mfa/token-secrets.js +1 -1
  74. package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
  75. package/dist/auth-mfa/web/client-plugin.js +1 -1
  76. package/dist/auth-mfa/web/i18n.js +8 -4
  77. package/dist/auth-mfa/web/index.js +4 -4
  78. package/dist/auth-mfa/web/mfa-client.js +17 -16
  79. package/dist/auth-mfa/web/mfa-code-or-recovery-field.d.ts +11 -0
  80. package/dist/auth-mfa/web/mfa-code-or-recovery-field.js +12 -0
  81. package/dist/auth-mfa/web/mfa-disable-dialog.js +14 -3
  82. package/dist/auth-mfa/web/mfa-regenerate-recovery-dialog.js +14 -3
  83. package/dist/auth-mfa/web/mfa-setup-preauth-screen.js +10 -5
  84. package/dist/auth-mfa/web/mfa-verify-screen.js +32 -11
  85. package/dist/billing-foundation/aggregate-id.d.ts +5 -5
  86. package/dist/billing-foundation/aggregate-id.js +10 -11
  87. package/dist/billing-foundation/checkout-core.d.ts +9 -14
  88. package/dist/billing-foundation/checkout-core.js +24 -39
  89. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +8 -2
  90. package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +1 -0
  91. package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -0
  92. package/dist/billing-foundation/consumer-protection/issue-confirmation.js +9 -3
  93. package/dist/billing-foundation/consumer-protection/mail-notifications.js +3 -1
  94. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +4 -2
  95. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +6 -5
  96. package/dist/billing-foundation/consumer-protection/termination-record.d.ts +5 -1
  97. package/dist/billing-foundation/consumer-protection/termination-record.js +27 -17
  98. package/dist/billing-foundation/consumer-protection/termination-request.js +12 -7
  99. package/dist/billing-foundation/consumer-protection/termination-revoke.js +12 -7
  100. package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
  101. package/dist/billing-foundation/consumer-protection/termination-texts.js +3 -0
  102. package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +5 -2
  103. package/dist/billing-foundation/consumer-protection/withdrawal-period.js +8 -9
  104. package/dist/billing-foundation/entities.js +27 -27
  105. package/dist/billing-foundation/events.d.ts +1 -0
  106. package/dist/billing-foundation/events.js +19 -17
  107. package/dist/billing-foundation/feature.js +45 -44
  108. package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
  109. package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
  110. package/dist/billing-foundation/handlers/billing-plans.query.js +35 -5
  111. package/dist/billing-foundation/handlers/create-checkout-session.write.js +19 -17
  112. package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
  113. package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
  114. package/dist/billing-foundation/handlers/plan-switch-preview.query.d.ts +13 -0
  115. package/dist/billing-foundation/handlers/plan-switch-preview.query.js +48 -0
  116. package/dist/billing-foundation/handlers/process-event.write.d.ts +6 -0
  117. package/dist/billing-foundation/handlers/process-event.write.js +28 -22
  118. package/dist/billing-foundation/handlers/start-plan-checkout.write.js +32 -5
  119. package/dist/billing-foundation/handlers/switch-plan.write.d.ts +2 -2
  120. package/dist/billing-foundation/handlers/switch-plan.write.js +56 -64
  121. package/dist/billing-foundation/handlers/sync-subscription.write.js +12 -1
  122. package/dist/billing-foundation/i18n.js +22 -0
  123. package/dist/billing-foundation/index.d.ts +1 -1
  124. package/dist/billing-foundation/plan-catalog.d.ts +21 -5
  125. package/dist/billing-foundation/plan-catalog.js +25 -16
  126. package/dist/billing-foundation/plan-switch.d.ts +12 -0
  127. package/dist/billing-foundation/plan-switch.js +60 -0
  128. package/dist/billing-foundation/projection.d.ts +15 -11
  129. package/dist/billing-foundation/projection.js +38 -37
  130. package/dist/billing-foundation/subscription-tier-sync.js +3 -3
  131. package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
  132. package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
  133. package/dist/billing-foundation/types.d.ts +97 -42
  134. package/dist/billing-foundation/types.js +29 -20
  135. package/dist/billing-foundation/validate-options.js +17 -0
  136. package/dist/billing-foundation/web/billing-plans-panel.js +82 -36
  137. package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +17 -3
  138. package/dist/billing-foundation/web/checkout-consent-dialog.js +14 -10
  139. package/dist/billing-foundation/web/constants.d.ts +4 -0
  140. package/dist/billing-foundation/web/constants.js +4 -0
  141. package/dist/billing-foundation/web/plan-price.d.ts +15 -0
  142. package/dist/billing-foundation/web/plan-price.js +32 -0
  143. package/dist/billing-foundation/web/plan-switch-confirm-dialog.d.ts +15 -0
  144. package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +66 -0
  145. package/dist/cap-counter/aggregate-id.js +8 -9
  146. package/dist/cap-counter/cap-reservation.js +5 -1
  147. package/dist/cap-counter/constants.js +11 -12
  148. package/dist/cap-counter/enforce-cap.d.ts +59 -62
  149. package/dist/cap-counter/enforce-cap.js +49 -49
  150. package/dist/cap-counter/entity.js +29 -29
  151. package/dist/cap-counter/feature.js +34 -35
  152. package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
  153. package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
  154. package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
  155. package/dist/cap-counter/stock-cap-guard.js +8 -2
  156. package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
  157. package/dist/channel-email/feature.js +1 -1
  158. package/dist/channel-email/smtp-transport.d.ts +18 -17
  159. package/dist/channel-email/smtp-transport.js +24 -21
  160. package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
  161. package/dist/compliance-profiles/feature.js +4 -4
  162. package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
  163. package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
  164. package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
  165. package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
  166. package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
  167. package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
  168. package/dist/compliance-profiles/schema/profile-selection.js +18 -23
  169. package/dist/compliance-profiles/seeding.js +14 -15
  170. package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
  171. package/dist/compliance-profiles-ops/index.js +2 -2
  172. package/dist/config/handlers/cascade.query.d.ts +5 -0
  173. package/dist/config/handlers/cascade.query.js +4 -3
  174. package/dist/config/handlers/readiness.query.d.ts +4 -4
  175. package/dist/config/handlers/readiness.query.js +1 -1
  176. package/dist/config/handlers/reencrypt.job.js +4 -5
  177. package/dist/config/handlers/set.write.js +4 -4
  178. package/dist/config/handlers/values.query.d.ts +5 -0
  179. package/dist/config/handlers/values.query.js +4 -3
  180. package/dist/config/resolver.js +4 -4
  181. package/dist/config/table.js +10 -10
  182. package/dist/config/write-helpers.d.ts +1 -0
  183. package/dist/config/write-helpers.js +19 -10
  184. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
  185. package/dist/crypto-shredding/handlers/forget-subject.write.js +25 -21
  186. package/dist/custom-fields/aggregate-id.d.ts +12 -13
  187. package/dist/custom-fields/aggregate-id.js +16 -17
  188. package/dist/custom-fields/entity.js +49 -47
  189. package/dist/custom-fields/events.js +7 -8
  190. package/dist/custom-fields/feature.d.ts +14 -14
  191. package/dist/custom-fields/feature.js +53 -54
  192. package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
  193. package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
  194. package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
  195. package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
  196. package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
  197. package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
  198. package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
  199. package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
  200. package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
  201. package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
  202. package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
  203. package/dist/custom-fields/lib/field-access.js +2 -2
  204. package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
  205. package/dist/custom-fields/lib/quota.js +1 -1
  206. package/dist/custom-fields/lib/value-schema.js +10 -12
  207. package/dist/custom-fields/run-retention.js +3 -3
  208. package/dist/custom-fields/schemas.js +14 -14
  209. package/dist/custom-fields/web/client-plugin.js +2 -5
  210. package/dist/custom-fields/web/constants.js +2 -2
  211. package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
  212. package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
  213. package/dist/custom-fields/wire-for-entity.js +65 -67
  214. package/dist/custom-fields/wire-user-data-rights.js +3 -3
  215. package/dist/data-retention/_internal/parse-override.js +2 -3
  216. package/dist/data-retention/feature.js +11 -18
  217. package/dist/data-retention/handlers/policy-for.query.js +6 -6
  218. package/dist/data-retention/keep-for.d.ts +7 -7
  219. package/dist/data-retention/keep-for.js +15 -15
  220. package/dist/data-retention/override-schema.d.ts +3 -4
  221. package/dist/data-retention/override-schema.js +13 -15
  222. package/dist/data-retention/presets.d.ts +4 -5
  223. package/dist/data-retention/presets.js +18 -23
  224. package/dist/data-retention/resolve-tenant-preset.js +17 -16
  225. package/dist/data-retention/resolver.d.ts +12 -12
  226. package/dist/data-retention/resolver.js +24 -23
  227. package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
  228. package/dist/data-retention/run-retention-cleanup.js +61 -51
  229. package/dist/data-retention/schema/tenant-retention-override.js +13 -11
  230. package/dist/delivery/channel-context.js +6 -3
  231. package/dist/delivery/db/queries/preferences.js +2 -2
  232. package/dist/delivery/delivery-service.js +22 -7
  233. package/dist/delivery/feature.js +2 -2
  234. package/dist/delivery/handlers/log.query.js +1 -1
  235. package/dist/delivery/handlers/preferences.query.d.ts +1 -1
  236. package/dist/delivery/handlers/preferences.query.js +20 -1
  237. package/dist/delivery/handlers/unsubscribe-page-chrome.query.js +1 -1
  238. package/dist/delivery/tables.js +9 -8
  239. package/dist/delivery/unsubscribe-link.d.ts +3 -1
  240. package/dist/delivery/unsubscribe-page-strings.d.ts +1 -0
  241. package/dist/delivery/unsubscribe-page-strings.js +1 -0
  242. package/dist/delivery/unsubscribe-page.d.ts +2 -0
  243. package/dist/delivery/unsubscribe-page.js +5 -0
  244. package/dist/delivery/unsubscribe.js +29 -11
  245. package/dist/document-ingest-foundation/entity.js +4 -4
  246. package/dist/document-ingest-foundation/events.js +2 -2
  247. package/dist/document-ingest-foundation/feature.js +2 -2
  248. package/dist/document-ingest-foundation/pages.js +1 -1
  249. package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
  250. package/dist/feature-toggles/compose-tier-resolver.js +1 -1
  251. package/dist/feature-toggles/feature.js +1 -1
  252. package/dist/feature-toggles/global-feature-state-table.js +1 -1
  253. package/dist/feature-toggles/handlers/registered.query.js +5 -5
  254. package/dist/feature-toggles/toggle-sync-signal.js +1 -1
  255. package/dist/feature-toggles/web/constants.js +1 -1
  256. package/dist/file-derivatives/feature.d.ts +1 -1
  257. package/dist/file-derivatives/feature.js +1 -1
  258. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  259. package/dist/file-derivatives/presets.js +1 -1
  260. package/dist/file-foundation/feature.js +6 -6
  261. package/dist/file-foundation/index.d.ts +1 -1
  262. package/dist/file-foundation/index.js +1 -1
  263. package/dist/file-foundation/read-file-ref-content.d.ts +3 -0
  264. package/dist/file-foundation/read-file-ref-content.js +6 -1
  265. package/dist/file-provider-inmemory/feature.d.ts +1 -1
  266. package/dist/file-provider-inmemory/feature.js +16 -16
  267. package/dist/file-provider-s3/env-named-feature.d.ts +0 -2
  268. package/dist/file-provider-s3/feature.js +16 -16
  269. package/dist/file-provider-s3/lib/s3-provider.js +6 -6
  270. package/dist/files/feature.js +6 -6
  271. package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
  272. package/dist/files-tenant-data/hooks.js +24 -26
  273. package/dist/files-tenant-data/index.js +1 -1
  274. package/dist/folders/feature.d.ts +1 -1
  275. package/dist/folders/web/folder-manager.js +2 -2
  276. package/dist/folders/web/tree.js +5 -5
  277. package/dist/form-draft/db/queries/cleanup.js +3 -3
  278. package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
  279. package/dist/form-draft/decrypt-draft.js +5 -1
  280. package/dist/form-draft/entity.js +3 -3
  281. package/dist/form-draft/feature.js +4 -5
  282. package/dist/form-draft/handlers/discard.write.js +1 -1
  283. package/dist/form-draft/release-file-refs.js +6 -6
  284. package/dist/form-draft/schemas.js +3 -3
  285. package/dist/form-draft/web/constants.js +0 -2
  286. package/dist/host-routing/dispatch.js +11 -20
  287. package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
  288. package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
  289. package/dist/inbound-mail-foundation/connect-routes.js +4 -3
  290. package/dist/inbound-mail-foundation/constants.d.ts +22 -24
  291. package/dist/inbound-mail-foundation/constants.js +30 -33
  292. package/dist/inbound-mail-foundation/entities.d.ts +12 -12
  293. package/dist/inbound-mail-foundation/entities.js +47 -48
  294. package/dist/inbound-mail-foundation/events.d.ts +4 -3
  295. package/dist/inbound-mail-foundation/events.js +50 -47
  296. package/dist/inbound-mail-foundation/feature.js +39 -39
  297. package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
  298. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
  299. package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
  300. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
  301. package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
  302. package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
  303. package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
  304. package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +2 -2
  305. package/dist/inbound-mail-foundation/handlers/update-account.write.js +22 -14
  306. package/dist/inbound-mail-foundation/i18n.js +0 -1
  307. package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
  308. package/dist/inbound-mail-foundation/oauth-state.js +11 -10
  309. package/dist/inbound-mail-foundation/projection.d.ts +4 -4
  310. package/dist/inbound-mail-foundation/projection.js +31 -29
  311. package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
  312. package/dist/inbound-mail-foundation/provider-factory.js +9 -9
  313. package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
  314. package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
  315. package/dist/inbound-mail-foundation/screens.js +0 -3
  316. package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
  317. package/dist/inbound-mail-foundation/types.d.ts +49 -49
  318. package/dist/inbound-mail-foundation/types.js +21 -21
  319. package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
  320. package/dist/inbound-mail-foundation/watch-supervisor.js +85 -67
  321. package/dist/inbound-provider-imap/credential-document.js +13 -13
  322. package/dist/inbound-provider-imap/feature.d.ts +2 -2
  323. package/dist/inbound-provider-imap/feature.js +42 -40
  324. package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
  325. package/dist/inbound-provider-imap/imap-client.js +20 -19
  326. package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
  327. package/dist/inbound-provider-inmemory/feature.js +19 -19
  328. package/dist/jobs/db/queries/retention.js +2 -2
  329. package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
  330. package/dist/jobs/events.js +1 -1
  331. package/dist/jobs/feature.d.ts +1 -0
  332. package/dist/jobs/feature.js +13 -10
  333. package/dist/jobs/handlers/detail.query.js +4 -4
  334. package/dist/jobs/handlers/list.query.js +1 -1
  335. package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
  336. package/dist/jobs/handlers/reindex-entity.job.js +1 -1
  337. package/dist/jobs/handlers/retry.write.js +1 -1
  338. package/dist/jobs/handlers/tenant-failures.query.d.ts +1 -1
  339. package/dist/jobs/handlers/tenant-failures.query.js +45 -41
  340. package/dist/jobs/handlers/tenant-runs.query.d.ts +1 -1
  341. package/dist/jobs/handlers/tenant-runs.query.js +54 -50
  342. package/dist/jobs/job-instant.js +1 -1
  343. package/dist/jobs/job-run-logger.js +15 -15
  344. package/dist/jobs/job-run-table.js +3 -3
  345. package/dist/jobs/screens.js +1 -1
  346. package/dist/jobs/tenant-job-failure-table.js +1 -1
  347. package/dist/jobs/tenant-job-run-table.js +1 -1
  348. package/dist/ledger/entity.js +5 -5
  349. package/dist/ledger/feature.js +3 -3
  350. package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
  351. package/dist/ledger/recurring.js +12 -12
  352. package/dist/ledger/schemas.js +1 -1
  353. package/dist/ledger/web/constants.js +2 -2
  354. package/dist/ledger/web/index.js +7 -7
  355. package/dist/legal-pages/feature.d.ts +4 -4
  356. package/dist/legal-pages/feature.js +25 -26
  357. package/dist/legal-pages/markdown.js +3 -3
  358. package/dist/legal-pages/web/client-plugin.js +23 -24
  359. package/dist/legal-pages/web/index.js +0 -1
  360. package/dist/mail-foundation/feature.d.ts +5 -5
  361. package/dist/mail-foundation/feature.js +27 -25
  362. package/dist/mail-transport-inmemory/feature.d.ts +4 -4
  363. package/dist/mail-transport-inmemory/feature.js +27 -28
  364. package/dist/mail-transport-smtp/feature.js +20 -20
  365. package/dist/managed-pages/branding.js +6 -6
  366. package/dist/managed-pages/feature.d.ts +26 -26
  367. package/dist/managed-pages/feature.js +34 -34
  368. package/dist/managed-pages/handlers/by-slug.query.js +5 -6
  369. package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
  370. package/dist/managed-pages/handlers/set.write.js +5 -7
  371. package/dist/managed-pages/screens/page-screens.js +11 -11
  372. package/dist/managed-pages/seeding.js +8 -8
  373. package/dist/managed-pages/table.js +13 -12
  374. package/dist/managed-pages/web/client-plugin.d.ts +1 -1
  375. package/dist/managed-pages/web/client-plugin.js +5 -5
  376. package/dist/managed-pages/web/i18n.js +1 -1
  377. package/dist/notes-history/entity.js +4 -4
  378. package/dist/notes-history/feature.d.ts +2 -2
  379. package/dist/notes-history-user-data/hooks.js +1 -1
  380. package/dist/page-render/layout.js +9 -9
  381. package/dist/page-render/markdown.js +11 -11
  382. package/dist/page-render/security-headers.js +7 -6
  383. package/dist/personal-access-tokens/feature.d.ts +1 -1
  384. package/dist/personal-access-tokens/feature.js +9 -5
  385. package/dist/personal-access-tokens/roles.js +2 -2
  386. package/dist/readiness/handlers/status.query.js +3 -3
  387. package/dist/renderer-foundation/api.js +12 -13
  388. package/dist/renderer-foundation/constants.js +2 -2
  389. package/dist/renderer-foundation/feature.js +7 -7
  390. package/dist/renderer-pdf/render-table-pdf.d.ts +1 -1
  391. package/dist/renderer-pdf/render-table-pdf.js +22 -10
  392. package/dist/secrets/feature.js +9 -9
  393. package/dist/secrets/web/constants.js +2 -2
  394. package/dist/seo/feature.js +3 -3
  395. package/dist/sessions/constants.d.ts +1 -1
  396. package/dist/sessions/constants.js +1 -1
  397. package/dist/sessions/feature.d.ts +2 -2
  398. package/dist/sessions/feature.js +12 -8
  399. package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
  400. package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
  401. package/dist/sessions/handlers/revoke.write.js +1 -1
  402. package/dist/sessions/schema/user-session.js +4 -4
  403. package/dist/sessions/screens.js +2 -2
  404. package/dist/sessions/session-callbacks.js +2 -2
  405. package/dist/sessions/session-revoked-event.js +2 -2
  406. package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
  407. package/dist/shared/decrypt-stored-pii.d.ts +1 -0
  408. package/dist/shared/decrypt-stored-pii.js +7 -2
  409. package/dist/shared/encrypt-for-direct-write.js +1 -1
  410. package/dist/shared/index.d.ts +1 -1
  411. package/dist/shared/index.js +1 -1
  412. package/dist/shared/mail-template-body.d.ts +6 -2
  413. package/dist/shared/mail-template-body.js +7 -4
  414. package/dist/shared/map-with-concurrency.js +5 -4
  415. package/dist/shared/parent-visibility.js +2 -2
  416. package/dist/shared/password-hashing.js +1 -1
  417. package/dist/shared/signup-handover.js +1 -2
  418. package/dist/shared/single-use-token-store.js +1 -1
  419. package/dist/step-dispatcher/feature.js +1 -0
  420. package/dist/subscription-dodo/client.d.ts +17 -0
  421. package/dist/subscription-dodo/client.js +31 -0
  422. package/dist/subscription-dodo/constants.d.ts +12 -0
  423. package/dist/subscription-dodo/constants.js +19 -0
  424. package/dist/subscription-dodo/feature.d.ts +37 -0
  425. package/dist/subscription-dodo/feature.js +144 -0
  426. package/dist/subscription-dodo/index.d.ts +2 -0
  427. package/dist/subscription-dodo/index.js +4 -0
  428. package/dist/subscription-dodo/plugin-methods.d.ts +23 -0
  429. package/dist/subscription-dodo/plugin-methods.js +260 -0
  430. package/dist/subscription-dodo/runtime.d.ts +27 -0
  431. package/dist/subscription-dodo/runtime.js +89 -0
  432. package/dist/subscription-dodo/verify-webhook.d.ts +37 -0
  433. package/dist/subscription-dodo/verify-webhook.js +189 -0
  434. package/dist/subscription-mollie/constants.js +15 -17
  435. package/dist/subscription-mollie/feature.d.ts +17 -18
  436. package/dist/subscription-mollie/feature.js +24 -24
  437. package/dist/subscription-mollie/index.js +3 -3
  438. package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
  439. package/dist/subscription-mollie/plugin-methods.js +15 -14
  440. package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
  441. package/dist/subscription-mollie/verify-webhook.js +27 -27
  442. package/dist/subscription-stripe/constants.js +4 -6
  443. package/dist/subscription-stripe/feature.d.ts +14 -14
  444. package/dist/subscription-stripe/feature.js +36 -34
  445. package/dist/subscription-stripe/index.js +6 -6
  446. package/dist/subscription-stripe/plugin-methods.js +18 -18
  447. package/dist/subscription-stripe/runtime.d.ts +1 -1
  448. package/dist/subscription-stripe/runtime.js +8 -7
  449. package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
  450. package/dist/subscription-stripe/verify-webhook.js +59 -61
  451. package/dist/tags/feature.d.ts +3 -3
  452. package/dist/tags/screens.js +1 -1
  453. package/dist/template-resolver/api.d.ts +3 -2
  454. package/dist/template-resolver/api.js +34 -12
  455. package/dist/template-resolver/compile-document-body.js +1 -0
  456. package/dist/template-resolver/feature.js +2 -2
  457. package/dist/template-resolver/handlers/bundle-import.js +3 -3
  458. package/dist/template-resolver/handlers/copy-locale.write.d.ts +5 -1
  459. package/dist/template-resolver/handlers/copy-locale.write.js +22 -7
  460. package/dist/template-resolver/handlers/diff-to-system.query.js +3 -2
  461. package/dist/template-resolver/handlers/document-base.js +3 -2
  462. package/dist/template-resolver/handlers/find-by-id.query.js +8 -7
  463. package/dist/template-resolver/handlers/list.query.js +6 -7
  464. package/dist/template-resolver/handlers/overview.query.d.ts +3 -0
  465. package/dist/template-resolver/handlers/overview.query.js +6 -3
  466. package/dist/template-resolver/handlers/shared.js +2 -2
  467. package/dist/template-resolver/handlers/template-write.js +3 -3
  468. package/dist/template-resolver/handlers/tenant-override-counts.query.js +1 -3
  469. package/dist/template-resolver/handlers/tenant-override-selection.d.ts +2 -1
  470. package/dist/template-resolver/handlers/tenant-overrides.query.js +28 -5
  471. package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
  472. package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
  473. package/dist/template-resolver/handlers/usage.query.d.ts +2 -1
  474. package/dist/template-resolver/handlers/usage.query.js +26 -16
  475. package/dist/template-resolver/handlers/validate.query.js +7 -2
  476. package/dist/template-resolver/seeding.js +30 -14
  477. package/dist/template-resolver/store-template-resource.d.ts +1 -0
  478. package/dist/template-resolver/store-template-resource.js +19 -4
  479. package/dist/template-resolver/table.js +10 -11
  480. package/dist/template-resolver/template-delivery-policy.d.ts +0 -5
  481. package/dist/template-resolver/template-delivery-policy.js +0 -6
  482. package/dist/template-resolver/template-meta.js +7 -4
  483. package/dist/template-resolver/template-preview.js +4 -3
  484. package/dist/template-resolver/web/client-plugin.js +2 -2
  485. package/dist/template-resolver/web/constants.d.ts +2 -2
  486. package/dist/template-resolver/web/constants.js +3 -3
  487. package/dist/template-resolver/web/index.d.ts +1 -0
  488. package/dist/template-resolver/web/index.js +1 -0
  489. package/dist/template-resolver/web/mail-layout-constants.d.ts +5 -0
  490. package/dist/template-resolver/web/mail-layout-constants.js +7 -0
  491. package/dist/tenant/feature.js +7 -7
  492. package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
  493. package/dist/tenant/handlers/member-directory.query.js +11 -4
  494. package/dist/tenant/handlers/members.query.js +1 -1
  495. package/dist/tenant/handlers/team-list.query.js +1 -1
  496. package/dist/tenant/handlers/tenant-directory.query.d.ts +1 -0
  497. package/dist/tenant/handlers/tenant-directory.query.js +9 -5
  498. package/dist/tenant/invitation-table.js +37 -40
  499. package/dist/tenant/membership-table.js +3 -3
  500. package/dist/tenant/schema/tenant.js +7 -9
  501. package/dist/tenant/screens.js +1 -1
  502. package/dist/tenant/seeding.d.ts +6 -5
  503. package/dist/tenant/seeding.js +12 -12
  504. package/dist/tenant/testing.js +5 -6
  505. package/dist/tenant/web/constants.js +2 -2
  506. package/dist/tenant-handover/events.js +5 -7
  507. package/dist/tenant-handover/feature.js +1 -1
  508. package/dist/tenant-handover/grant.js +1 -1
  509. package/dist/tenant-handover/handlers/claim.write.js +1 -1
  510. package/dist/tenant-handover/move-entity-graph.js +8 -9
  511. package/dist/tenant-handover/signup-handover-provider.js +1 -2
  512. package/dist/tenant-handover/transfer-graph.js +4 -5
  513. package/dist/tenant-lifecycle/boot-checks.js +1 -1
  514. package/dist/tenant-lifecycle/constants.d.ts +1 -0
  515. package/dist/tenant-lifecycle/constants.js +4 -0
  516. package/dist/tenant-lifecycle/feature.js +1 -1
  517. package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -2
  518. package/dist/tenant-settings/constants.js +1 -1
  519. package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
  520. package/dist/tenant-settings/tenant-defaults.js +9 -2
  521. package/dist/tier-engine/aggregate-id.d.ts +11 -11
  522. package/dist/tier-engine/aggregate-id.js +15 -16
  523. package/dist/tier-engine/compose-app.d.ts +14 -14
  524. package/dist/tier-engine/compose-app.js +1 -1
  525. package/dist/tier-engine/entity.js +18 -19
  526. package/dist/tier-engine/feature.d.ts +19 -17
  527. package/dist/tier-engine/feature.js +68 -69
  528. package/dist/tier-engine/handlers/active-tier.query.js +6 -7
  529. package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
  530. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +7 -8
  531. package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
  532. package/dist/tier-engine/tier-resolver.js +2 -2
  533. package/dist/tier-engine/trial.js +8 -8
  534. package/dist/tier-engine/web/constants.js +2 -2
  535. package/dist/tier-engine/web/index.js +4 -4
  536. package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
  537. package/dist/user/handlers/create.write.js +4 -4
  538. package/dist/user/schema/user.d.ts +11 -11
  539. package/dist/user/schema/user.js +45 -44
  540. package/dist/user/seeding.d.ts +15 -15
  541. package/dist/user/seeding.js +28 -28
  542. package/dist/user/testing.js +3 -3
  543. package/dist/user-data-rights/audit-download.d.ts +8 -8
  544. package/dist/user-data-rights/audit-download.js +19 -20
  545. package/dist/user-data-rights/boot-checks.js +1 -1
  546. package/dist/user-data-rights/deletion-token.js +1 -1
  547. package/dist/user-data-rights/feature.d.ts +49 -50
  548. package/dist/user-data-rights/feature.js +54 -53
  549. package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
  550. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
  551. package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
  552. package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
  553. package/dist/user-data-rights/handlers/export-status.query.js +8 -9
  554. package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
  555. package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
  556. package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
  557. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
  558. package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
  559. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
  560. package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
  561. package/dist/user-data-rights/handlers/request-export.write.js +42 -41
  562. package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
  563. package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
  564. package/dist/user-data-rights/index.js +8 -10
  565. package/dist/user-data-rights/lib/default-mailers.js +3 -3
  566. package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
  567. package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
  568. package/dist/user-data-rights/run-export-jobs.js +130 -237
  569. package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
  570. package/dist/user-data-rights/run-forget-cleanup.js +65 -83
  571. package/dist/user-data-rights/run-user-export.d.ts +34 -33
  572. package/dist/user-data-rights/run-user-export.js +55 -57
  573. package/dist/user-data-rights/schema/download-attempt.js +11 -11
  574. package/dist/user-data-rights/schema/download-token.js +37 -41
  575. package/dist/user-data-rights/schema/export-job.d.ts +4 -4
  576. package/dist/user-data-rights/schema/export-job.js +65 -67
  577. package/dist/user-data-rights/token-helpers.d.ts +8 -9
  578. package/dist/user-data-rights/token-helpers.js +21 -25
  579. package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
  580. package/dist/user-data-rights/web/client-plugin.js +1 -1
  581. package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
  582. package/dist/user-data-rights/web/constants.js +1 -1
  583. package/dist/user-data-rights/web/index.js +5 -5
  584. package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
  585. package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
  586. package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
  587. package/dist/user-data-rights/zip-path.d.ts +15 -15
  588. package/dist/user-data-rights/zip-path.js +41 -40
  589. package/dist/user-data-rights-defaults/feature.js +15 -15
  590. package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
  591. package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
  592. package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
  593. package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
  594. package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
  595. package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
  596. package/dist/user-profile/handlers/change-email.write.js +13 -13
  597. package/dist/user-profile/index.js +3 -3
  598. package/dist/user-profile/web/client-plugin.js +1 -1
  599. package/dist/user-profile/web/constants.js +1 -1
  600. package/dist/user-profile/web/profile-screen.js +1 -1
  601. package/dist/waitlist/handlers/export-purge.write.js +2 -1
  602. package/dist/waitlist/handlers/submit.write.js +5 -5
  603. package/dist/waitlist/import.d.ts +1 -0
  604. package/dist/waitlist/import.js +4 -0
  605. package/dist/waitlist/legacy-migrate.js +17 -6
  606. package/dist/waitlist/link-on-signup.js +18 -6
  607. package/dist/waitlist/options.d.ts +3 -0
  608. package/dist/workflow-runner/aggregate-id.d.ts +1 -1
  609. package/dist/workflow-runner/aggregate-id.js +1 -1
  610. package/dist/workflow-runner/event-subscriber.js +4 -5
  611. package/dist/workflow-runner/event-trigger.js +4 -17
  612. package/dist/workflow-runner/feature.js +2 -2
  613. package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
  614. package/dist/workflow-runner/pending-projection.js +6 -7
  615. package/dist/workflow-runner/runner.js +6 -6
  616. package/dist/workflow-runner/tables.js +6 -6
  617. package/dist/workflow-runner/workflow-registry.js +1 -1
  618. package/package.json +20 -10
  619. package/src/agent-tools/changes.json +6 -0
  620. package/src/audit/changes.json +12 -0
  621. package/src/auth-mfa/changes.json +6 -0
  622. package/src/billing-foundation/changes.json +55 -0
  623. package/src/cap-counter/changes.json +18 -0
  624. package/src/channel-email/changes.json +6 -0
  625. package/src/config/changes.json +12 -0
  626. package/src/crypto-shredding/changes.json +6 -0
  627. package/src/delivery/changes.json +30 -0
  628. package/src/form-draft/changes.json +6 -0
  629. package/src/host-routing/changes.json +6 -0
  630. package/src/inbound-mail-foundation/changes.json +18 -0
  631. package/src/jobs/changes.json +13 -0
  632. package/src/mail-foundation/changes.json +6 -0
  633. package/src/renderer-pdf/changes.json +6 -0
  634. package/src/step-dispatcher/changes.json +6 -0
  635. package/src/subscription-dodo/changes.json +14 -0
  636. package/src/subscription-stripe/changes.json +12 -0
  637. package/src/template-resolver/changes.json +48 -0
  638. package/src/tenant/changes.json +6 -0
  639. package/src/tenant-lifecycle/changes.json +6 -0
  640. package/src/tier-engine/changes.json +6 -0
  641. package/src/waitlist/changes.json +18 -0
  642. package/dist/billing-foundation/consumer-protection/stored-user-email.d.ts +0 -1
  643. package/dist/billing-foundation/consumer-protection/stored-user-email.js +0 -8
@@ -1,55 +1,29 @@
1
- // Async Export-Job Worker (S2.U3 Atom 3b) — pure Pipeline-Function.
1
+ // Async export-job worker — a pure pipeline function.
2
2
  //
3
- // Spec: docs/plans/architecture/user-data-rights.md "Async Export-Pipeline".
4
- // Cron-getriggert (siehe r.job-Wrap im feature.ts), iteriert ueber alle
5
- // pending-Jobs in einem Pass, fuehrt pro Job aus:
3
+ // A cron-triggered pass (see the r.job wrap in feature.ts) processes all
4
+ // pending jobs. Each job is claimed with optimistic locking through the version
5
+ // column: when two worker replicas pick the same job, the second one gets a
6
+ // VersionConflictError and skips it. The export bundle is built by
7
+ // run-user-export.ts, which also owns the cross-tenant iteration over the
8
+ // user's memberships. The requesting tenant's compliance profile supplies
9
+ // exportDownloadTtl for expiresAt, and the storage provider is resolved per
10
+ // tenant (S3 or local). The ZIP is streamed into storage atomically (tmp+rename
11
+ // locally, multipart upload on S3), so a throw anywhere in the pipeline marks
12
+ // the job failed without leaving a half-written ZIP at the final path.
6
13
  //
7
- // 1. Claim: `crud.update({status: running, startedAt})` mit
8
- // optimistic-locking via version-Spalte. Wenn 2 Worker-Replicas
9
- // denselben Job picken wollen, gewinnt der erste, der zweite
10
- // kriegt VersionConflictError + skippt.
14
+ // Before pickup, a stale pass fails running jobs whose
15
+ // startedAt + exportStaleTimeoutMinutes lies in the past, so worker crashes,
16
+ // OOM kills and replica restarts do not leave jobs stuck in running.
11
17
  //
12
- // 2. Bundle bauen: runUserExport({db, registry, userId, now}) →
13
- // UserExportBundle (siehe run-user-export.ts). Cross-Tenant-
14
- // Iteration ueber Memberships ist dort implementiert.
15
- //
16
- // 3. Profile-Resolution: `resolveProfileForTenant(requestedFromTenantId)`
17
- // liefert effective compliance-profile. exportDownloadTtl wird
18
- // fuer expiresAt-Berechnung gebraucht.
19
- //
20
- // 4. Storage-Provider builden: `buildStorageProvider(ctx, requestedFromTenantId)`
21
- // — file-foundation's createFileProviderForTenant-Pattern.
22
- // Pro Tenant unterschiedlicher Provider moeglich (S3 vs lokal).
23
- //
24
- // 5. ZIP-Stream: `createZipStream(bundleAsZipEntries(bundle))` →
25
- // AsyncIterable<Uint8Array>. Wir wrappen das in einen tracking-
26
- // Iterator der bytesWritten zaehlt.
27
- //
28
- // 6. Storage-Write: provider.writeStream(storageKey, zipStream).
29
- // Atomar via tmp+rename (lokal) bzw. multipart-upload (S3).
30
- //
31
- // 7. Job=done: `crud.update({status: "done", completedAt, expiresAt,
32
- // downloadStorageKey, bytesWritten})`.
33
- //
34
- // Bei Throw zwischen 2-7: Job=failed mit errorMessage. Storage-Write
35
- // ist atomar (writeStream rollback bei Error im Source-Stream), keine
36
- // halb-fertigen ZIPs am final-Pfad.
37
- //
38
- // **Stale-Detection:** vor dem Pickup-Pass laeuft ein separater
39
- // Stale-Pass: Jobs in `running` mit `startedAt + exportStaleTimeoutMinutes
40
- // < now` werden auf `failed` geflippt. Worker-Crashes / OOM-Kills /
41
- // Replica-Restarts hinterlassen sonst stuck-running-Jobs.
42
- //
43
- // **Storage-Cleanup:** dritter Pass loescht abgelaufene downloadStorageKeys
44
- // (`expiresAt + exportStorageCleanupGraceHours < now`) — abgelaufene ZIPs
45
- // auf S3 sollen nicht ewig liegen.
18
+ // A final cleanup pass deletes downloadStorageKeys past
19
+ // expiresAt + exportStorageCleanupGraceHours, so expired ZIPs do not stay in
20
+ // storage indefinitely.
46
21
  import { addDurationSpec } from "@cosmicdrift/kumiko-framework/compliance";
47
- import { PII_ERASED_SENTINEL } from "@cosmicdrift/kumiko-framework/crypto";
48
22
  import { createEventStoreExecutor, createTenantDb, fetchOne, selectMany, } from "@cosmicdrift/kumiko-framework/db";
49
23
  import { createSystemUser } from "@cosmicdrift/kumiko-framework/engine";
50
24
  import { createZipStream, tenantExportPrefix, } from "@cosmicdrift/kumiko-framework/files";
51
25
  import { resolveProfileForTenant } from "../compliance-profiles/index.js";
52
- import { decryptStoredPii } from "../shared/index.js";
26
+ import { decryptStoredUserEmail } from "../shared/index.js";
53
27
  import { userTable } from "../user/index.js";
54
28
  import { selectExportJobsForStorageCleanup } from "./db/queries/export-jobs.js";
55
29
  import { runUserExport } from "./run-user-export.js";
@@ -68,28 +42,23 @@ export const EXPORT_CLEANUP_LAST_RUN_TIMESTAMP_METRIC = "export_cleanup_last_run
68
42
  const crud = createEventStoreExecutor(exportJobsTable, exportJobEntity, {
69
43
  entityName: "export-job",
70
44
  });
71
- // Atom 4a — separater ES-Executor fuer Download-Tokens. crud.create
72
- // emittiert `exportDownloadToken.created`-Event in den event-store +
73
- // projected synchron auf read_export_download_tokens. KEIN direct-INSERT
74
- // (Memory `feedback_no_fake_dispatcher`).
45
+ // Separate event-store executor for download tokens. `crud.create` emits
46
+ // `exportDownloadToken.created` and synchronously updates
47
+ // `read_export_download_tokens`; direct inserts would bypass that projection.
75
48
  //
76
- // Exportiert weil Atom 4b's download-handler tokenCrud.update fuer
77
- // Audit-Felder (useCount, lastUsedAt, IP, UA) nutzt — ES-konsistent.
49
+ // Exported because the download handler uses tokenCrud.update for audit fields
50
+ // while preserving event-store consistency.
78
51
  export const tokenCrud = createEventStoreExecutor(exportDownloadTokensTable, exportDownloadTokenEntity, { entityName: "export-download-token" });
79
52
  export async function runExportJobs(args) {
80
53
  const { db, registry, buildStorageProvider, now, sendExportReadyEmail, sendExportFailedEmail } = args;
81
- // Boot-Misconfig-Check: wer sendExportReadyEmail setzt aber URL
82
- // vergisst, soll einen klaren Error sehen — VOR dem Pass-Loop, damit
83
- // der Throw nicht vom per-Job-try/catch (Atom 5.fix3) geschluckt wird.
84
- // Runtime-Callback-Failures sind best-effort, Boot-Misconfig ist hard-
85
- // fail (App-Author-Bug, kein Network-Hiccup).
54
+ // Validate this configuration before the pass loop so a missing URL is not
55
+ // swallowed by the per-job try/catch. Callback failures are best-effort, but
56
+ // this is a hard application configuration error.
86
57
  if (sendExportReadyEmail && args.appExportDownloadUrl === undefined) {
87
58
  throw new Error("user-data-rights: sendExportReadyEmail gesetzt aber appExportDownloadUrl fehlt — beide muessen zusammen konfiguriert sein");
88
59
  }
89
- // Pass 1: Stale-Detection — running jobs die laenger als das tenant-
90
- // spezifische exportStaleTimeoutMinutes haengen werden gefailed.
91
- // Wird VOR dem pickup-pass ausgefuehrt damit ein neuer Worker-Run
92
- // den vorhergehenden Crash erstmal als failed markiert.
60
+ // Fail running jobs whose tenant-specific stale timeout has elapsed before
61
+ // pickup, so a new run recovers jobs left behind by an earlier crash.
93
62
  const staleFailedJobIds = await staleDetectionPass({ db, now });
94
63
  // Pass 2: Pickup pending jobs + Process them.
95
64
  const completedJobIds = [];
@@ -110,20 +79,13 @@ export async function runExportJobs(args) {
110
79
  });
111
80
  if (outcome.kind === "done") {
112
81
  completedJobIds.push(job.id);
113
- // Worker hat plain im Memory; weitergeben fuer downstream-callbacks
114
- // + Atom-5-Tests (RunExportJobsResult.tokenByJobId). Plain landet
115
- // NICHT in Logs/Telemetry/jobRunsTable.
82
+ // Keep the plain token only in memory for downstream callbacks; never put it
83
+ // in logs, telemetry, or jobRunsTable.
116
84
  tokenByJobId.set(job.id, outcome.tokenPlain);
117
- // Atom 5: Email-Notification beim done-flip.
118
- //
119
- // Best-effort (Atom 5.fix3): Throw fuer Job A darf den Batch nicht
120
- // abwuergen — Job-Status ist bereits done committed, restliche
121
- // pending-Jobs muessen noch verarbeitet werden. Throw waere ein
122
- // Bug: r.job-Wrap markiert den Run failed, retry findet keinen
123
- // pending-Job mehr (Status=done) → silent miss + ZIP laeuft nach
124
- // TTL ab ohne dass der User die Email je bekommt. console.warn ist
125
- // die einzige Operator-Sichtbarkeit (runExportJobs-args fuehren
126
- // AppContext.log nicht durch — pure-function-Pattern).
85
+ // A notification failure must not abort the batch: the job is already done,
86
+ // and retrying the worker would find no pending job and silently miss the
87
+ // notification. `console.warn` is the only available operator visibility
88
+ // because these arguments do not carry AppContext.log.
127
89
  if (sendExportReadyEmail) {
128
90
  try {
129
91
  await fireExportReadyCallback({
@@ -145,10 +107,8 @@ export async function runExportJobs(args) {
145
107
  else if (outcome.kind === "failed") {
146
108
  failedJobIds.push(job.id);
147
109
  errors.push(outcome.error);
148
- // Atom 5: Email-Notification beim failed-flip — User soll wissen
149
- // dass er neuen Export anfordern muss. Best-effort analog
150
- // sendExportReadyEmail (Atom 5.fix3) — Email-Send-Throw darf den
151
- // Batch nicht killen.
110
+ // A failed-job notification is best-effort so an email-send failure cannot
111
+ // abort processing of the remaining pending jobs.
152
112
  if (sendExportFailedEmail) {
153
113
  try {
154
114
  await fireExportFailedCallback({
@@ -164,12 +124,11 @@ export async function runExportJobs(args) {
164
124
  }
165
125
  }
166
126
  }
167
- // "skipped" (claim race lost) wird still passiert — kein error,
168
- // anderer Worker hat den Job bereits.
127
+ // A claim race means another worker already owns the job; skip it without
128
+ // reporting an error.
169
129
  }
170
- // Pass 3: Storage-Cleanup — abgelaufene done-Jobs (expiresAt+grace < now)
171
- // bekommen ihren downloadStorageKey aus dem Storage-Provider geloescht
172
- // + im DB-Row genullt.
130
+ // Storage cleanup: expired done jobs (expiresAt + grace < now) get their
131
+ // storage object deleted and downloadStorageKey cleared in the database.
173
132
  const cleanedJobIds = await storageCleanupPass({
174
133
  db,
175
134
  buildStorageProvider,
@@ -194,19 +153,14 @@ async function fetchPendingJobs(db) {
194
153
  async function processJob(args) {
195
154
  const { db, registry, buildStorageProvider, now, job } = args;
196
155
  const executor = createSystemUser(job.requestedFromTenantId);
197
- // crud.update braucht TenantDb fuer Stream-Identity (Memory
198
- // feedback_event_store_tenant_consistency). system-mode bypassed
199
- // den auto-tenant-filter; Stream wird ueber requestedFromTenantId
200
- // konsistent gehalten.
156
+ // The event-store stream lookup requires a TenantDb even though an export job
157
+ // is tenant-agnostic. System mode bypasses the automatic tenant filter while
158
+ // `requestedFromTenantId` preserves the stream identity.
201
159
  const tdb = systemTenantDb(db, job.requestedFromTenantId);
202
- // Phase 1: Claim — version-bumped update, scheitert via VersionConflict
203
- // wenn paralleler Worker schon claimed hat.
204
- //
205
- // **Path-pre-claim** (Atom 4a.fix): downloadStorageKey wird HIER schon
206
- // persistiert (deterministischer Pfad aus job.id). Begründung: wenn
207
- // Worker zwischen ZIP-write und done-flip crashed, hat der Job-Row
208
- // bereits den Pfad → storageCleanupPass kann den orphan-ZIP via
209
- // failed-Job-Cleanup-Pass loeschen (sonst forever-leak im S3).
160
+ // Claim with a version check so a concurrent worker causes a conflict.
161
+ // Persist downloadStorageKey during the claim: if the worker crashes after
162
+ // writing the ZIP but before marking the job done, cleanup can find the
163
+ // orphaned object instead of leaving it in storage indefinitely.
210
164
  const storageKey = buildExportStorageKey(job);
211
165
  const claimResult = await crud.update({
212
166
  id: job.id,
@@ -218,11 +172,11 @@ async function processJob(args) {
218
172
  },
219
173
  }, executor, tdb);
220
174
  if (!claimResult.isSuccess) {
221
- // Race-Loss: anderer Worker hat den Job claimed. Skip silently.
175
+ // Another worker won the claim race, so skip this job without logging an error.
222
176
  return { kind: "skipped" };
223
177
  }
224
- // Phase 2-6: Bundle bauen + ZIP-streamen + Storage-Write.
225
- // Errors bis zur completion werden als job=failed materialisiert.
178
+ // Build the bundle, stream the ZIP, and write it to storage. Errors before
179
+ // completion are materialized as a failed job.
226
180
  try {
227
181
  const bundle = await runUserExport({
228
182
  db,
@@ -238,11 +192,10 @@ async function processJob(args) {
238
192
  tenantId: job.requestedFromTenantId,
239
193
  });
240
194
  const ttl = profile.profile.userRights.exportDownloadTtl;
241
- // Per-Tenant-Provider-Cache: Cross-Tenant-fileRefs (Alice Member von
242
- // Tenant A + B → File-Refs aus beiden) brauchen pro Tenant separat
243
- // einen Provider-Build (S3-Bucket pro Tenant, andere config). Job-
244
- // Tenant ist die Identitaet fuer den write-Pfad; read-Pfade gehen
245
- // ueber den fileRef.tenantId.
195
+ // Cross-tenant file references require a provider per tenant because storage
196
+ // configuration may differ. Cache each provider so a tenant is initialized
197
+ // only once; the requested tenant owns the write path, while reads use each
198
+ // file reference's tenantId.
246
199
  const providerCache = new Map();
247
200
  const cachedProvider = async (tenantId) => {
248
201
  let p = providerCache.get(tenantId);
@@ -253,32 +206,26 @@ async function processJob(args) {
253
206
  return p;
254
207
  };
255
208
  const writeProvider = await cachedProvider(job.requestedFromTenantId);
256
- // writeStream + readStream sind im FileStorageProvider-Type required
257
- // (Atom 3c.fix Type-Honesty) — keine Runtime-Optional-Checks mehr noetig.
209
+ // The provider type requires both methods, so runtime optional checks are
210
+ // unnecessary.
258
211
  const tracker = countingStream(createZipStream(bundleToZipEntries(bundle, now, cachedProvider)));
259
212
  await writeProvider.writeStream(storageKey, tracker.stream, {
260
213
  mimeType: "application/zip",
261
214
  });
262
- // Phase 7: Token VOR done-flip (Atom 4a.fix Sequencing).
263
- //
264
- // Wenn Token-Create failt, bleibt Job in `running` → catch-Pfad
265
- // flippt auf failed (monotone Status-Transition). Wenn Token-Create
266
- // VOR und done-flip NACH: tightes Race-Window in dem ein Worker-
267
- // Crash zwischen den zwei calls einen Token + ZIP orphans. Beides
268
- // catched die Stale-Detection im naechsten Pass — Job=failed,
269
- // Storage-Cleanup-Pass clearted den ZIP (path-pre-claim oben).
215
+ // Create the token before the done transition: if token creation fails, the
216
+ // job is still running and the catch path marks it failed (monotonic status).
217
+ // A crash between the two calls can orphan a token and a ZIP; the next
218
+ // stale-detection pass fails the job and the cleanup pass removes the ZIP.
270
219
  //
271
- // Plain-Token bleibt im Worker-Memory + wird via RunExportJobsResult
272
- // an Atom 5 (Notification) weitergegeben. NUR der hash landet in DB.
273
- // ES via tokenCrud.create — kein direct-INSERT.
220
+ // The plain token stays in worker memory and reaches the notification
221
+ // through RunExportJobsResult; only its hash is stored.
274
222
  //
275
- // **Re-Run (fw#832):** Flippt ein Operator einen done/failed-Job
276
- // zurueck auf pending (Plain-Token verloren, Email-Fail), existiert
277
- // fuers jobId schon eine Token-Row. Ein zweites create wuerde ein
278
- // zweites `created`-Event auf frischem Aggregat emittieren — der
279
- // Projection-Rebuild kollidiert dann dauerhaft am one_per_job-Index.
280
- // Stattdessen: Token-Rotation in place auf demselben Aggregat
281
- // (update mit frischem Hash + Reset der Audit-Felder).
223
+ // **Reruns:** when an operator flips a done or failed job back to pending
224
+ // (lost plain token, email failure), a token row already exists for the
225
+ // jobId. A second create would emit another created event on a fresh
226
+ // aggregate, and projection rebuilds would keep colliding on the
227
+ // one_per_job index. Rotate the token in place on the existing aggregate
228
+ // instead (fresh hash, audit fields reset).
282
229
  const expiresAt = addDurationSpec(now, ttl);
283
230
  const { plain: tokenPlain, hash: tokenHash } = await generateDownloadToken();
284
231
  const existingToken = await fetchOne(db, exportDownloadTokensTable, { jobId: job.id });
@@ -301,39 +248,33 @@ async function processJob(args) {
301
248
  tokenHash,
302
249
  issuedAt: now,
303
250
  expiresAt,
304
- // Atom 4a.fix: useCount explizit 0 statt default null. 4b's
305
- // Verify-Pfad incrementiert via `useCount + 1` ohne COALESCE-
306
- // Defensiv-Code.
251
+ // Set useCount explicitly to 0 so verification can increment it with
252
+ // `useCount + 1` without defensive COALESCE logic.
307
253
  useCount: 0,
308
254
  }, executor, tdb);
309
255
  if (!tokenCreateResult.isSuccess) {
310
- // Token-Creation failed VOR done-flip → Job bleibt running.
311
- // Catch-Pfad flippt auf failed mit klarer Diagnose. Storage-
312
- // Cleanup-Pass clearted den orphan-ZIP (path bereits via claim
313
- // persistiert).
256
+ // Token creation failed before the done transition. The catch path marks the
257
+ // job failed, and cleanup can remove the ZIP whose path was persisted at
258
+ // claim time.
314
259
  throw new Error(`Job ${job.id}: Token-Creation failed before done-flip. ` +
315
260
  `${tokenCreateResult.error?.code ?? "unknown"}`);
316
261
  }
317
- // Phase 8: Job=done. expiresAt wurde oben fuer Token gesetzt; identisch
318
- // hier persistiert (denormalisiert in beiden Tabellen).
262
+ // Persist the same expiresAt used for the token on the job row as well.
319
263
  const doneResult = await crud.update({
320
264
  id: job.id,
321
- version: job.version + 1, // +1 weil wir bereits via claim einen update gemacht haben
265
+ version: job.version + 1, // +1 because the claim already performed one update
322
266
  changes: {
323
267
  status: EXPORT_JOB_STATUS.Done,
324
268
  completedAt: now,
325
- // downloadStorageKey ist beim claim bereits gesetzt — kein
326
- // Re-Set hier noetig (waere identisch).
269
+ // downloadStorageKey was set during the claim, so it does not need to be
270
+ // written again.
327
271
  expiresAt,
328
272
  bytesWritten: tracker.bytes,
329
273
  },
330
274
  }, executor, tdb);
331
275
  if (!doneResult.isSuccess) {
332
- // Sehr unerwartet — wir haben gerade claimed (version+1), niemand
333
- // sollte den Job zwischenzeitlich aendern. Materialisieren als
334
- // failed damit der Operator das sieht. Token-Row bleibt orphan
335
- // bis Atom-spaeter-Cleanup; Storage-ZIP wird via failed-Cleanup
336
- // gecleared.
276
+ // The done transition failed after claiming. Mark the job failed so the
277
+ // operator can see the error; the token and ZIP remain for later cleanup.
337
278
  throw new Error(`Job ${job.id}: failed to flip status=done after successful Token-Create. ` +
338
279
  `${doneResult.error?.code ?? "unknown"}`);
339
280
  }
@@ -341,9 +282,8 @@ async function processJob(args) {
341
282
  }
342
283
  catch (e) {
343
284
  const message = e instanceof Error ? e.message : String(e);
344
- // Best-effort failure-flip. Wenn auch das failed (DB down etc.),
345
- // bleibt der Job in running stehen — Stale-Detection im naechsten
346
- // Worker-Run faengt ihn dann auf.
285
+ // Failure handling is best-effort. If the failure transition also fails, the
286
+ // job remains running and stale detection will recover it on a later run.
347
287
  await crud
348
288
  .update({
349
289
  id: job.id,
@@ -370,12 +310,10 @@ async function processJob(args) {
370
310
  }
371
311
  async function staleDetectionPass(args) {
372
312
  const { db, now } = args;
373
- // **Kein Coarse-Filter** — der vorherige `startedAt <= now-1h` Filter
374
- // war fragile: profile.exportStaleTimeoutMinutes hat Default 30min und
375
- // erlaubt per-Tenant-Override auf z.B. 5min. Ein 60min-Coarse-Filter
376
- // wuerde 30-60min-alte stale-Jobs UNERKANNT lassen. Cron laeuft alle
377
- // 60s, zu jedem Zeitpunkt sind nur wenige Jobs in `running` —
378
- // alle fetchen + profile-resolve im Loop ist bezahlbar + korrekt.
313
+ // Fetch all running jobs instead of applying a coarse one-hour filter:
314
+ // exportStaleTimeoutMinutes is tenant-specific and may be as low as five
315
+ // minutes, so a coarse filter could miss stale jobs. The expected number of
316
+ // running jobs makes resolving each tenant's profile practical.
379
317
  const candidates = await selectMany(db, exportJobsTable, { status: EXPORT_JOB_STATUS.Running });
380
318
  const failed = [];
381
319
  for (const c of candidates) {
@@ -397,41 +335,21 @@ async function staleDetectionPass(args) {
397
335
  }, createSystemUser(c.requestedFromTenantId), systemTenantDb(db, c.requestedFromTenantId));
398
336
  if (result.isSuccess)
399
337
  failed.push(c.id);
400
- // Race-loss (Worker hat das Job parallel completed) → skip
338
+ // Another worker completed the job, so skip it.
401
339
  }
402
340
  }
403
341
  return failed;
404
342
  }
405
343
  async function storageCleanupPass(args) {
406
344
  const { db, buildStorageProvider, now, metrics } = args;
407
- // Zwei Cleanup-Pfade:
408
- //
409
- // 1. **Done-Jobs** (TTL-expired): expiresAt + grace < now → cleanup.
410
- // Per-Tenant-Grace aus Profile (default 24h post-expiry damit
411
- // gleichzeitige Re-Downloads bei Connection-Abbrueche moeglich).
412
- //
413
- // 2. **Failed-Jobs mit downloadStorageKey** (Atom 4a.fix orphan-cleanup):
414
- // Worker hat ZIP geschrieben, dann ist VOR done-flip etwas
415
- // schiefgegangen (Token-Create-Fail, done-flip-Fail, Stale-
416
- // Detection mid-write). Job=failed mit gesetztem downloadStorageKey =
417
- // ZIP-Orphan in Storage, kein User-Pfad zum download.
418
- //
419
- // **Strategie failed-Jobs: SOFORTIGE Cleanup ohne Grace.**
420
- //
421
- // Trade-off (DSGVO vs Audit-Forensik):
422
- // - DSGVO: User-Daten-ZIP einer failed-export-Anfrage hat keinen
423
- // legitimen Aufbewahrungs-Grund — hier ueberwiegt das Recht auf
424
- // Loeschung
425
- // - Audit-Forensik: forensische Untersuchung ("was ist im ZIP gelandet,
426
- // warum failed der Job") wuerde von einer Grace-Periode profitieren —
427
- // ABER: Token wurde nie ausgegeben (kein User hat das ZIP gesehen),
428
- // also keine User-Schaden-Forensik. Operator-Audit hat job.errorMessage
429
- // + Stack-Trace, das reicht
430
- // → Trade-off zugunsten DSGVO entschieden. Wenn ein Operator forensik
431
- // braucht, muss er das vor dem Cleanup-Pass capturen (out-of-band).
432
- //
433
- // **SQL-Filter:** status IN (done, failed) + downloadStorageKey IS NOT NULL
434
- // via selectMany (db/queries/export-jobs.ts).
345
+ // Done jobs are cleaned after expiresAt plus the tenant's grace period
346
+ // (default 24h after expiry, so re-downloads after a dropped connection still
347
+ // work). Failed jobs with a persisted downloadStorageKey are orphaned ZIPs:
348
+ // the worker wrote the ZIP but failed before the done flip, so no token was
349
+ // ever issued and no user has seen it. They are removed immediately, without
350
+ // grace: the right to erasure outweighs forensics here, and the job's
351
+ // errorMessage and stack trace are the operator's audit record. An operator
352
+ // who needs the ZIP itself must capture it out of band before this pass.
435
353
  const candidates = await selectExportJobsForStorageCleanup(db, EXPORT_JOB_STATUS.Done, EXPORT_JOB_STATUS.Failed);
436
354
  const cleaned = [];
437
355
  // Candidates past their due-instant, keyed by that instant — used below to
@@ -539,11 +457,9 @@ function reportCleanupLastRun(metrics, now) {
539
457
  console.warn(`[user-data-rights:run-export-jobs] metrics.set(${EXPORT_CLEANUP_LAST_RUN_TIMESTAMP_METRIC}) failed err=${err instanceof Error ? err.message : String(err)}`);
540
458
  }
541
459
  }
542
- // Wrapper: crud.update braucht TenantDb. ExportJob ist tenant-agnostisch
543
- // (1 Job pro userId), aber der event-store-Stream-Lookup erwartet einen
544
- // Tenant-Context. system-mode TenantDb bypassed den auto-tenant-filter
545
- // — wir nutzen `requestedFromTenantId` als Stream-Identity damit der
546
- // Stream-Counter konsistent bleibt (Memory feedback_event_store_tenant_consistency).
460
+ // `crud.update` requires a TenantDb for event-store stream lookup. Export jobs are tenant-agnostic
461
+ // (one job per userId), so system mode bypasses the automatic tenant filter while
462
+ // `requestedFromTenantId` preserves stream identity and keeps the stream counter consistent.
547
463
  // @wrapper-known semantic-alias
548
464
  function systemTenantDb(db, tenantId) {
549
465
  return createTenantDb({ db, tenantId, mode: "system" });
@@ -552,41 +468,31 @@ function buildExportStorageKey(job) {
552
468
  return `${tenantExportPrefix(job.requestedFromTenantId)}${job.id}.zip`;
553
469
  }
554
470
  /**
555
- * Konvertiert das UserExportBundle in eine `AsyncIterable<ZipEntry>`
556
- * fuer createZipStream.
471
+ * Converts a UserExportBundle to an `AsyncIterable<ZipEntry>` for
472
+ * createZipStream. `bundle.json` contains the bundle metadata and file
473
+ * references; each file is streamed from the provider at its `zipPath`, so
474
+ * large files do not require an in-memory copy.
557
475
  *
558
- * Bundle-Layout (Atom 3c):
559
- * bundle.json — Top-Level-Bundle-Metadata
560
- * + tenant-sections + flat
561
- * fileRefs[] mit zipPath
562
- * pro Datei
563
- * files/<tenantId>/<fileRefId>-<name> — File-Binaries, Pfad
564
- * identisch zu fileRef.zipPath
565
- * im JSON. Bytes via
566
- * provider.readStream — kein
567
- * Memory-Spike, auch grosse
568
- * PDFs streamen durch.
569
- *
570
- * `getProvider` ist ein async-cached Resolver pro tenantId. Caller
571
- * (processJob) baut die Caching-Map damit jeder Tenant nur EINMAL
572
- * via `buildStorageProvider` materialisiert wird.
476
+ * `getProvider` caches providers by tenantId, ensuring each tenant is built
477
+ * only once while processing cross-tenant file references.
573
478
  */
574
479
  async function* bundleToZipEntries(bundle, mtime, getProvider) {
575
- // bundle.json zuerst — Reader-Tools koennen das frueh parsen.
480
+ // Emit bundle.json first so readers can parse the metadata before the file
481
+ // entries arrive.
576
482
  const bundleJson = JSON.stringify(bundle, null, 2);
577
483
  yield {
578
484
  path: "bundle.json",
579
485
  data: oneShot(new TextEncoder().encode(bundleJson)),
580
486
  mtime,
581
487
  };
582
- // File-Binaries: pro fileRef einen ZIP-Entry. data ist der readStream
583
- // direkt — der ZIP-Builder konsumiert chunk-fuer-chunk, kein Upfront-
584
- // Memory-Spike pro File. Bei 50 PDFs à 10MB werden trotzdem max. ~64KB
585
- // (default highWaterMark) gleichzeitig im Heap gehalten.
488
+ // File binaries: one ZIP entry per fileRef. `data` is the readStream directly,
489
+ // so the ZIP builder consumes chunks without an upfront memory spike. Even
490
+ // with 50 PDFs at 10MB each, at most about 64KB (default highWaterMark) is
491
+ // held in the heap at once.
586
492
  for (const ref of bundle.fileRefs) {
587
493
  const provider = await getProvider(ref.tenantId);
588
- // readStream ist required im FileStorageProvider-Type (Atom 3c.fix
589
- // Type-Honesty) — kein Runtime-Optional-Check noetig.
494
+ // `readStream` is required by the FileStorageProvider type, so no runtime
495
+ // optional check is needed.
590
496
  yield {
591
497
  path: ref.zipPath,
592
498
  data: provider.readStream(ref.storageKey),
@@ -598,9 +504,9 @@ async function* oneShot(bytes) {
598
504
  yield bytes;
599
505
  }
600
506
  /**
601
- * Wraps an `AsyncIterable<Uint8Array>` und zaehlt total-bytes. Nach
602
- * `for await` ist `tracker.bytes` der finale Wert. Verwendet fuer
603
- * bytesWritten im Job-Row.
507
+ * Wraps an `AsyncIterable<Uint8Array>` and counts the total bytes. After
508
+ * `for await`, `tracker.bytes` contains the final value used for
509
+ * `bytesWritten` in the job row.
604
510
  */
605
511
  function countingStream(source) {
606
512
  const tracker = { bytes: 0 };
@@ -619,46 +525,34 @@ function countingStream(source) {
619
525
  },
620
526
  };
621
527
  }
622
- // Atom 5 — userEmail-Lookup fuer notification-callback.
623
- //
624
- // **Direct-DB statt queryAs:** Worker-AppContext hat kein queryAs
625
- // (anders als HandlerContext im request-Pfad). Pattern matched
626
- // runUserExport's Cross-Tenant-Iteration die ebenso direkt
627
- // tenantMembershipsTable liest. Cross-feature-API via dispatcher
628
- // haette einen JobContext-Wrapper gebraucht den der framework noch
629
- // nicht hat — wenn das mal kommt, ist der hier Refactor-Kandidat.
528
+ // Reads directly from the database because the worker's AppContext has no
529
+ // queryAs, unlike HandlerContext in the request path. The cross-tenant
530
+ // export iteration uses the same approach; a dispatcher-based cross-feature
531
+ // API would require a JobContext wrapper that the framework does not provide.
630
532
  //
631
- // **tenant-agnostic:** userTable.id ist UNIQUE (PK). Cross-Tenant
632
- // (Alice in Tenant A+B) hat trotzdem nur 1 user-Row mit ihrer
633
- // Heim-Tenant als tenantId. Lookup ohne tenantId-filter findet sie
634
- // aus jedem Worker-Tenant-Context.
533
+ // userTable.id is globally unique. Even when a user belongs to multiple
534
+ // tenants, there is only one user row with its home tenant as tenantId, so a
535
+ // lookup without a tenantId filter works from any worker tenant context.
635
536
  async function lookupUserContact(db, userId) {
636
537
  // @cast-boundary db-row.
637
538
  const row = (await fetchOne(db, userTable, { id: userId }));
638
539
  if (!row?.email)
639
540
  return null;
640
- const email = await decryptStoredPii(row.email, "email", "user-data-rights:export-ready");
641
- if (email === PII_ERASED_SENTINEL)
541
+ const email = await decryptStoredUserEmail(row.email, "user-data-rights:export-ready");
542
+ if (email === null)
642
543
  return null;
643
544
  return { email, locale: row.locale ?? null };
644
545
  }
645
- // Atom 5 — Email-Notification beim done-flip. Best-effort:
646
- // Throw vom Callback bubbelt zum r.job-handler hoch + Worker-Run wird
647
- // als failed in jobRunsTable gemerkt. Operator kann via jobs:write:retry
648
- // den Worker-Run erneut anstossen — der Job selbst bleibt done, das
649
- // retry findet keinen pending-Job mehr aber die Audit-Log zeigt den
650
- // Failure.
546
+ // The callback is best-effort: its failure must not abort the batch because
547
+ // the job is already done, and retrying the worker will not find it pending.
548
+ // The failure remains visible in the worker-run audit log.
651
549
  async function fireExportReadyCallback(args) {
652
550
  const contact = await lookupUserContact(args.db, args.job.userId);
653
551
  if (!contact) {
654
- // User-Row fehlt (z.B. forget-Pfad mid-export). Skip-Notification mit
655
- // Operator-Alert via job-run-Log statt Throw — Job bleibt done, User
656
- // hat ja seinen Token via export-status.query erreichbar (UI-Pfad).
657
- // **console.warn statt ctx.log:** runExportJobs-args fuehren AppContext.log
658
- // aktuell nicht durch (Worker-pure-function-Pattern). console.warn ist
659
- // die einzige Operator-Sichtbarkeit fuer den missing-user-edge-case.
660
- // Wenn jobs-Feature spaeter ctx.log threadet oder Worker-args erweitert
661
- // werden, hier Refactor-Kandidat.
552
+ // If the user row is missing, skip the notification instead of throwing:
553
+ // the job remains done, and the token remains available through
554
+ // export-status.query. console.warn is used because the worker arguments
555
+ // do not currently carry AppContext.log.
662
556
  // biome-ignore lint/suspicious/noConsole: operator-visibility for missing-user edge-case
663
557
  console.warn(`[user-data-rights:run-export-jobs] userId=${args.job.userId} hat kein userEmail — sendExportReadyEmail skipped`);
664
558
  // skip: missing user-email row; console.warn above provides operator visibility
@@ -680,7 +574,6 @@ async function fireExportReadyCallback(args) {
680
574
  bytesWritten: args.bytesWritten,
681
575
  });
682
576
  }
683
- // Atom 5 — Email-Notification beim failed-flip.
684
577
  async function fireExportFailedCallback(args) {
685
578
  const contact = await lookupUserContact(args.db, args.job.userId);
686
579
  if (!contact) {