@cosmicdrift/kumiko-samples 0.377.0 → 0.379.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 (526) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +18 -17
  3. package/packages/bundled-features/src/account-security/feature.ts +2 -2
  4. package/packages/bundled-features/src/account-security/index.ts +8 -8
  5. package/packages/bundled-features/src/account-security/{constants.ts → web/constants.ts} +0 -2
  6. package/packages/bundled-features/src/account-security/{i18n.ts → web/i18n.ts} +0 -1
  7. package/packages/bundled-features/src/admin-shell/feature.ts +10 -9
  8. package/packages/bundled-features/src/admin-shell/index.ts +8 -8
  9. package/packages/bundled-features/src/admin-shell/web/client-plugin.tsx +2 -3
  10. package/packages/bundled-features/src/admin-shell/{constants.ts → web/constants.ts} +0 -1
  11. package/packages/bundled-features/src/admin-shell/web/default-translations.ts +10 -0
  12. package/packages/bundled-features/src/admin-shell/web/i18n.ts +51 -9
  13. package/packages/bundled-features/src/admin-shell/web/index.ts +0 -1
  14. package/packages/bundled-features/src/audit/changes.json +7 -0
  15. package/packages/bundled-features/src/audit/feature.ts +72 -64
  16. package/packages/bundled-features/src/audit/handlers/audit-events.ts +153 -0
  17. package/packages/bundled-features/src/audit/handlers/audit-system-db.ts +13 -0
  18. package/packages/bundled-features/src/audit/handlers/details-all-tenants.query.ts +26 -0
  19. package/packages/bundled-features/src/audit/handlers/details.query.ts +11 -39
  20. package/packages/bundled-features/src/audit/handlers/list-all-tenants.query.ts +47 -0
  21. package/packages/bundled-features/src/audit/handlers/list.query.ts +16 -123
  22. package/packages/bundled-features/src/audit/handlers/resolve-audit-tenant.ts +21 -7
  23. package/packages/bundled-features/src/audit/index.ts +1 -1
  24. package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +4 -4
  25. package/packages/bundled-features/src/audit/screens.ts +82 -0
  26. package/packages/bundled-features/src/audit/web/client-plugin.tsx +2 -3
  27. package/packages/bundled-features/src/audit/{constants.ts → web/constants.ts} +11 -1
  28. package/packages/bundled-features/src/audit/web/default-translations.ts +7 -0
  29. package/packages/bundled-features/src/audit/web/i18n.ts +33 -7
  30. package/packages/bundled-features/src/audit/web/index.ts +0 -2
  31. package/packages/bundled-features/src/auth-email-password/bootstrap.ts +6 -6
  32. package/packages/bundled-features/src/auth-email-password/changes.json +6 -0
  33. package/packages/bundled-features/src/auth-email-password/errors.ts +1 -1
  34. package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
  35. package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts +1 -1
  36. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +4 -4
  37. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +3 -3
  38. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
  39. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +38 -4
  40. package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +1 -1
  41. package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +1 -1
  42. package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +1 -1
  43. package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +1 -1
  44. package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +1 -1
  45. package/packages/bundled-features/src/auth-email-password/index.ts +6 -1
  46. package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +1 -1
  47. package/packages/bundled-features/src/auth-email-password/signup-activation.ts +1 -1
  48. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +0 -1
  49. package/packages/bundled-features/src/auth-email-password/web/auth-form-logic.ts +0 -1
  50. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +0 -1
  51. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +0 -1
  52. package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +0 -1
  53. package/packages/bundled-features/src/auth-email-password/web/client-actions.ts +0 -1
  54. package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +1 -2
  55. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +0 -1
  56. package/packages/bundled-features/src/auth-email-password/{constants.ts → web/constants.ts} +3 -6
  57. package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +0 -1
  58. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +0 -1
  59. package/packages/bundled-features/src/auth-email-password/{i18n.ts → web/i18n.ts} +3 -4
  60. package/packages/bundled-features/src/auth-email-password/web/index.ts +1 -2
  61. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +2 -3
  62. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +0 -1
  63. package/packages/bundled-features/src/auth-email-password/{mail-i18n.ts → web/mail-i18n.ts} +0 -1
  64. package/packages/bundled-features/src/auth-email-password/web/request-account-unlock-screen.tsx +0 -1
  65. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +0 -1
  66. package/packages/bundled-features/src/auth-email-password/web/session-bootstrap-error.tsx +0 -1
  67. package/packages/bundled-features/src/auth-email-password/web/session.tsx +0 -1
  68. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +2 -3
  69. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +0 -1
  70. package/packages/bundled-features/src/auth-email-password/web/tenant-menu-items.tsx +0 -1
  71. package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +0 -1
  72. package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +0 -1
  73. package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +0 -1
  74. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +0 -1
  75. package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
  76. package/packages/bundled-features/src/auth-mfa/errors.ts +2 -2
  77. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -1
  78. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  79. package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +1 -1
  80. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
  81. package/packages/bundled-features/src/auth-mfa/index.ts +8 -8
  82. package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +4 -4
  83. package/packages/bundled-features/src/auth-mfa/screens.ts +1 -1
  84. package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +0 -1
  85. package/packages/bundled-features/src/auth-mfa/{constants.ts → web/constants.ts} +0 -1
  86. package/packages/bundled-features/src/auth-mfa/web/i18n.ts +0 -1
  87. package/packages/bundled-features/src/auth-mfa/web/index.ts +0 -1
  88. package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +0 -1
  89. package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +1 -2
  90. package/packages/bundled-features/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +0 -1
  91. package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +1 -2
  92. package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +21 -23
  93. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +0 -1
  94. package/packages/bundled-features/src/billing-foundation/changes.json +18 -0
  95. package/packages/bundled-features/src/billing-foundation/checkout-core.ts +3 -3
  96. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-text.ts +2 -2
  97. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +11 -3
  98. package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +11 -1
  99. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +1 -1
  100. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +12 -1
  101. package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +12 -0
  102. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +6 -1
  103. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +1 -1
  104. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +167 -28
  105. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +102 -3
  106. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +43 -12
  107. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +202 -0
  108. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +70 -0
  109. package/packages/bundled-features/src/billing-foundation/events.ts +20 -2
  110. package/packages/bundled-features/src/billing-foundation/feature.ts +5 -5
  111. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +82 -9
  112. package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +1 -1
  113. package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +1 -1
  114. package/packages/bundled-features/src/billing-foundation/index.ts +27 -27
  115. package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +7 -7
  116. package/packages/bundled-features/src/billing-foundation/screens.ts +1 -1
  117. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +1 -1
  118. package/packages/bundled-features/src/billing-foundation/types.ts +17 -3
  119. package/packages/bundled-features/src/billing-foundation/validate-options.ts +3 -3
  120. package/packages/bundled-features/src/billing-foundation/web/billing-contact-plan.tsx +1 -3
  121. package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +5 -7
  122. package/packages/bundled-features/src/billing-foundation/web/cancel-contract-dialog.tsx +1 -3
  123. package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +1 -3
  124. package/packages/bundled-features/src/billing-foundation/web/client-plugin.tsx +1 -2
  125. package/packages/bundled-features/src/billing-foundation/{constants.ts → web/constants.ts} +37 -31
  126. package/packages/bundled-features/src/billing-foundation/{consumer-protection → web/consumer-protection}/consent-locale.ts +0 -1
  127. package/packages/bundled-features/src/billing-foundation/{contact-card.ts → web/contact-card.ts} +0 -2
  128. package/packages/bundled-features/src/billing-foundation/web/index.ts +14 -15
  129. package/packages/bundled-features/src/billing-foundation/web/localized-params.ts +0 -2
  130. package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +5 -2
  131. package/packages/bundled-features/src/cap-counter/changes.json +12 -0
  132. package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -0
  133. package/packages/bundled-features/src/cap-overview/feature.ts +1 -1
  134. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
  135. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -2
  136. package/packages/bundled-features/src/cap-overview/index.ts +9 -9
  137. package/packages/bundled-features/src/cap-overview/screens.ts +2 -2
  138. package/packages/bundled-features/src/cap-overview/web/cap-cards-panel.tsx +1 -2
  139. package/packages/bundled-features/src/cap-overview/web/cap-usage-bar.tsx +1 -2
  140. package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +0 -2
  141. package/packages/bundled-features/src/cap-overview/web/client-plugin.tsx +3 -4
  142. package/packages/bundled-features/src/cap-overview/{constants.ts → web/constants.ts} +0 -1
  143. package/packages/bundled-features/src/cap-overview/web/index.ts +0 -1
  144. package/packages/bundled-features/src/cap-overview/{usage-math.ts → web/usage-math.ts} +1 -2
  145. package/packages/bundled-features/src/compliance-profiles/feature.ts +5 -5
  146. package/packages/bundled-features/src/compliance-profiles/index.ts +6 -6
  147. package/packages/bundled-features/src/compliance-profiles/web/client-plugin.tsx +2 -3
  148. package/packages/bundled-features/src/compliance-profiles/web/compliance-profile-catalog.tsx +1 -2
  149. package/packages/bundled-features/src/compliance-profiles/{constants.ts → web/constants.ts} +0 -1
  150. package/packages/bundled-features/src/compliance-profiles/web/i18n.ts +0 -1
  151. package/packages/bundled-features/src/compliance-profiles/web/index.ts +0 -1
  152. package/packages/bundled-features/src/config/index.ts +6 -6
  153. package/packages/bundled-features/src/config/web/client-plugin.ts +1 -2
  154. package/packages/bundled-features/src/config/{constants.ts → web/constants.ts} +0 -1
  155. package/packages/bundled-features/src/config/web/i18n.ts +1 -1
  156. package/packages/bundled-features/src/config/web/index.ts +1 -3
  157. package/packages/bundled-features/src/config/write-helpers.ts +1 -1
  158. package/packages/bundled-features/src/custom-fields/feature.ts +8 -8
  159. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
  160. package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +1 -1
  161. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
  162. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +4 -4
  163. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
  164. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +1 -1
  165. package/packages/bundled-features/src/custom-fields/index.ts +8 -8
  166. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +1 -1
  167. package/packages/bundled-features/src/custom-fields/schemas.ts +1 -1
  168. package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +1 -2
  169. package/packages/bundled-features/src/custom-fields/{constants.ts → web/constants.ts} +18 -19
  170. package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +1 -2
  171. package/packages/bundled-features/src/custom-fields/web/i18n.ts +0 -1
  172. package/packages/bundled-features/src/custom-fields/web/index.ts +2 -3
  173. package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +1 -2
  174. package/packages/bundled-features/src/delivery/address-opt-out.ts +1 -1
  175. package/packages/bundled-features/src/delivery/attempt-log-retention.ts +24 -8
  176. package/packages/bundled-features/src/delivery/changes.json +6 -0
  177. package/packages/bundled-features/src/delivery/constants.ts +2 -2
  178. package/packages/bundled-features/src/delivery/i18n.ts +1 -1
  179. package/packages/bundled-features/src/delivery/unsubscribe-page.ts +1 -1
  180. package/packages/bundled-features/src/delivery/web/client-plugin.tsx +6 -7
  181. package/packages/bundled-features/src/delivery/web/delivery-channel-cell.tsx +0 -1
  182. package/packages/bundled-features/src/delivery/web/delivery-error-cell.tsx +0 -1
  183. package/packages/bundled-features/src/delivery/web/delivery-status-cell.tsx +1 -2
  184. package/packages/bundled-features/src/delivery/web/delivery-time-cell.tsx +0 -1
  185. package/packages/bundled-features/src/delivery/web/delivery-type-cell.tsx +0 -1
  186. package/packages/bundled-features/src/delivery/web/index.ts +0 -1
  187. package/packages/bundled-features/src/delivery/{public-names.ts → web/public-names.ts} +0 -1
  188. package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
  189. package/packages/bundled-features/src/document-ingest-foundation/events.ts +4 -0
  190. package/packages/bundled-features/src/document-ingest-foundation/executor.ts +2 -1
  191. package/packages/bundled-features/src/document-ingest-foundation/feature.ts +18 -4
  192. package/packages/bundled-features/src/document-ingest-foundation/index.ts +1 -0
  193. package/packages/bundled-features/src/feature-toggles/feature.ts +7 -7
  194. package/packages/bundled-features/src/feature-toggles/handlers/list.query.ts +1 -1
  195. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
  196. package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +3 -3
  197. package/packages/bundled-features/src/feature-toggles/index.ts +6 -6
  198. package/packages/bundled-features/src/feature-toggles/web/client-plugin.tsx +1 -2
  199. package/packages/bundled-features/src/feature-toggles/{constants.ts → web/constants.ts} +0 -1
  200. package/packages/bundled-features/src/feature-toggles/web/i18n.ts +0 -1
  201. package/packages/bundled-features/src/feature-toggles/web/index.ts +0 -1
  202. package/packages/bundled-features/src/files/changes.json +7 -0
  203. package/packages/bundled-features/src/folders/feature.ts +1 -1
  204. package/packages/bundled-features/src/folders/handlers/clear-folder.write.ts +1 -1
  205. package/packages/bundled-features/src/folders/handlers/delete-folder.write.ts +1 -1
  206. package/packages/bundled-features/src/folders/handlers/set-folder.write.ts +1 -1
  207. package/packages/bundled-features/src/folders/index.ts +8 -8
  208. package/packages/bundled-features/src/folders/web/client-plugin.tsx +1 -3
  209. package/packages/bundled-features/src/folders/{constants.ts → web/constants.ts} +0 -1
  210. package/packages/bundled-features/src/folders/web/folder-manager.tsx +1 -2
  211. package/packages/bundled-features/src/folders/web/folder-section.tsx +1 -2
  212. package/packages/bundled-features/src/folders/web/i18n.ts +0 -1
  213. package/packages/bundled-features/src/folders/web/index.ts +1 -2
  214. package/packages/bundled-features/src/folders/web/tree.ts +0 -1
  215. package/packages/bundled-features/src/form-draft/changes.json +7 -0
  216. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +25 -11
  217. package/packages/bundled-features/src/form-draft/decrypt-draft.ts +24 -0
  218. package/packages/bundled-features/src/form-draft/entity.ts +6 -9
  219. package/packages/bundled-features/src/form-draft/feature.ts +1 -1
  220. package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +26 -5
  221. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
  222. package/packages/bundled-features/src/form-draft/handlers/get.query.ts +6 -7
  223. package/packages/bundled-features/src/form-draft/handlers/list.query.ts +13 -7
  224. package/packages/bundled-features/src/form-draft/handlers/save.write.ts +5 -5
  225. package/packages/bundled-features/src/form-draft/index.ts +8 -8
  226. package/packages/bundled-features/src/form-draft/lookup.ts +10 -4
  227. package/packages/bundled-features/src/form-draft/schemas.ts +1 -1
  228. package/packages/bundled-features/src/form-draft/{constants.ts → web/constants.ts} +0 -1
  229. package/packages/bundled-features/src/form-draft-user-data/hooks.ts +2 -2
  230. package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
  231. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +11 -4
  232. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +10 -10
  233. package/packages/bundled-features/src/jobs/feature.ts +7 -7
  234. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  235. package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +1 -1
  236. package/packages/bundled-features/src/jobs/index.ts +1 -1
  237. package/packages/bundled-features/src/jobs/web/client-plugin.tsx +2 -3
  238. package/packages/bundled-features/src/jobs/{constants.ts → web/constants.ts} +0 -1
  239. package/packages/bundled-features/src/jobs/web/default-translations.ts +7 -0
  240. package/packages/bundled-features/src/jobs/web/i18n.ts +53 -7
  241. package/packages/bundled-features/src/jobs/web/index.ts +0 -1
  242. package/packages/bundled-features/src/ledger/changes.json +7 -0
  243. package/packages/bundled-features/src/ledger/entity.ts +1 -1
  244. package/packages/bundled-features/src/ledger/feature.ts +1 -1
  245. package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +2 -10
  246. package/packages/bundled-features/src/ledger/handlers/create-transaction.write.ts +1 -1
  247. package/packages/bundled-features/src/ledger/handlers/reports.query.ts +1 -1
  248. package/packages/bundled-features/src/ledger/handlers/reverse-transaction.write.ts +1 -1
  249. package/packages/bundled-features/src/ledger/index.ts +13 -13
  250. package/packages/bundled-features/src/ledger/recurring.ts +1 -1
  251. package/packages/bundled-features/src/ledger/reports.ts +1 -1
  252. package/packages/bundled-features/src/ledger/schemas.ts +1 -1
  253. package/packages/bundled-features/src/ledger/{constants.ts → web/constants.ts} +0 -1
  254. package/packages/bundled-features/src/ledger/web/index.ts +11 -12
  255. package/packages/bundled-features/src/legal-pages/README.md +1 -1
  256. package/packages/bundled-features/src/legal-pages/feature.ts +5 -5
  257. package/packages/bundled-features/src/legal-pages/index.ts +11 -11
  258. package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +1 -1
  259. package/packages/bundled-features/src/legal-pages/lib/validate-routes.ts +1 -1
  260. package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +1 -2
  261. package/packages/bundled-features/src/legal-pages/{constants.ts → web/constants.ts} +6 -8
  262. package/packages/bundled-features/src/legal-pages/web/index.ts +0 -1
  263. package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
  264. package/packages/bundled-features/src/mail-foundation/i18n.ts +3 -2
  265. package/packages/bundled-features/src/mail-transport-smtp/i18n.ts +1 -1
  266. package/packages/bundled-features/src/managed-pages/feature.ts +1 -1
  267. package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +1 -2
  268. package/packages/bundled-features/src/managed-pages/web/default-translations.ts +17 -0
  269. package/packages/bundled-features/src/managed-pages/web/i18n.ts +59 -16
  270. package/packages/bundled-features/src/managed-pages/web/index.ts +1 -2
  271. package/packages/bundled-features/src/metrics/compute.ts +1 -1
  272. package/packages/bundled-features/src/metrics/feature.ts +6 -6
  273. package/packages/bundled-features/src/metrics/index.ts +8 -8
  274. package/packages/bundled-features/src/metrics/types.ts +1 -1
  275. package/packages/bundled-features/src/metrics/validate.ts +1 -1
  276. package/packages/bundled-features/src/metrics/{constants.ts → web/constants.ts} +0 -1
  277. package/packages/bundled-features/src/metrics/window.ts +1 -1
  278. package/packages/bundled-features/src/notes-history/feature.ts +1 -1
  279. package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
  280. package/packages/bundled-features/src/notes-history/index.ts +8 -8
  281. package/packages/bundled-features/src/notes-history/web/client-plugin.tsx +1 -3
  282. package/packages/bundled-features/src/notes-history/{constants.ts → web/constants.ts} +0 -1
  283. package/packages/bundled-features/src/notes-history/web/i18n.ts +0 -1
  284. package/packages/bundled-features/src/notes-history/web/index.ts +2 -3
  285. package/packages/bundled-features/src/notes-history/web/notes-section.tsx +2 -3
  286. package/packages/bundled-features/src/personal-access-tokens/feature.ts +12 -10
  287. package/packages/bundled-features/src/personal-access-tokens/handlers/available-scopes.query.ts +1 -1
  288. package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +1 -1
  289. package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
  290. package/packages/bundled-features/src/personal-access-tokens/hash.ts +1 -1
  291. package/packages/bundled-features/src/personal-access-tokens/i18n.ts +2 -2
  292. package/packages/bundled-features/src/personal-access-tokens/index.ts +5 -5
  293. package/packages/bundled-features/src/personal-access-tokens/resolver.ts +1 -1
  294. package/packages/bundled-features/src/personal-access-tokens/schema/api-token.ts +1 -1
  295. package/packages/bundled-features/src/personal-access-tokens/screens.ts +2 -2
  296. package/packages/bundled-features/src/personal-access-tokens/{constants.ts → web/constants.ts} +0 -1
  297. package/packages/bundled-features/src/personal-access-tokens/{scopes.ts → web/scopes.ts} +0 -1
  298. package/packages/bundled-features/src/presets/changes.json +7 -0
  299. package/packages/bundled-features/src/presets/index.ts +0 -1
  300. package/packages/bundled-features/src/presets/security-baseline.ts +1 -1
  301. package/packages/bundled-features/src/renderer-foundation/types.ts +1 -1
  302. package/packages/bundled-features/src/secrets/feature.ts +1 -1
  303. package/packages/bundled-features/src/secrets/handlers/delete.write.ts +1 -1
  304. package/packages/bundled-features/src/secrets/handlers/list.query.ts +1 -1
  305. package/packages/bundled-features/src/secrets/handlers/set.write.ts +1 -1
  306. package/packages/bundled-features/src/secrets/i18n.ts +1 -1
  307. package/packages/bundled-features/src/secrets/index.ts +8 -8
  308. package/packages/bundled-features/src/secrets/{constants.ts → web/constants.ts} +0 -1
  309. package/packages/bundled-features/src/secrets/write-gate.ts +1 -1
  310. package/packages/bundled-features/src/sessions/feature.ts +7 -4
  311. package/packages/bundled-features/src/sessions/{i18n.ts → web/i18n.ts} +0 -1
  312. package/packages/bundled-features/src/shared/index.ts +6 -1
  313. package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +10 -2
  314. package/packages/bundled-features/src/shared/web/translate-or-raw.ts +0 -1
  315. package/packages/bundled-features/src/subscription-stripe/feature.ts +2 -0
  316. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -0
  317. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +1 -0
  318. package/packages/bundled-features/src/tags/changes.json +6 -0
  319. package/packages/bundled-features/src/tags/feature.ts +1 -1
  320. package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +11 -7
  321. package/packages/bundled-features/src/tags/handlers/create-tag.write.ts +1 -1
  322. package/packages/bundled-features/src/tags/handlers/delete-tag.write.ts +4 -1
  323. package/packages/bundled-features/src/tags/handlers/remove-tag.write.ts +1 -1
  324. package/packages/bundled-features/src/tags/handlers/update-tag.write.ts +1 -1
  325. package/packages/bundled-features/src/tags/index.ts +12 -12
  326. package/packages/bundled-features/src/tags/screens.ts +1 -1
  327. package/packages/bundled-features/src/tags/web/client-plugin.tsx +1 -3
  328. package/packages/bundled-features/src/tags/{constants.ts → web/constants.ts} +4 -1
  329. package/packages/bundled-features/src/tags/web/entity-tags.tsx +1 -2
  330. package/packages/bundled-features/src/tags/web/i18n.ts +2 -2
  331. package/packages/bundled-features/src/tags/web/index.ts +2 -3
  332. package/packages/bundled-features/src/tags/web/tag-chip.tsx +0 -1
  333. package/packages/bundled-features/src/tags/web/tag-filter.tsx +1 -2
  334. package/packages/bundled-features/src/tags/web/tag-manager.tsx +5 -4
  335. package/packages/bundled-features/src/tags/web/tag-picker.tsx +0 -1
  336. package/packages/bundled-features/src/tags/web/tag-section.tsx +1 -2
  337. package/packages/bundled-features/src/tags/web/tags-cell.tsx +1 -2
  338. package/packages/bundled-features/src/template-resolver/README.md +1 -1
  339. package/packages/bundled-features/src/template-resolver/api.ts +75 -28
  340. package/packages/bundled-features/src/template-resolver/changes.json +63 -0
  341. package/packages/bundled-features/src/template-resolver/compile-document-body.ts +62 -4
  342. package/packages/bundled-features/src/template-resolver/compile-steps-block.ts +2 -2
  343. package/packages/bundled-features/src/template-resolver/document-ops.ts +5 -5
  344. package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +39 -4
  345. package/packages/bundled-features/src/template-resolver/feature.ts +88 -38
  346. package/packages/bundled-features/src/template-resolver/handlers/by-slug.query.ts +6 -1
  347. package/packages/bundled-features/src/template-resolver/handlers/by-tenant.query.ts +6 -2
  348. package/packages/bundled-features/src/template-resolver/handlers/collection-item.query.ts +1 -1
  349. package/packages/bundled-features/src/template-resolver/handlers/collection-list.query.ts +1 -1
  350. package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +20 -7
  351. package/packages/bundled-features/src/template-resolver/handlers/collection-shared.ts +0 -6
  352. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +69 -60
  353. package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +7 -1
  354. package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +37 -14
  355. package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +8 -2
  356. package/packages/bundled-features/src/template-resolver/handlers/document-blocks.query.ts +2 -2
  357. package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +5 -2
  358. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +4 -2
  359. package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +37 -28
  360. package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +10 -5
  361. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +5 -2
  362. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +132 -70
  363. package/packages/bundled-features/src/template-resolver/handlers/patch-draft.write.ts +106 -97
  364. package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +62 -49
  365. package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +44 -29
  366. package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +52 -36
  367. package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +1 -1
  368. package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +127 -107
  369. package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +38 -0
  370. package/packages/bundled-features/src/template-resolver/handlers/send-test-status.query.ts +31 -0
  371. package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +106 -112
  372. package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +111 -104
  373. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +1 -1
  374. package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +6 -6
  375. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +2 -2
  376. package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +56 -28
  377. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +38 -29
  378. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +8 -2
  379. package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +4 -2
  380. package/packages/bundled-features/src/template-resolver/i18n.ts +25 -0
  381. package/packages/bundled-features/src/template-resolver/index.ts +68 -60
  382. package/packages/bundled-features/src/template-resolver/layout-appearance.ts +1 -1
  383. package/packages/bundled-features/src/template-resolver/layout-regions.ts +1 -1
  384. package/packages/bundled-features/src/template-resolver/seeding.ts +16 -7
  385. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +2 -2
  386. package/packages/bundled-features/src/template-resolver/table.ts +1 -1
  387. package/packages/bundled-features/src/template-resolver/template-bundle.ts +2 -2
  388. package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +82 -0
  389. package/packages/bundled-features/src/template-resolver/template-draft.ts +1 -1
  390. package/packages/bundled-features/src/template-resolver/template-edit-policy.ts +91 -0
  391. package/packages/bundled-features/src/template-resolver/template-meta.ts +2 -2
  392. package/packages/bundled-features/src/template-resolver/template-preview.ts +22 -12
  393. package/packages/bundled-features/src/template-resolver/template-usage.ts +1 -1
  394. package/packages/bundled-features/src/template-resolver/template-validation.ts +33 -12
  395. package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
  396. package/packages/bundled-features/src/template-resolver/user-content-table.ts +1 -1
  397. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +4 -5
  398. package/packages/bundled-features/src/template-resolver/{constants.ts → web/constants.ts} +10 -10
  399. package/packages/bundled-features/src/template-resolver/{document-blocks.ts → web/document-blocks.ts} +3 -2
  400. package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +31 -3
  401. package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +0 -1
  402. package/packages/bundled-features/src/template-resolver/web/document-images.ts +28 -0
  403. package/packages/bundled-features/src/template-resolver/{document-schema.ts → web/document-schema.ts} +5 -1
  404. package/packages/bundled-features/src/template-resolver/{document-steps.ts → web/document-steps.ts} +0 -1
  405. package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +48 -11
  406. package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +4 -3
  407. package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +1 -2
  408. package/packages/bundled-features/src/template-resolver/web/i18n.ts +8 -1
  409. package/packages/bundled-features/src/template-resolver/web/index.ts +13 -14
  410. package/packages/bundled-features/src/template-resolver/{mail-constants.ts → web/mail-constants.ts} +0 -1
  411. package/packages/bundled-features/src/template-resolver/{pdf-constants.ts → web/pdf-constants.ts} +0 -1
  412. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +0 -1
  413. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +5 -4
  414. package/packages/bundled-features/src/template-resolver/{placeholder-text.ts → web/placeholder-text.ts} +0 -1
  415. package/packages/bundled-features/src/template-resolver/{qualified-names.ts → web/qualified-names.ts} +2 -1
  416. package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +0 -1
  417. package/packages/bundled-features/src/tenant/handlers/add-member.write.ts +1 -1
  418. package/packages/bundled-features/src/tenant/index.ts +7 -7
  419. package/packages/bundled-features/src/tenant/invitation-table.ts +1 -1
  420. package/packages/bundled-features/src/tenant/invited-membership.ts +1 -1
  421. package/packages/bundled-features/src/tenant/last-tenant-admin.ts +1 -1
  422. package/packages/bundled-features/src/tenant/member-role-options.ts +1 -1
  423. package/packages/bundled-features/src/tenant/screens.ts +2 -2
  424. package/packages/bundled-features/src/tenant/web/client-plugin.tsx +1 -2
  425. package/packages/bundled-features/src/tenant/{constants.ts → web/constants.ts} +3 -6
  426. package/packages/bundled-features/src/tenant/web/index.ts +0 -1
  427. package/packages/bundled-features/src/tenant/web/member-roles-cell.tsx +0 -1
  428. package/packages/bundled-features/src/tenant/web/member-status-cell.tsx +0 -1
  429. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +2 -2
  430. package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
  431. package/packages/bundled-features/src/tier-engine/feature.ts +7 -7
  432. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +1 -1
  433. package/packages/bundled-features/src/tier-engine/index.ts +8 -8
  434. package/packages/bundled-features/src/tier-engine/web/client-plugin.tsx +2 -3
  435. package/packages/bundled-features/src/tier-engine/{constants.ts → web/constants.ts} +2 -3
  436. package/packages/bundled-features/src/tier-engine/{i18n.ts → web/i18n.ts} +0 -1
  437. package/packages/bundled-features/src/tier-engine/web/index.ts +0 -1
  438. package/packages/bundled-features/src/user/handlers/create.write.ts +1 -1
  439. package/packages/bundled-features/src/user/handlers/update-roles.ts +1 -1
  440. package/packages/bundled-features/src/user/handlers/update.write.ts +1 -1
  441. package/packages/bundled-features/src/user/index.ts +1 -1
  442. package/packages/bundled-features/src/user/invited-global-roles.ts +1 -1
  443. package/packages/bundled-features/src/user/schema/user.ts +1 -1
  444. package/packages/bundled-features/src/user/{constants.ts → web/constants.ts} +0 -1
  445. package/packages/bundled-features/src/user-data-rights/changes.json +14 -0
  446. package/packages/bundled-features/src/user-data-rights/env.ts +1 -1
  447. package/packages/bundled-features/src/user-data-rights/feature.ts +9 -9
  448. package/packages/bundled-features/src/user-data-rights/index.ts +1 -1
  449. package/packages/bundled-features/src/user-data-rights/lib/resolve-tenant-model.ts +1 -1
  450. package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +1 -1
  451. package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +2 -3
  452. package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +0 -1
  453. package/packages/bundled-features/src/user-data-rights/{constants.ts → web/constants.ts} +11 -12
  454. package/packages/bundled-features/src/user-data-rights/web/default-translations.ts +91 -0
  455. package/packages/bundled-features/src/user-data-rights/web/i18n.ts +41 -91
  456. package/packages/bundled-features/src/user-data-rights/web/index.ts +1 -2
  457. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +1 -2
  458. package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +0 -1
  459. package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +0 -1
  460. package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +10 -1
  461. package/packages/bundled-features/src/user-profile/feature.ts +2 -2
  462. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +1 -1
  463. package/packages/bundled-features/src/user-profile/index.ts +3 -2
  464. package/packages/bundled-features/src/user-profile/web/client-plugin.ts +2 -3
  465. package/packages/bundled-features/src/user-profile/{constants.ts → web/constants.ts} +0 -1
  466. package/packages/bundled-features/src/user-profile/{i18n.ts → web/i18n.ts} +0 -1
  467. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -3
  468. package/packages/bundled-features/src/waitlist/changes.json +6 -0
  469. package/packages/bundled-features/src/waitlist/entity.ts +1 -1
  470. package/packages/bundled-features/src/waitlist/feature.ts +5 -5
  471. package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +3 -3
  472. package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +5 -1
  473. package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
  474. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +3 -3
  475. package/packages/bundled-features/src/waitlist/import.ts +1 -1
  476. package/packages/bundled-features/src/waitlist/index.ts +11 -11
  477. package/packages/bundled-features/src/waitlist/legacy-migrate.ts +1 -1
  478. package/packages/bundled-features/src/waitlist/link-on-signup.ts +1 -1
  479. package/packages/bundled-features/src/waitlist/mail.ts +7 -6
  480. package/packages/bundled-features/src/waitlist/payloads.ts +1 -1
  481. package/packages/bundled-features/src/waitlist/screens.ts +1 -1
  482. package/packages/bundled-features/src/waitlist/{constants.ts → web/constants.ts} +0 -1
  483. package/samples/apps/styleguide/src/features/content/web.tsx +0 -1
  484. package/samples/apps/ui-walkthrough/src/app/client.tsx +2 -0
  485. package/samples/apps/ui-walkthrough/src/app/help-action.tsx +37 -0
  486. package/samples/apps/ui-walkthrough/src/app/seed-rental.ts +17 -1
  487. package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +13 -0
  488. package/samples/apps/ui-walkthrough/src/app/server.ts +2 -1
  489. package/samples/apps/ui-walkthrough/src/app/shell.tsx +6 -0
  490. package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +20 -0
  491. package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +35 -7
  492. package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +6 -0
  493. package/samples/apps/ui-walkthrough/src/features/rental/entities.ts +27 -0
  494. package/samples/apps/ui-walkthrough/src/features/rental/feature.ts +120 -2
  495. package/samples/apps/ui-walkthrough/src/features/rental/i18n.ts +94 -0
  496. package/samples/apps/ui-walkthrough/src/features/rental/invoice-receive.ts +19 -0
  497. package/samples/apps/ui-walkthrough/src/features/rental/lease-support.ts +32 -1
  498. package/samples/apps/ui-walkthrough/src/features/rental/qualified-names.ts +5 -0
  499. package/samples/apps/ui-walkthrough/src/features/rental/receipts-upload.ts +70 -0
  500. package/samples/apps/ui-walkthrough/src/features/rental/screens.ts +156 -0
  501. package/samples/apps/ui-walkthrough/src/features/subscription-plans/feature.ts +103 -0
  502. package/samples/apps/ui-walkthrough/src/features/subscription-plans/i18n.ts +36 -0
  503. package/samples/apps/ui-walkthrough/src/features/subscription-plans/index.ts +2 -0
  504. package/samples/apps/ui-walkthrough/src/features/subscription-plans/seed.ts +21 -0
  505. package/samples/apps/ui-walkthrough/src/features/tasks/i18n.ts +8 -0
  506. package/samples/apps/ui-walkthrough/src/run-config.ts +43 -3
  507. package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +1 -1
  508. package/samples/apps/use-all-bundled/feature-manifest.json +5 -5
  509. package/samples/apps/use-all-bundled/scripts/check-coverage.ts +1 -1
  510. package/samples/apps/use-all-bundled/src/app/dev-app-options.ts +3 -1
  511. package/samples/recipes/compose-stacks/README.md +1 -2
  512. package/samples/recipes/delivery-notifications/README.md +1 -1
  513. package/samples/recipes/embedded/README.md +8 -0
  514. package/samples/recipes/embedded/src/feature.ts +8 -0
  515. package/samples/recipes/embedded/src/handlers/invoice-receive.write.ts +37 -0
  516. package/samples/recipes/embedded/src/screens/invoice-receive.screen.ts +56 -0
  517. package/samples/recipes/legal-pages/README.md +2 -2
  518. package/samples/recipes/legal-pages/src/feature.ts +1 -1
  519. package/samples/recipes/reference-data/src/feature.ts +3 -7
  520. package/packages/bundled-features/src/admin-shell/i18n.ts +0 -54
  521. package/packages/bundled-features/src/audit/i18n.ts +0 -25
  522. package/packages/bundled-features/src/auth-mfa/web/qrcode-browser.d.ts +0 -11
  523. package/packages/bundled-features/src/jobs/i18n.ts +0 -55
  524. package/packages/bundled-features/src/managed-pages/i18n.ts +0 -63
  525. package/packages/bundled-features/src/presets/dsgvo-self-service.ts +0 -35
  526. package/packages/bundled-features/src/user-data-rights/i18n.ts +0 -44
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // Default translation bundle for the tags UI. tagsClient() hangs it into the
3
2
  // LocaleProvider as a fallback bundle — apps override individual keys via
4
3
  // tagsClient({ translations }). Keys follow `tags.<area>.<slug>`.
@@ -25,7 +24,8 @@ export const defaultTranslations: TranslationsByLocale = {
25
24
  "tags.manage.toggle": "Toggle label",
26
25
  "tags.manage.usage": "{count}×",
27
26
  "tags.manage.deleteConfirmTitle": "Delete label “{name}”?",
28
- "tags.manage.deleteConfirmDesc": "Removes it from {count} objects. This can't be undone.",
27
+ "tags.manage.deleteConfirmDesc.one": "Removes it from 1 object. This can't be undone.",
28
+ "tags.manage.deleteConfirmDesc.other": "Removes it from {count} objects. This can't be undone.",
29
29
  "tags.picker.title": "Tags",
30
30
  "tags.picker.done": "Done",
31
31
  "tags.filter.label": "Filter by tag",
@@ -1,4 +1,4 @@
1
- // @runtime client
1
+ export { tagsClient } from "./client-plugin.tsx";
2
2
  export {
3
3
  TAGS_COLUMN_RENDERER_NAME,
4
4
  TAGS_EDIT_SCREEN_ID,
@@ -7,8 +7,7 @@ export {
7
7
  TAGS_SECTION_EXTENSION_NAME,
8
8
  TagsHandlers,
9
9
  TagsQueries,
10
- } from "../constants.ts";
11
- export { tagsClient } from "./client-plugin.tsx";
10
+ } from "./constants.ts";
12
11
  export { EntityTags } from "./entity-tags.tsx";
13
12
  export { contrastText, TagChip } from "./tag-chip.tsx";
14
13
  export { TagFilter } from "./tag-filter.tsx";
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // TagChip — read-only colored label pill, GitLab-labels style. The tag's `color`
3
2
  // (a hex like "#22cc88") drives the background; the text color is chosen for
4
3
  // contrast (YIQ luminance) so light and dark labels both stay legible. A tag
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // TagFilter — a drop-in tag filter for ANY entityList toolbar. Register it as
3
2
  // the list's header slot (`screen.slots.header`); the renderer passes it the
4
3
  // list's screenId, and picking tags resolves the matching row ids and narrows
@@ -20,7 +19,7 @@ import {
20
19
  useTranslation,
21
20
  } from "@cosmicdrift/kumiko-renderer";
22
21
  import { type ReactNode, useState } from "react";
23
- import { TagsQueries } from "../constants.ts";
22
+ import { TagsQueries } from "./constants.ts";
24
23
  import { TagChip } from "./tag-chip.tsx";
25
24
  import { TagPicker } from "./tag-picker.tsx";
26
25
 
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // TagManager — the shared tag-catalog UI, GitLab-labels style. Built ONCE,
3
2
  // mounted twice:
4
3
  // - standalone: <TagManager /> → the Tags admin screen
@@ -16,8 +15,9 @@ import {
16
15
  useQuery,
17
16
  useTranslation,
18
17
  } from "@cosmicdrift/kumiko-renderer";
18
+ import { pluralKey } from "@cosmicdrift/kumiko-renderer/headless";
19
19
  import { type ReactNode, useState } from "react";
20
- import { TagsHandlers, TagsQueries } from "../constants.ts";
20
+ import { TagsHandlers, TagsQueries } from "./constants.ts";
21
21
  import { TagChip } from "./tag-chip.tsx";
22
22
 
23
23
  type TagRow = {
@@ -139,6 +139,7 @@ export function TagManager({
139
139
  const usage = new Map<string, number>();
140
140
  for (const a of assignments.data?.rows ?? []) usage.set(a.tagId, (usage.get(a.tagId) ?? 0) + 1);
141
141
 
142
+ const deletingUsage = usage.get(deleting?.id ?? "") ?? 0;
142
143
  const selecting = selection !== undefined;
143
144
  const allTags = catalog.data?.rows ?? [];
144
145
  const tags = selecting
@@ -378,8 +379,8 @@ export function TagManager({
378
379
  if (!open) setDeleting(null);
379
380
  }}
380
381
  title={t("tags.manage.deleteConfirmTitle", { name: deleting?.name ?? "" })}
381
- description={t("tags.manage.deleteConfirmDesc", {
382
- count: usage.get(deleting?.id ?? "") ?? 0,
382
+ description={t(pluralKey("tags.manage.deleteConfirmDesc", deletingUsage), {
383
+ count: deletingUsage,
383
384
  })}
384
385
  confirmLabel={t("tags.manage.delete")}
385
386
  variant="danger"
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // TagPicker — the shared "manage + pick labels" modal. Wraps TagManager in
3
2
  // select-mode inside a Dialog: the user can create/recolor/delete labels (those
4
3
  // hit the catalog immediately) and toggle which ones apply (buffered). On
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // TagSection — drop-in tag editor for ANY entity, GitLab-labels style. Shows the
3
2
  // entity's tags as colored chips plus an "Edit tags" button that opens the
4
3
  // shared TagPicker modal (pick + manage). Applying the picker diffs the
@@ -19,7 +18,7 @@ import {
19
18
  useTranslation,
20
19
  } from "@cosmicdrift/kumiko-renderer";
21
20
  import { type ReactNode, useState } from "react";
22
- import { TagsHandlers, TagsQueries } from "../constants.ts";
21
+ import { TagsHandlers, TagsQueries } from "./constants.ts";
23
22
  import { TagChip } from "./tag-chip.tsx";
24
23
  import { TagPicker } from "./tag-picker.tsx";
25
24
 
@@ -1,4 +1,3 @@
1
- // @runtime client
2
1
  // TagsCell — a reusable columnRenderer that shows an entity's tags as colored
3
2
  // chips inline in a list row. Drop it into ANY entityList by declaring a labeled
4
3
  // virtual column (no host-schema change), after mounting tagsClient():
@@ -17,7 +16,7 @@
17
16
 
18
17
  import { type ColumnRendererProps, useQuery } from "@cosmicdrift/kumiko-renderer";
19
18
  import type { ReactNode } from "react";
20
- import { TagsQueries } from "../constants.ts";
19
+ import { TagsQueries } from "./constants.ts";
21
20
  import { TagChip } from "./tag-chip.tsx";
22
21
 
23
22
  type TagRow = { readonly id: string; readonly name: string; readonly color?: string | null };
@@ -23,7 +23,7 @@ const features = [
23
23
  const app = createKumikoApp({
24
24
  features,
25
25
  extraContext: ({ db }) => ({
26
- templateResolver: createTemplateResolverApi(db),
26
+ templateResolver: createTemplateResolverApi(db, registry),
27
27
  }),
28
28
  });
29
29
  ```
@@ -4,12 +4,10 @@
4
4
 
5
5
  import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
6
6
  import { selectMany } from "@cosmicdrift/kumiko-framework/db";
7
- import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
7
+ import type { Registry, SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
8
8
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
9
9
  import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
10
10
  import type { FileAttachmentRef } from "../renderer-foundation/index.ts";
11
- import type { ContentFormat, TemplateKind } from "./constants.ts";
12
- import { FALLBACK_LOCALE, SYSTEM_TENANT_ID } from "./constants.ts";
13
11
  import {
14
12
  fileAttachmentsOf,
15
13
  imageLinksOf,
@@ -18,8 +16,16 @@ import {
18
16
  type TemplatePdfAttachmentLink,
19
17
  } from "./linked-resources.ts";
20
18
  import { type TemplateResourceRow, templateResourcesTable } from "./table.ts";
21
- import { activeVersionOf, servedVersionOf } from "./template-draft.ts";
19
+ import {
20
+ deliveryPolicyOf,
21
+ isRowServed,
22
+ isServedByCollectionOnly,
23
+ type TemplateDeliveryPolicy,
24
+ } from "./template-delivery-policy.ts";
25
+ import { activeVersionOf, isSystemAdmin, servedVersionOf } from "./template-draft.ts";
22
26
  import { parseTemplateMeta, type TemplateMeta } from "./template-meta.ts";
27
+ import type { ContentFormat, TemplateKind } from "./web/constants.ts";
28
+ import { FALLBACK_LOCALE, SYSTEM_TENANT_ID } from "./web/constants.ts";
23
29
 
24
30
  // Public TemplateResource: what consumers see. Hides DB-internal columns
25
31
  // (insertedById, internal id-type) but keeps the fields needed for rendering.
@@ -68,15 +74,11 @@ export type ResolveLayoutRequest = {
68
74
 
69
75
  export type TemplateResolverApi = {
70
76
  /**
71
- * Findet ein konkretes Template by (tenantId, slug, kind, locale).
72
- * Kein Locale-Fallback, keine Override-Hierarchie — Raw-Lookup für
73
- * Admin-UI-Edits und Konformitäts-Tests. Für Render-Aufrufe immer
74
- * `resolveTemplate` nutzen.
77
+ * Raw lookup by (tenantId, slug, kind, locale): no locale fallback, no override
78
+ * hierarchy. Render paths must use `resolveTemplate`. `scope: "system"` forces
79
+ * SYSTEM_TENANT_ID and ignores the passed tenantId.
75
80
  *
76
- * `scope: "system"` zwingt Lookup gegen SYSTEM_TENANT_ID (ignoriert
77
- * den übergebenen tenantId). Default = Lookup gegen den übergebenen
78
- * tenantId direkt (Caller wählt entweder Tenant oder System per
79
- * tenantId).
81
+ * Tenant rows of a template that is not tenantEditable are not returned.
80
82
  */
81
83
  readonly findExact: (args: {
82
84
  readonly tenantId: TenantId;
@@ -87,18 +89,10 @@ export type TemplateResolverApi = {
87
89
  }) => Promise<TemplateResource | null>;
88
90
 
89
91
  /**
90
- * Resolver mit 4-Stufen-Fallback (siehe template-resolver.md):
91
- * 1. tenant + requested-locale
92
- * 2. system + requested-locale
93
- * 3. tenant + FALLBACK_LOCALE
94
- * 4. system + FALLBACK_LOCALE
95
- * Resource-Substitution wird hier NICHT ausgeführt — das macht der
96
- * Caller (renderer-foundation-Plugin) je nach kind-passendem Modus
97
- * (inline-base64 vs. signed-url). API liefert TemplateResource pur.
98
- *
99
- * **Caller-Invariante:** `tenantId` MUSS vom Server kommen (typisch
100
- * `ctx.user.tenantId`). Niemals direkt aus User-Input — sonst kann
101
- * User cross-tenant Templates abfragen (Tenant-Isolation gebrochen).
92
+ * Four-step fallback: tenant+locale, system+locale, tenant+FALLBACK_LOCALE,
93
+ * system+FALLBACK_LOCALE (see template-resolver.md).
94
+ * Tenant rows count only for templates unlocked for tenants (tenantEditable,
95
+ * or a collection with access); locked overrides are skipped but stay stored.
102
96
  */
103
97
  readonly resolveTemplate: (args: ResolveRequest) => Promise<TemplateResource>;
104
98
 
@@ -114,12 +108,18 @@ export type TemplateResolverApi = {
114
108
  readonly resolveLayout: (args: ResolveLayoutRequest) => Promise<TemplateResource>;
115
109
  };
116
110
 
117
- export function createTemplateResolverApi(db: DbRunner | TenantDb): TemplateResolverApi {
111
+ export function createTemplateResolverApi(
112
+ db: DbRunner | TenantDb,
113
+ registry: Registry,
114
+ ): TemplateResolverApi {
115
+ const deliveryPolicy = deliveryPolicyOf(registry);
116
+ // A collection entry must not shadow a system template of the same slug that the app did not unlock.
117
+ const isServed = (row: TemplateResourceRow) => isRowDelivered(db, deliveryPolicy, row);
118
118
  return {
119
119
  findExact: async ({ tenantId, slug, kind, locale, scope }) => {
120
120
  const effectiveTenantId = scope === "system" ? SYSTEM_TENANT_ID : tenantId;
121
121
  const row = await fetchTemplate(db, effectiveTenantId, slug, kind, locale);
122
- return row ? toPublic(row) : null;
122
+ return row && (await isServed(row)) ? toPublic(row) : null;
123
123
  },
124
124
 
125
125
  resolveTemplate: async ({ tenantId, slug, kind, locale }) => {
@@ -135,14 +135,16 @@ export function createTemplateResolverApi(db: DbRunner | TenantDb): TemplateReso
135
135
  : []),
136
136
  ];
137
137
  for (const c of candidates) {
138
+ if (!isRowServed(deliveryPolicy, { tenantId: c.tid, kind, slug })) continue;
138
139
  const row = await fetchTemplate(db, c.tid, slug, kind, c.loc);
139
- if (row && row.status === "active") return toPublic(row);
140
+ if (row && row.status === "active" && (await isServed(row))) return toPublic(row);
140
141
  }
141
142
  throw new TemplateNotFoundError({ slug, kind, locale });
142
143
  },
143
144
 
144
145
  resolveLayout: async ({ tenantId, slug, kind, locale }) => {
145
146
  for (const tid of [tenantId, SYSTEM_TENANT_ID]) {
147
+ if (!isRowServed(deliveryPolicy, { tenantId: tid, kind, slug })) continue;
146
148
  const rows = await selectMany<TemplateResourceRow>(db, templateResourcesTable, {
147
149
  tenantId: tid,
148
150
  slug,
@@ -156,6 +158,51 @@ export function createTemplateResolverApi(db: DbRunner | TenantDb): TemplateReso
156
158
  };
157
159
  }
158
160
 
161
+ // The one delivery rule for a stored row: unlocked, and a collection entry never shadows a system
162
+ // template of the same slug that the app did not unlock.
163
+ export async function isRowDelivered(
164
+ db: DbRunner | TenantDb,
165
+ policy: TemplateDeliveryPolicy,
166
+ row: TemplateResourceRow,
167
+ ): Promise<boolean> {
168
+ if (!isRowServed(policy, row)) return false;
169
+ if (row.tenantId === SYSTEM_TENANT_ID || !isServedByCollectionOnly(policy, row)) return true;
170
+ return !(await systemTemplateExists(db, row.kind, row.slug));
171
+ }
172
+
173
+ export async function isRowDeliveredTo(
174
+ db: DbRunner | TenantDb,
175
+ policy: TemplateDeliveryPolicy,
176
+ user: { readonly roles: readonly string[] },
177
+ row: TemplateResourceRow,
178
+ ): Promise<boolean> {
179
+ return isSystemAdmin(user) || (await isRowDelivered(db, policy, row));
180
+ }
181
+
182
+ export async function filterDeliveredTo(
183
+ db: DbRunner | TenantDb,
184
+ policy: TemplateDeliveryPolicy,
185
+ user: { readonly roles: readonly string[] },
186
+ rows: readonly TemplateResourceRow[],
187
+ ): Promise<TemplateResourceRow[]> {
188
+ const flags = await Promise.all(rows.map((row) => isRowDeliveredTo(db, policy, user, row)));
189
+ return rows.filter((_, index) => flags[index]);
190
+ }
191
+
192
+ export async function systemTemplateExists(
193
+ db: DbRunner | TenantDb,
194
+ kind: string,
195
+ slug: string,
196
+ ): Promise<boolean> {
197
+ const rows = await selectMany(
198
+ db,
199
+ templateResourcesTable,
200
+ { tenantId: SYSTEM_TENANT_ID, slug, kind },
201
+ { limit: 1 },
202
+ );
203
+ return rows.length > 0;
204
+ }
205
+
159
206
  function pickActiveLayoutRow(
160
207
  rows: readonly TemplateResourceRow[],
161
208
  locale: string,
@@ -268,7 +315,7 @@ export function requireTemplateResolver(
268
315
  throw new InternalError({
269
316
  message:
270
317
  `[${callerName}] ctx.templateResolver missing — App-Bootstrap muss ` +
271
- `extraContext: { templateResolver: createTemplateResolverApi(db) } setzen ` +
318
+ `extraContext: { templateResolver: createTemplateResolverApi(db, registry) } setzen ` +
272
319
  `(siehe template-resolver/README.md).`,
273
320
  });
274
321
  }
@@ -1,4 +1,67 @@
1
1
  [
2
+ {
3
+ "version": "0.379.0",
4
+ "type": "improvement",
5
+ "title": "Titles for the template-resolver write handlers",
6
+ "detail": "Every agent-callable template-resolver write handler now has a title in de, en, es, fr and it, so the AI assistant names actions such as \"Edit template draft\" instead of \"template-resolver:write:patch-draft\". The handlers of mounted content collections (`<id>-set`) stay untitled because their names depend on the app. The empty-field hint of the placeholder line editor now ends in an ellipsis inside the field instead of running past it."
7
+ },
8
+ {
9
+ "version": "0.379.0",
10
+ "type": "breaking",
11
+ "title": "Overrides of locked templates are no longer served",
12
+ "detail": "A tenant override of a mail, PDF or layout template is delivered (mail send, PDF render, linked PDFs, layouts, previews, export bundles, document blocks, overview, text blocks) only while the template is listed in tenantEditable. A locked override stays stored and has no effect; listing the template makes it effective again, nothing is deleted. A SystemAdmin sees locked overrides in the overview (`lockedOverride`, `lockedOverrideI18nKey`) and can reset or archive them with the existing handlers; the template-resolver queries do not show them to a TenantAdmin; the audit log keeps the history. Tenant rows of a collection mounted with `access` are served, except for a slug that has a system template outside tenantEditable: that system template wins, and the collection `-set` handler refuses such a slug for a non-SystemAdmin. createTemplateResolverApi(db) became createTemplateResolverApi(db, registry) because the delivery policy is read from the mounted feature; without the mounted feature no tenant override is served.",
13
+ "migration": "Overrides of templates that are not tenantEditable are no longer served; enable the template via tenantEditable to keep using them. Pass the registry as second argument to createTemplateResolverApi (ctx.registry in handlers, stack.registry in tests). Tenant rows without a system counterpart (for example text blocks seeded directly into a tenant) are hidden as well unless listed in tenantEditable."
14
+ },
15
+ {
16
+ "version": "0.379.0",
17
+ "type": "improvement",
18
+ "title": "Overview label for locked template overrides",
19
+ "detail": "Adds templateResolver.overview.lockedOverride."
20
+ },
21
+ {
22
+ "version": "0.379.0",
23
+ "type": "improvement",
24
+ "title": "Mail rendering follows the template delivery policy",
25
+ "detail": "Resolves templates through createTemplateResolverApi(db, registry); overrides of locked templates fall back to the system version."
26
+ },
27
+ {
28
+ "version": "0.379.0",
29
+ "type": "improvement",
30
+ "title": "PDF rendering follows the template delivery policy",
31
+ "detail": "Resolves templates through createTemplateResolverApi(db, registry); overrides of locked templates fall back to the system version."
32
+ },
33
+ {
34
+ "version": "0.379.0",
35
+ "type": "improvement",
36
+ "title": "Samples follow the createTemplateResolverApi(db, registry) signature",
37
+ "detail": "Recipes and sample apps build the template resolver API with the registry as second argument."
38
+ },
39
+ {
40
+ "version": "0.379.0",
41
+ "type": "improvement",
42
+ "title": "seedSystemTemplate accepts titleI18nKey",
43
+ "detail": "A seeded template can store an i18n key as its title (titleI18nKey, exclusive with title). The overview returns the key; the document-designer overview, mail editor and PDF link modal translate it and fall back to the raw text. Templates without a key behave as before; no migration."
44
+ },
45
+ {
46
+ "version": "0.378.0",
47
+ "type": "improvement",
48
+ "title": "Document PDFs embed the images of a record",
49
+ "detail": "A template variable of type images (templateVariable.images()) holds FileRef ids of the record. The new Images block (div[data-images], PDF templates only, rejected for mail kinds) renders them as data-URI images in the real PDF render. Only files of the rendering tenant are read, only png, jpeg, gif and webp, at most 24 images and 20 MB per document; missing, foreign, oversized or non-image ids are dropped silently. Previews and draft PDFs never read files and show one placeholder box per block. An images variable never renders as inline text. i18n keys template-resolver.documentEditor.images, template-resolver.documentEditor.imagesVariable and templateResolver.errors.document.imagesBlockPdfOnly ship in de, es, fr and it.",
50
+ "migration": "Declare a template variable with type `images`, place the Images block, and pass FileRef ids of the record in `variables`. Images resolve only in the real PDF render, previews show placeholders. The ids must come from the record on the server, never from client input: a resolved id is read for the whole tenant without an entity access check."
51
+ },
52
+ {
53
+ "version": "0.378.0",
54
+ "type": "breaking",
55
+ "title": "Template editing is SystemAdmin-only unless the app unlocks it",
56
+ "detail": "Every tenant write handler, the overview and resource upload accept SystemAdmin only, plus the roles of the tenantEditable entries. A write on a template that is not unlocked fails with templateNotTenantEditable. The test send follows the same rule. For a non-SystemAdmin an unknown template id answers 403 like a locked one, so the response does not reveal whether it exists. Collections without an access rule default to SystemAdmin.",
57
+ "migration": "Where TenantAdmins must keep editing a template, pass tenantEditable to createTemplateResolverFeature, for example tenantEditable: [{ kind: \"mail\", slug: \"billing:notify:invoice-issued\", access: { roles: [\"TenantAdmin\"] } }]. Give template collections an explicit access rule if tenants should manage them."
58
+ },
59
+ {
60
+ "version": "0.378.0",
61
+ "type": "improvement",
62
+ "title": "send-test-status query for the template test mail",
63
+ "detail": "template-resolver:query:send-test-status answers whether a test mail can be sent (canSend) and, if not, why (no_channel or no_address), so clients can disable the action instead of failing on send."
64
+ },
2
65
  {
3
66
  "version": "0.377.0",
4
67
  "type": "improvement",
@@ -11,7 +11,7 @@ import {
11
11
  isDocumentPlaceholderHref,
12
12
  isDocumentReferenceName,
13
13
  isDocumentResourceKey,
14
- } from "./document-schema.ts";
14
+ } from "./web/document-schema.ts";
15
15
 
16
16
  // Private-use characters mark where a Handlebars expression goes. Author text
17
17
  // is scrubbed of them first and every `{`/`}` is entity-encoded before the
@@ -78,10 +78,15 @@ function valueExpression(
78
78
  return `{{${path}}}`;
79
79
  }
80
80
 
81
+ // Never interpolated as text: its value is a list of file ids.
82
+ function isImagesVariable(def: TemplateVariableDef | undefined): boolean {
83
+ return def?.type === "images";
84
+ }
85
+
81
86
  function rewriteVariable(element: ScanElement, state: CompileState): void {
82
87
  const name = element.getAttribute("data-variable") ?? "";
83
88
  const replacement = element.ownerDocument.createTextNode(
84
- isDocumentReferenceName(name)
89
+ isDocumentReferenceName(name) && !isImagesVariable(pathAndDef(name, element, state)[1])
85
90
  ? marker(
86
91
  state,
87
92
  valueExpression(
@@ -131,6 +136,8 @@ function wrapVariableOnlyBlock(block: ScanElement, state: CompileState): void {
131
136
  // skip: a line with static content or several placeholders stays as it is
132
137
  if (name === null) return;
133
138
  const [path, def] = pathAndDef(name, block, state);
139
+ // skip: images blocks are rewritten by rewriteImagesBlock
140
+ if (isImagesVariable(def)) return;
134
141
  const host =
135
142
  def?.type === "richText" && block.tagName === "P" ? richTextBlockHost(block, state) : block;
136
143
  const document = host.ownerDocument;
@@ -158,8 +165,8 @@ function rewriteLink(element: ScanElement, state: CompileState): void {
158
165
  if (isDocumentPlaceholderHref(href)) {
159
166
  const name = href.slice(2, -2);
160
167
  if (isDocumentReferenceName(name)) {
161
- const [path] = pathAndDef(name, element, state);
162
- element.setAttribute("href", marker(state, `{{link ${path}}}`));
168
+ const [path, def] = pathAndDef(name, element, state);
169
+ element.setAttribute("href", isImagesVariable(def) ? "#" : marker(state, `{{link ${path}}}`));
163
170
  } else {
164
171
  element.setAttribute("href", "#");
165
172
  }
@@ -225,8 +232,14 @@ export type CompileDocumentBodyOptions = {
225
232
  readonly headingAsInlineStyle?: boolean;
226
233
  // Bar color of reached stages in a steps block; ignored unless it is a #rrggbb hex.
227
234
  readonly stepsColor?: string;
235
+ // Data URIs of the record's images for the images block bound to `variable`.
236
+ readonly recordImagesSrc?: (variable: string) => readonly string[];
237
+ // Previews and drafts never read the record's files; each images block shows one neutral box.
238
+ readonly recordImagesPlaceholder?: boolean;
228
239
  };
229
240
 
241
+ const IMAGES_PLACEHOLDER_STYLE = "height:96px;margin:8px 0;border-radius:4px;background:#e5e7eb";
242
+
230
243
  const MAIL_BUTTON_STYLE =
231
244
  "display:inline-block;padding:10px 20px;border-radius:6px;background-color:#1f5468;color:#ffffff;font-weight:600;text-decoration:none";
232
245
 
@@ -258,6 +271,48 @@ export function collectDocumentResourceKeys(html: string): readonly string[] {
258
271
  });
259
272
  }
260
273
 
274
+ // Unique valid names of the images blocks in document order.
275
+ export function collectDocumentImageVariables(html: string): readonly string[] {
276
+ return withParsedDocument(html, (root) => {
277
+ const names = new Set<string>();
278
+ for (const element of Array.from(root.querySelectorAll("div[data-images]"))) {
279
+ const name = element.getAttribute("data-images") ?? "";
280
+ if (isDocumentReferenceName(name)) names.add(name);
281
+ }
282
+ return [...names];
283
+ });
284
+ }
285
+
286
+ function rewriteImagesBlock(
287
+ element: ScanElement,
288
+ state: CompileState,
289
+ options: CompileDocumentBodyOptions,
290
+ ): void {
291
+ const name = element.getAttribute("data-images") ?? "";
292
+ const declared =
293
+ isDocumentReferenceName(name) && isImagesVariable(ownVariable(state.variables, name));
294
+ if (!declared) {
295
+ element.remove();
296
+ // skip: an images block bound to anything but a declared images variable renders nothing
297
+ return;
298
+ }
299
+ const document = element.ownerDocument;
300
+ element.removeAttribute("data-images");
301
+ if (options.recordImagesPlaceholder === true) {
302
+ element.setAttribute("class", "kumiko-doc-image-placeholder");
303
+ element.setAttribute("style", IMAGES_PLACEHOLDER_STYLE);
304
+ // skip: preview and drafts never resolve record images
305
+ return;
306
+ }
307
+ element.setAttribute("class", "kumiko-doc-images");
308
+ for (const src of options.recordImagesSrc?.(name) ?? []) {
309
+ const image = document.createElement("img");
310
+ image.setAttribute("src", src);
311
+ image.setAttribute("alt", "");
312
+ element.append(image);
313
+ }
314
+ }
315
+
261
316
  export function toResourceDataUri(contentType: string, bytes: Uint8Array): string {
262
317
  if (!isResourceImageContentType(contentType)) {
263
318
  throw new Error(`resource content type "${contentType}" cannot be embedded as an image`);
@@ -374,6 +429,9 @@ export function compileDocumentBodyToHandlebars(
374
429
  color: options.stepsColor,
375
430
  });
376
431
  }
432
+ for (const element of Array.from(root.querySelectorAll("div[data-images]"))) {
433
+ rewriteImagesBlock(element, state, options);
434
+ }
377
435
  for (const block of Array.from(root.querySelectorAll(`[${DOCUMENT_CONDITION_ATTRIBUTE}]`))) {
378
436
  rewriteConditionalBlock(block, state);
379
437
  }
@@ -1,7 +1,7 @@
1
1
  import type { Element as ScanElement } from "happy-dom";
2
- import { isDocumentReferenceName } from "./document-schema.ts";
3
- import { readDocumentSteps } from "./document-steps.ts";
4
2
  import { isHexColor } from "./layout-appearance.ts";
3
+ import { isDocumentReferenceName } from "./web/document-schema.ts";
4
+ import { readDocumentSteps } from "./web/document-steps.ts";
5
5
 
6
6
  export const DEFAULT_STEPS_COLOR = "#1f5468";
7
7
  const TODO_BAR_COLOR = "#ECEDEA";
@@ -1,17 +1,17 @@
1
1
  import type { FieldIssue } from "@cosmicdrift/kumiko-framework/errors";
2
2
  import { getSchema, type JSONContent } from "@tiptap/core";
3
3
  import * as z from "zod";
4
- import type { DocumentHtmlKind } from "./constants.ts";
5
- import { documentBlocksForKind } from "./document-blocks.ts";
4
+ import { toVariableDef } from "./template-validation.ts";
5
+ import type { DocumentHtmlKind } from "./web/constants.ts";
6
+ import { documentBlocksForKind } from "./web/document-blocks.ts";
6
7
  import {
7
8
  DOCUMENT_TEXT_ALIGNMENTS,
8
9
  documentExtensions,
9
10
  isDocumentPlaceholderHref,
10
11
  isDocumentReferenceName,
11
12
  isDocumentResourceKey,
12
- } from "./document-schema.ts";
13
- import { contentToLine, lineToContent, singleLine } from "./placeholder-text.ts";
14
- import { toVariableDef } from "./template-validation.ts";
13
+ } from "./web/document-schema.ts";
14
+ import { contentToLine, lineToContent, singleLine } from "./web/placeholder-text.ts";
15
15
 
16
16
  export const MAX_DOCUMENT_OPS = 50;
17
17
 
@@ -5,8 +5,8 @@ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
5
5
  import type { JSONContent } from "@tiptap/core";
6
6
  import { generateHTML, generateJSON } from "@tiptap/html/server";
7
7
  import { type Element as ScanElement, Window } from "happy-dom";
8
- import type { ContentFormat } from "./constants.ts";
9
- import { DOCUMENT_HTML_KINDS } from "./constants.ts";
8
+ import type { ContentFormat } from "./web/constants.ts";
9
+ import { DOCUMENT_HTML_KINDS } from "./web/constants.ts";
10
10
  import {
11
11
  DOCUMENT_CONDITION_ATTRIBUTE,
12
12
  documentExtensions,
@@ -14,8 +14,8 @@ import {
14
14
  isDocumentPlaceholderHref,
15
15
  isDocumentReferenceName,
16
16
  isDocumentResourceKey,
17
- } from "./document-schema.ts";
18
- import { isDocumentStepKey } from "./document-steps.ts";
17
+ } from "./web/document-schema.ts";
18
+ import { isDocumentStepKey } from "./web/document-steps.ts";
19
19
 
20
20
  export type DocumentViolationCode =
21
21
  | "script_element"
@@ -26,6 +26,7 @@ export type DocumentViolationCode =
26
26
  | "invalid_reference_name"
27
27
  | "embedded_content"
28
28
  | "unsafe_attribute_value"
29
+ | "images_block_pdf_only"
29
30
  | "button_href_not_placeholder"
30
31
  | "document_requires_html_format"
31
32
  | "layout_requires_mjml_format"
@@ -143,6 +144,7 @@ const REFERENCE_ATTRIBUTES: ReadonlyArray<readonly [string, (value: string) => b
143
144
  [DOCUMENT_CONDITION_ATTRIBUTE, isDocumentReferenceName],
144
145
  ["data-resource", isDocumentResourceKey],
145
146
  ["data-steps", isDocumentReferenceName],
147
+ ["data-images", isDocumentReferenceName],
146
148
  ["data-step", isDocumentStepKey],
147
149
  ];
148
150
 
@@ -313,6 +315,36 @@ function requiredFormatViolation(
313
315
  return null;
314
316
  }
315
317
 
318
+ const IMAGES_BLOCK_KIND = "document-pdf";
319
+
320
+ // The node is part of the shared document model, so a mail body would keep it after the
321
+ // rebuild; only a PDF resolves the record's images.
322
+ function imagesBlockViolation(input: {
323
+ readonly kind: string;
324
+ readonly contentFormat: ContentFormat;
325
+ readonly content: string | null;
326
+ }): DocumentViolation | null {
327
+ if (
328
+ input.kind === IMAGES_BLOCK_KIND ||
329
+ !isSanitizedHtmlKind(input.kind) ||
330
+ input.contentFormat !== "html" ||
331
+ input.content === null
332
+ ) {
333
+ return null;
334
+ }
335
+ const hasImagesBlock = withParsedDocument(
336
+ input.content,
337
+ (body) => body.querySelector("div[data-images]") !== null,
338
+ );
339
+ return hasImagesBlock
340
+ ? violation(
341
+ "images_block_pdf_only",
342
+ input.kind,
343
+ `Record images blocks are only allowed in templates of kind ${IMAGES_BLOCK_KIND}`,
344
+ )
345
+ : null;
346
+ }
347
+
316
348
  export function sanitizeTemplateBody(input: {
317
349
  readonly kind: string;
318
350
  readonly contentFormat: ContentFormat;
@@ -320,6 +352,8 @@ export function sanitizeTemplateBody(input: {
320
352
  }): SanitizeBodyResult {
321
353
  const formatViolation = requiredFormatViolation(input.kind, input.contentFormat);
322
354
  if (formatViolation !== null) return { ok: false, violations: [formatViolation] };
355
+ const imagesViolation = imagesBlockViolation(input);
356
+ if (imagesViolation !== null) return { ok: false, violations: [imagesViolation] };
323
357
  if (
324
358
  !isSanitizedHtmlKind(input.kind) ||
325
359
  input.contentFormat !== "html" ||
@@ -340,6 +374,7 @@ const VIOLATION_I18N_KEYS: Readonly<Record<DocumentViolationCode, string>> = {
340
374
  invalid_reference_name: "templateResolver.errors.document.invalidReferenceName",
341
375
  embedded_content: "templateResolver.errors.document.embeddedContent",
342
376
  unsafe_attribute_value: "templateResolver.errors.document.unsafeAttributeValue",
377
+ images_block_pdf_only: "templateResolver.errors.document.imagesBlockPdfOnly",
343
378
  button_href_not_placeholder: "templateResolver.errors.document.buttonHrefNotPlaceholder",
344
379
  document_requires_html_format: "templateResolver.errors.document.documentRequiresHtmlFormat",
345
380
  layout_requires_mjml_format: "templateResolver.errors.document.layoutRequiresMjmlFormat",