@cosmicdrift/kumiko-bundled-features 0.362.0 → 0.364.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 (468) hide show
  1. package/dist/admin-shell/feature.d.ts +3 -0
  2. package/dist/admin-shell/feature.js +6 -1
  3. package/dist/agent-tools/agent-manifest.js +2 -1
  4. package/dist/agent-tools/filter-ops.d.ts +1 -1
  5. package/dist/agent-tools/filter-ops.js +1 -3
  6. package/dist/agent-tools/tool-dispatch.js +2 -1
  7. package/dist/audit/handlers/list.query.js +9 -5
  8. package/dist/auth-email-password/bootstrap.d.ts +8 -5
  9. package/dist/auth-email-password/bootstrap.js +12 -4
  10. package/dist/auth-email-password/email-rate-limits.d.ts +4 -0
  11. package/dist/auth-email-password/email-rate-limits.js +13 -0
  12. package/dist/auth-email-password/email-templates.d.ts +4 -26
  13. package/dist/auth-email-password/email-templates.js +9 -101
  14. package/dist/auth-email-password/feature.js +24 -6
  15. package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
  16. package/dist/auth-email-password/handlers/change-password.write.js +6 -2
  17. package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
  18. package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
  19. package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
  20. package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
  21. package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
  22. package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
  23. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  24. package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
  25. package/dist/auth-email-password/handlers/logout.write.js +6 -2
  26. package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
  27. package/dist/auth-email-password/handlers/request-account-unlock.write.js +2 -4
  28. package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
  29. package/dist/auth-email-password/handlers/request-email-verification.write.js +2 -4
  30. package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
  31. package/dist/auth-email-password/handlers/request-password-reset.write.js +2 -4
  32. package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
  33. package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
  34. package/dist/auth-email-password/handlers/signup-confirm.write.js +3 -4
  35. package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
  36. package/dist/auth-email-password/handlers/signup-request.write.js +2 -1
  37. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
  38. package/dist/auth-email-password/handlers/token-request-handler.d.ts +2 -3
  39. package/dist/auth-email-password/handlers/token-request-handler.js +2 -2
  40. package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
  41. package/dist/auth-email-password/index.d.ts +2 -2
  42. package/dist/auth-email-password/index.js +1 -1
  43. package/dist/auth-email-password/magic-link-mail.d.ts +3 -2
  44. package/dist/auth-email-password/magic-link-mail.js +19 -14
  45. package/dist/auth-email-password/mail-notifications.d.ts +32 -0
  46. package/dist/auth-email-password/mail-notifications.js +116 -0
  47. package/dist/auth-email-password/signup-activation.d.ts +1 -1
  48. package/dist/auth-email-password/signup-activation.js +2 -3
  49. package/dist/auth-email-password/web/auth-gate.js +16 -4
  50. package/dist/auth-foundation/types.js +2 -8
  51. package/dist/auth-mfa/feature.js +4 -4
  52. package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
  53. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
  54. package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
  55. package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
  56. package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
  57. package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
  58. package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
  59. package/dist/auth-mfa/hmac-token-codec.js +4 -4
  60. package/dist/auth-mfa/mfa-challenge-token.js +3 -3
  61. package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -3
  62. package/dist/auth-mfa/mfa-setup-token.js +8 -8
  63. package/dist/billing-foundation/constants.d.ts +3 -3
  64. package/dist/billing-foundation/constants.js +5 -4
  65. package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +32 -14
  66. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +74 -32
  67. package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +3 -0
  68. package/dist/billing-foundation/consumer-protection/consent-gate.js +21 -2
  69. package/dist/billing-foundation/consumer-protection/issue-confirmation.js +3 -3
  70. package/dist/billing-foundation/consumer-protection/mail-notifications.d.ts +8 -0
  71. package/dist/billing-foundation/consumer-protection/mail-notifications.js +95 -0
  72. package/dist/billing-foundation/consumer-protection/register-consent.js +2 -0
  73. package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
  74. package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +25 -13
  75. package/dist/billing-foundation/consumer-protection/termination-mail.js +11 -23
  76. package/dist/billing-foundation/consumer-protection/termination-notify.js +2 -2
  77. package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -3
  78. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +6 -0
  79. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +34 -0
  80. package/dist/billing-foundation/consumer-protection/termination-record.d.ts +2 -0
  81. package/dist/billing-foundation/consumer-protection/termination-record.js +7 -1
  82. package/dist/billing-foundation/consumer-protection/termination-request.js +31 -21
  83. package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
  84. package/dist/billing-foundation/consumer-protection/termination-texts.js +6 -0
  85. package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +10 -0
  86. package/dist/billing-foundation/consumer-protection/withdrawal-period.js +26 -0
  87. package/dist/billing-foundation/handlers/create-checkout-session.write.js +11 -5
  88. package/dist/billing-foundation/handlers/process-event.write.js +12 -8
  89. package/dist/billing-foundation/handlers/process-payment-event.write.js +1 -1
  90. package/dist/billing-foundation/i18n.js +3 -0
  91. package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
  92. package/dist/billing-foundation/subscription-tier-sync.js +16 -9
  93. package/dist/billing-foundation/types.d.ts +7 -2
  94. package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
  95. package/dist/cap-counter/enforce-cap.d.ts +18 -1
  96. package/dist/cap-counter/enforce-cap.js +49 -6
  97. package/dist/cap-counter/index.d.ts +1 -1
  98. package/dist/cap-counter/index.js +1 -1
  99. package/dist/cap-counter/with-cap-enforcement.d.ts +2 -0
  100. package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -1
  101. package/dist/cap-overview/web/cap-usage-cell.js +5 -4
  102. package/dist/channel-email/email-channel.d.ts +2 -2
  103. package/dist/channel-email/email-channel.js +101 -26
  104. package/dist/channel-email/feature.js +3 -3
  105. package/dist/channel-email/index.d.ts +1 -1
  106. package/dist/channel-email/index.js +1 -1
  107. package/dist/channel-email/pii-guard.js +10 -0
  108. package/dist/channel-email/smtp-transport.js +13 -0
  109. package/dist/channel-email/types.d.ts +8 -0
  110. package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
  111. package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
  112. package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
  113. package/dist/config/handlers/reset.write.d.ts +1 -1
  114. package/dist/config/handlers/set.write.d.ts +1 -1
  115. package/dist/config/handlers/set.write.js +3 -4
  116. package/dist/config/resolver.d.ts +1 -0
  117. package/dist/config/resolver.js +11 -14
  118. package/dist/config/write-helpers.d.ts +2 -0
  119. package/dist/config/write-helpers.js +19 -0
  120. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
  121. package/dist/crypto-shredding/handlers/forget-subject.write.js +29 -19
  122. package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
  123. package/dist/custom-fields/lib/value-schema.js +10 -11
  124. package/dist/custom-fields/wire-user-data-rights.js +7 -6
  125. package/dist/data-retention/index.d.ts +1 -0
  126. package/dist/data-retention/index.js +1 -0
  127. package/dist/data-retention/run-retention-cleanup.js +6 -6
  128. package/dist/delivery/attachment-ref.d.ts +41 -0
  129. package/dist/delivery/attachment-ref.js +78 -0
  130. package/dist/delivery/channel-context.d.ts +8 -4
  131. package/dist/delivery/channel-context.js +94 -2
  132. package/dist/delivery/constants.d.ts +1 -0
  133. package/dist/delivery/constants.js +1 -0
  134. package/dist/delivery/delivery-service.d.ts +4 -1
  135. package/dist/delivery/delivery-service.js +38 -32
  136. package/dist/delivery/feature.js +1 -1
  137. package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
  138. package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
  139. package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
  140. package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
  141. package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
  142. package/dist/delivery/index.d.ts +5 -2
  143. package/dist/delivery/index.js +4 -1
  144. package/dist/delivery/jobs.js +66 -14
  145. package/dist/delivery/notify-factory.js +4 -1
  146. package/dist/delivery/testing.d.ts +4 -1
  147. package/dist/delivery/types.d.ts +26 -14
  148. package/dist/delivery/unsubscribe.js +4 -1
  149. package/dist/derivatives-sharp/render.js +1 -18
  150. package/dist/document-ingest-foundation/pages.js +4 -4
  151. package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
  152. package/dist/feature-toggles/toggle-sync-signal.js +4 -4
  153. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  154. package/dist/file-foundation/index.d.ts +1 -0
  155. package/dist/file-foundation/index.js +1 -0
  156. package/dist/file-foundation/read-file-ref-content.d.ts +27 -0
  157. package/dist/file-foundation/read-file-ref-content.js +51 -0
  158. package/dist/files-tenant-data/hooks.js +4 -0
  159. package/dist/folders/web/folder-manager.js +3 -2
  160. package/dist/form-draft/db/queries/cleanup.js +4 -4
  161. package/dist/form-draft/db/queries/draft-count.js +2 -2
  162. package/dist/form-draft/db/queries/owned-file-refs.js +2 -2
  163. package/dist/form-draft/handlers/discard.write.d.ts +1 -1
  164. package/dist/form-draft/handlers/save.write.d.ts +1 -1
  165. package/dist/form-draft/release-file-refs.js +3 -2
  166. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
  167. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +6 -6
  168. package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +1 -1
  169. package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
  170. package/dist/inbound-mail-foundation/oauth-state.js +7 -7
  171. package/dist/inbound-mail-foundation/types.js +4 -4
  172. package/dist/inbound-mail-foundation/watch-supervisor.js +24 -13
  173. package/dist/inbound-provider-imap/imap-client.js +4 -3
  174. package/dist/jobs/db/queries/stale-run-sweep.d.ts +1 -1
  175. package/dist/jobs/db/queries/stale-run-sweep.js +12 -5
  176. package/dist/jobs/feature.js +5 -3
  177. package/dist/jobs/handlers/list.query.js +3 -1
  178. package/dist/jobs/handlers/retry.write.d.ts +1 -1
  179. package/dist/jobs/handlers/stale-run-sweep.job.d.ts +1 -1
  180. package/dist/jobs/handlers/stale-run-sweep.job.js +2 -2
  181. package/dist/jobs/handlers/trigger.write.d.ts +1 -1
  182. package/dist/jobs/job-run-logger.js +32 -10
  183. package/dist/jobs/tenant-job-run-table.js +3 -2
  184. package/dist/ledger/entity.js +3 -1
  185. package/dist/ledger/recurring.js +1 -3
  186. package/dist/managed-pages/branding.d.ts +2 -0
  187. package/dist/managed-pages/branding.js +24 -2
  188. package/dist/managed-pages/feature.js +1 -0
  189. package/dist/managed-pages/handlers/set.write.d.ts +1 -1
  190. package/dist/managed-pages/i18n.js +1 -0
  191. package/dist/managed-pages/screens/branding-screen.js +8 -1
  192. package/dist/page-render/branding.d.ts +1 -0
  193. package/dist/page-render/branding.js +1 -0
  194. package/dist/page-render/markdown.js +1 -1
  195. package/dist/personal-access-tokens/feature.js +1 -1
  196. package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
  197. package/dist/personal-access-tokens/handlers/list.query.js +2 -1
  198. package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
  199. package/dist/personal-access-tokens/revoke-for-user.js +2 -1
  200. package/dist/renderer-foundation/api.d.ts +13 -13
  201. package/dist/renderer-foundation/api.js +1 -1
  202. package/dist/renderer-foundation/constants.js +2 -4
  203. package/dist/renderer-foundation/feature.js +7 -7
  204. package/dist/renderer-foundation/index.d.ts +2 -1
  205. package/dist/renderer-foundation/index.js +1 -0
  206. package/dist/renderer-foundation/types.d.ts +94 -32
  207. package/dist/renderer-pdf/feature.js +7 -1
  208. package/dist/renderer-pdf/render-table-pdf.d.ts +27 -33
  209. package/dist/renderer-pdf/render-table-pdf.js +95 -34
  210. package/dist/secrets/feature.js +4 -3
  211. package/dist/secrets/handlers/delete.write.d.ts +1 -1
  212. package/dist/secrets/handlers/set.write.d.ts +1 -1
  213. package/dist/secrets/secrets-context.js +15 -3
  214. package/dist/sessions/db/queries/cleanup.js +2 -2
  215. package/dist/sessions/feature.js +1 -1
  216. package/dist/sessions/handlers/list.query.js +2 -1
  217. package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
  218. package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
  219. package/dist/sessions/handlers/revoke.write.d.ts +1 -1
  220. package/dist/shared/attachment-file-name.d.ts +3 -0
  221. package/dist/shared/attachment-file-name.js +35 -0
  222. package/dist/shared/chunked-entity-migration.d.ts +1 -0
  223. package/dist/shared/chunked-entity-migration.js +1 -1
  224. package/dist/shared/index.d.ts +3 -0
  225. package/dist/shared/index.js +3 -0
  226. package/dist/shared/is-tenant-db.js +1 -1
  227. package/dist/shared/mail-template-body.d.ts +7 -0
  228. package/dist/shared/mail-template-body.js +27 -0
  229. package/dist/shared/sharp-input-limits.d.ts +6 -0
  230. package/dist/shared/sharp-input-limits.js +22 -0
  231. package/dist/shared/signup-handover.js +2 -2
  232. package/dist/subscription-stripe/plugin-methods.js +18 -8
  233. package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
  234. package/dist/tags/handlers/assign-tag.write.js +3 -1
  235. package/dist/tags/web/tag-chip.js +2 -0
  236. package/dist/template-resolver/api.d.ts +30 -2
  237. package/dist/template-resolver/api.js +42 -6
  238. package/dist/template-resolver/compile-document-body.d.ts +12 -0
  239. package/dist/template-resolver/compile-document-body.js +205 -0
  240. package/dist/template-resolver/constants.d.ts +13 -4
  241. package/dist/template-resolver/constants.js +32 -10
  242. package/dist/template-resolver/document-sanitizer.d.ts +32 -0
  243. package/dist/template-resolver/document-sanitizer.js +201 -0
  244. package/dist/template-resolver/document-schema.d.ts +21 -0
  245. package/dist/template-resolver/document-schema.js +316 -0
  246. package/dist/template-resolver/feature.d.ts +20 -0
  247. package/dist/template-resolver/feature.js +53 -6
  248. package/dist/template-resolver/handlers/bundle-import.d.ts +20 -0
  249. package/dist/template-resolver/handlers/bundle-import.js +147 -0
  250. package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
  251. package/dist/template-resolver/handlers/collection-set.write.js +25 -2
  252. package/dist/template-resolver/handlers/copy-locale.write.d.ts +19 -0
  253. package/dist/template-resolver/handlers/copy-locale.write.js +134 -0
  254. package/dist/template-resolver/handlers/diff-to-system.query.d.ts +22 -0
  255. package/dist/template-resolver/handlers/diff-to-system.query.js +30 -0
  256. package/dist/template-resolver/handlers/discard-draft.write.d.ts +13 -0
  257. package/dist/template-resolver/handlers/discard-draft.write.js +37 -0
  258. package/dist/template-resolver/handlers/export-bundle.query.d.ts +35 -0
  259. package/dist/template-resolver/handlers/export-bundle.query.js +118 -0
  260. package/dist/template-resolver/handlers/find-by-id.query.d.ts +2 -0
  261. package/dist/template-resolver/handlers/find-by-id.query.js +9 -6
  262. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +39 -0
  263. package/dist/template-resolver/handlers/import-bundle-system.write.js +36 -0
  264. package/dist/template-resolver/handlers/import-bundle.write.d.ts +40 -0
  265. package/dist/template-resolver/handlers/import-bundle.write.js +19 -0
  266. package/dist/template-resolver/handlers/layout-source.query.d.ts +31 -0
  267. package/dist/template-resolver/handlers/layout-source.query.js +87 -0
  268. package/dist/template-resolver/handlers/list.query.d.ts +4 -1
  269. package/dist/template-resolver/handlers/list.query.js +9 -8
  270. package/dist/template-resolver/handlers/override-diff.d.ts +20 -0
  271. package/dist/template-resolver/handlers/override-diff.js +41 -0
  272. package/dist/template-resolver/handlers/overview.query.d.ts +65 -0
  273. package/dist/template-resolver/handlers/overview.query.js +205 -0
  274. package/dist/template-resolver/handlers/preview.query.d.ts +12 -0
  275. package/dist/template-resolver/handlers/preview.query.js +24 -0
  276. package/dist/template-resolver/handlers/reset-to-system.write.d.ts +8 -0
  277. package/dist/template-resolver/handlers/reset-to-system.write.js +50 -0
  278. package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +8 -0
  279. package/dist/template-resolver/handlers/resource-link-pdf.write.js +32 -0
  280. package/dist/template-resolver/handlers/resource-remove.write.d.ts +9 -0
  281. package/dist/template-resolver/handlers/resource-remove.write.js +44 -0
  282. package/dist/template-resolver/handlers/resource-shared.d.ts +9 -0
  283. package/dist/template-resolver/handlers/resource-shared.js +14 -0
  284. package/dist/template-resolver/handlers/resource-upload.write.d.ts +13 -0
  285. package/dist/template-resolver/handlers/resource-upload.write.js +98 -0
  286. package/dist/template-resolver/handlers/send-test.write.d.ts +13 -0
  287. package/dist/template-resolver/handlers/send-test.write.js +108 -0
  288. package/dist/template-resolver/handlers/set.write.d.ts +5 -2
  289. package/dist/template-resolver/handlers/set.write.js +25 -3
  290. package/dist/template-resolver/handlers/shared.d.ts +9 -5
  291. package/dist/template-resolver/handlers/shared.js +6 -2
  292. package/dist/template-resolver/handlers/template-gates.d.ts +36 -0
  293. package/dist/template-resolver/handlers/template-gates.js +142 -0
  294. package/dist/template-resolver/handlers/template-write.d.ts +45 -0
  295. package/dist/template-resolver/handlers/template-write.js +141 -0
  296. package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +49 -0
  297. package/dist/template-resolver/handlers/tenant-overrides.query.js +112 -0
  298. package/dist/template-resolver/handlers/toggle-status.write.d.ts +27 -2
  299. package/dist/template-resolver/handlers/toggle-status.write.js +47 -15
  300. package/dist/template-resolver/handlers/upsert-system.write.d.ts +5 -8
  301. package/dist/template-resolver/handlers/upsert-system.write.js +14 -59
  302. package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +5 -8
  303. package/dist/template-resolver/handlers/upsert-tenant.write.js +11 -62
  304. package/dist/template-resolver/handlers/usage.query.d.ts +17 -0
  305. package/dist/template-resolver/handlers/usage.query.js +24 -0
  306. package/dist/template-resolver/handlers/validate.query.d.ts +74 -0
  307. package/dist/template-resolver/handlers/validate.query.js +80 -0
  308. package/dist/template-resolver/index.d.ts +19 -1
  309. package/dist/template-resolver/index.js +17 -1
  310. package/dist/template-resolver/layout-appearance.d.ts +10 -0
  311. package/dist/template-resolver/layout-appearance.js +26 -0
  312. package/dist/template-resolver/layout-regions.d.ts +23 -0
  313. package/dist/template-resolver/layout-regions.js +125 -0
  314. package/dist/template-resolver/linked-resources.d.ts +17 -0
  315. package/dist/template-resolver/linked-resources.js +75 -0
  316. package/dist/template-resolver/pdf-constants.d.ts +7 -0
  317. package/dist/template-resolver/pdf-constants.js +8 -0
  318. package/dist/template-resolver/preview-display-html.d.ts +7 -0
  319. package/dist/template-resolver/preview-display-html.js +27 -0
  320. package/dist/template-resolver/print-document.d.ts +1 -0
  321. package/dist/template-resolver/print-document.js +55 -0
  322. package/dist/template-resolver/qualified-names.d.ts +34 -0
  323. package/dist/template-resolver/qualified-names.js +34 -0
  324. package/dist/template-resolver/record-mapping.d.ts +2 -0
  325. package/dist/template-resolver/record-mapping.js +21 -0
  326. package/dist/template-resolver/resource-image.d.ts +15 -0
  327. package/dist/template-resolver/resource-image.js +34 -0
  328. package/dist/template-resolver/seeding.d.ts +14 -1
  329. package/dist/template-resolver/seeding.js +125 -18
  330. package/dist/template-resolver/store-template-resource.d.ts +64 -0
  331. package/dist/template-resolver/store-template-resource.js +264 -0
  332. package/dist/template-resolver/table.d.ts +150 -8
  333. package/dist/template-resolver/table.js +20 -2
  334. package/dist/template-resolver/template-bundle.d.ts +36 -0
  335. package/dist/template-resolver/template-bundle.js +60 -0
  336. package/dist/template-resolver/template-diff.d.ts +7 -0
  337. package/dist/template-resolver/template-diff.js +68 -0
  338. package/dist/template-resolver/template-draft.d.ts +51 -0
  339. package/dist/template-resolver/template-draft.js +72 -0
  340. package/dist/template-resolver/template-meta.d.ts +85 -0
  341. package/dist/template-resolver/template-meta.js +145 -0
  342. package/dist/template-resolver/template-preview.d.ts +103 -0
  343. package/dist/template-resolver/template-preview.js +418 -0
  344. package/dist/template-resolver/template-resources.d.ts +19 -0
  345. package/dist/template-resolver/template-resources.js +73 -0
  346. package/dist/template-resolver/template-usage.d.ts +16 -0
  347. package/dist/template-resolver/template-usage.js +35 -0
  348. package/dist/template-resolver/template-validation.d.ts +22 -0
  349. package/dist/template-resolver/template-validation.js +173 -0
  350. package/dist/template-resolver/testing.js +5 -5
  351. package/dist/template-resolver/user-content-table.d.ts +14 -8
  352. package/dist/template-resolver/web/client-plugin.d.ts +1 -0
  353. package/dist/template-resolver/web/client-plugin.js +12 -7
  354. package/dist/template-resolver/web/document-editor-extensions.d.ts +18 -0
  355. package/dist/template-resolver/web/document-editor-extensions.js +145 -0
  356. package/dist/template-resolver/web/document-editor.d.ts +4 -0
  357. package/dist/template-resolver/web/document-editor.js +17 -0
  358. package/dist/template-resolver/web/document-tiptap-editor.d.ts +3 -0
  359. package/dist/template-resolver/web/document-tiptap-editor.js +492 -0
  360. package/dist/template-resolver/web/document-toolbar-blocks.d.ts +41 -0
  361. package/dist/template-resolver/web/document-toolbar-blocks.js +19 -0
  362. package/dist/template-resolver/web/editor-handle.d.ts +3 -0
  363. package/dist/template-resolver/web/editor-handle.js +24 -0
  364. package/dist/template-resolver/web/i18n.js +91 -0
  365. package/dist/template-resolver/web/index.d.ts +5 -0
  366. package/dist/template-resolver/web/index.js +5 -0
  367. package/dist/template-resolver/web/placeholder-line-editor-lazy.d.ts +4 -0
  368. package/dist/template-resolver/web/placeholder-line-editor-lazy.js +16 -0
  369. package/dist/template-resolver/web/placeholder-line-editor.d.ts +17 -0
  370. package/dist/template-resolver/web/placeholder-line-editor.js +94 -0
  371. package/dist/template-resolver/web/placeholder-text.d.ts +4 -0
  372. package/dist/template-resolver/web/placeholder-text.js +34 -0
  373. package/dist/template-resolver/web/use-stable-by-key.d.ts +1 -0
  374. package/dist/template-resolver/web/use-stable-by-key.js +13 -0
  375. package/dist/tenant/handlers/add-member.write.d.ts +1 -1
  376. package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
  377. package/dist/tenant/handlers/create.write.d.ts +1 -1
  378. package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
  379. package/dist/tenant/handlers/team-list.query.js +3 -2
  380. package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
  381. package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
  382. package/dist/tenant/handlers/update.write.d.ts +1 -1
  383. package/dist/tenant/seeding.js +31 -6
  384. package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
  385. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +2 -2
  386. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +2 -1
  387. package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
  388. package/dist/tier-engine/feature.js +9 -3
  389. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
  390. package/dist/user/db/queries/stream-tenant-backfill.js +4 -4
  391. package/dist/user/handlers/create.write.d.ts +1 -1
  392. package/dist/user/handlers/detail.query.js +2 -1
  393. package/dist/user/handlers/update.write.d.ts +1 -1
  394. package/dist/user/resolve-display-names.js +5 -9
  395. package/dist/user/seeding.js +20 -1
  396. package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
  397. package/dist/user-data-rights/email-templates.d.ts +1 -29
  398. package/dist/user-data-rights/email-templates.js +5 -106
  399. package/dist/user-data-rights/feature.js +53 -35
  400. package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +2 -2
  401. package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
  402. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
  403. package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
  404. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
  405. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
  406. package/dist/user-data-rights/handlers/request-deletion.write.js +5 -7
  407. package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
  408. package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
  409. package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
  410. package/dist/user-data-rights/lib/default-mailers.d.ts +15 -7
  411. package/dist/user-data-rights/lib/default-mailers.js +100 -48
  412. package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
  413. package/dist/user-data-rights/mail-notifications.d.ts +13 -0
  414. package/dist/user-data-rights/mail-notifications.js +118 -0
  415. package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
  416. package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
  417. package/dist/user-profile/web/profile-screen.js +2 -1
  418. package/dist/waitlist/feature.js +10 -0
  419. package/dist/waitlist/handlers/invite.write.d.ts +1 -1
  420. package/dist/waitlist/handlers/reject.write.d.ts +1 -1
  421. package/dist/waitlist/handlers/submit.write.d.ts +1 -1
  422. package/dist/waitlist/handlers/submit.write.js +3 -4
  423. package/dist/waitlist/mail.d.ts +19 -6
  424. package/dist/waitlist/mail.js +50 -14
  425. package/dist/waitlist/options.js +1 -1
  426. package/dist/workflow-runner/event-trigger.js +2 -2
  427. package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
  428. package/package.json +37 -17
  429. package/src/admin-shell/changes.json +6 -0
  430. package/src/audit/changes.json +6 -0
  431. package/src/auth-email-password/changes.json +51 -0
  432. package/src/billing-foundation/changes.json +43 -0
  433. package/src/cap-counter/changes.json +6 -0
  434. package/src/channel-email/changes.json +19 -0
  435. package/src/config/changes.json +6 -0
  436. package/src/crypto-shredding/changes.json +6 -0
  437. package/src/custom-fields/changes.json +6 -0
  438. package/src/data-retention/changes.json +6 -0
  439. package/src/delivery/changes.json +66 -0
  440. package/src/file-foundation/changes.json +6 -0
  441. package/src/files-tenant-data/changes.json +6 -0
  442. package/src/inbound-mail-foundation/changes.json +6 -0
  443. package/src/jobs/changes.json +19 -0
  444. package/src/ledger/changes.json +7 -0
  445. package/src/managed-pages/changes.json +6 -0
  446. package/src/renderer-foundation/changes.json +39 -0
  447. package/src/renderer-pdf/changes.json +12 -0
  448. package/src/secrets/changes.json +6 -0
  449. package/src/shared/changes.json +6 -0
  450. package/src/subscription-stripe/changes.json +6 -0
  451. package/src/template-resolver/changes.json +186 -0
  452. package/src/tier-engine/changes.json +6 -0
  453. package/src/user/changes.json +6 -0
  454. package/src/user-data-rights/changes.json +13 -0
  455. package/src/user-profile/changes.json +1 -1
  456. package/src/waitlist/changes.json +7 -0
  457. package/src/workflow-runner/changes.json +6 -0
  458. package/dist/renderer-simple/feature.d.ts +0 -10
  459. package/dist/renderer-simple/feature.js +0 -42
  460. package/dist/renderer-simple/index.d.ts +0 -3
  461. package/dist/renderer-simple/index.js +0 -2
  462. package/dist/renderer-simple/mail-blocks.d.ts +0 -21
  463. package/dist/renderer-simple/mail-blocks.js +0 -107
  464. package/dist/renderer-simple/resolve-variables.d.ts +0 -7
  465. package/dist/renderer-simple/resolve-variables.js +0 -47
  466. package/dist/renderer-simple/simple-renderer.d.ts +0 -24
  467. package/dist/renderer-simple/simple-renderer.js +0 -200
  468. package/src/renderer-simple/changes.json +0 -27
@@ -0,0 +1,418 @@
1
+ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import { NotFoundError, UnprocessableError, ValidationError, } from "@cosmicdrift/kumiko-framework/errors";
3
+ import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
4
+ import { escapeHtml } from "@cosmicdrift/kumiko-headless";
5
+ import { createEmailAttachmentReader, createTemplateMailRenderer } from "../delivery/index.js";
6
+ import { fileContextForTenant } from "../file-foundation/index.js";
7
+ import { collectRendererPlugins, createRendererFoundationApi, RendererError, } from "../renderer-foundation/index.js";
8
+ import { createTemplateResolverApi, TemplateNotFoundError } from "./api.js";
9
+ import { collectDocumentResourceKeys } from "./compile-document-body.js";
10
+ import { DEFAULT_MAIL_LAYOUT_SLUG, MAIL_PREVIEW_MAX_BYTES, SYSTEM_TENANT_ID, } from "./constants.js";
11
+ import { parseStoredJson } from "./handlers/template-gates.js";
12
+ import { validateTemplateDraft } from "./handlers/validate.query.js";
13
+ import { readLayoutRegions, sanitizeLayoutHtml, } from "./layout-regions.js";
14
+ import { parseLinkedResourceEntries } from "./linked-resources.js";
15
+ import { buildDisplayHtml } from "./preview-display-html.js";
16
+ import { TemplateResolverErrors } from "./qualified-names.js";
17
+ import { templateResourcesTable } from "./table.js";
18
+ import { isMailTemplateMeta, parseTemplateMeta, readPdfTemplateMeta, } from "./template-meta.js";
19
+ const SAMPLE_LINK = "https://example.com";
20
+ const SAMPLE_DATE = "2026-01-15";
21
+ const SAMPLE_LIST_ROWS = 3;
22
+ const DEFAULT_PDF_LAYOUT_SLUG = "default";
23
+ function sampleScalar(name, def) {
24
+ if (def.example !== undefined)
25
+ return def.example;
26
+ switch (def.type) {
27
+ case "text":
28
+ return `[${def.label ?? name}]`;
29
+ case "richText":
30
+ return `<p>[${escapeHtml(def.label ?? name)}]</p>`;
31
+ case "link":
32
+ return SAMPLE_LINK;
33
+ case "money":
34
+ return 0;
35
+ case "date":
36
+ return SAMPLE_DATE;
37
+ }
38
+ }
39
+ function sampleRow(item, given = {}) {
40
+ const row = {};
41
+ for (const [name, def] of Object.entries(item))
42
+ row[name] = sampleScalar(name, def);
43
+ return { ...row, ...given };
44
+ }
45
+ function sampleList(def) {
46
+ const rows = (def.example ?? [])
47
+ .slice(0, SAMPLE_LIST_ROWS)
48
+ .map((given) => sampleRow(def.item, given));
49
+ while (rows.length < SAMPLE_LIST_ROWS)
50
+ rows.push(sampleRow(def.item));
51
+ return rows;
52
+ }
53
+ // Declared examples win; the fallbacks are deterministic so a preview never differs between calls.
54
+ export function sampleValuesFor(variables) {
55
+ const values = {};
56
+ for (const [name, def] of Object.entries(variables)) {
57
+ values[name] = def.type === "list" ? sampleList(def) : sampleScalar(name, def);
58
+ }
59
+ return values;
60
+ }
61
+ const utf8Bytes = (value) => new TextEncoder().encode(value).length;
62
+ const failed = (error) => ({ ok: false, error });
63
+ async function loadStoredDraft(ctx, user, input) {
64
+ try {
65
+ return await createTemplateResolverApi(ctx.db).resolveTemplate({
66
+ tenantId: user.tenantId,
67
+ slug: input.slug,
68
+ kind: input.kind,
69
+ locale: input.locale,
70
+ });
71
+ }
72
+ catch (e) {
73
+ if (e instanceof TemplateNotFoundError)
74
+ return null;
75
+ throw e;
76
+ }
77
+ }
78
+ async function describeAttachments(ctx, refs) {
79
+ const fileRefIds = refs.flatMap((ref) => (ref.kind === "rendered-pdf" ? [] : [ref.fileRefId]));
80
+ const rows = fileRefIds.length === 0
81
+ ? []
82
+ : await selectMany(ctx.db, fileRefsTable, {
83
+ id: fileRefIds,
84
+ });
85
+ const sizeById = new Map(rows.map((row) => [row.id, Number(row.size)]));
86
+ // An inline image is named by its content id because the stored file name is personal data and
87
+ // may be ciphertext; a file attachment carries the name the admin gave it.
88
+ const attachments = refs.map((ref) => previewAttachmentOf(ref, sizeById));
89
+ // A fileRef without a row has no known size: it is reported, not counted as 0 bytes.
90
+ const missing = refs.flatMap((ref, index) => ref.kind !== "rendered-pdf" && !sizeById.has(ref.fileRefId)
91
+ ? [attachments[index]?.name ?? ""]
92
+ : []);
93
+ return { attachments, missing };
94
+ }
95
+ function previewAttachmentOf(ref, sizeById) {
96
+ switch (ref.kind) {
97
+ case "inline":
98
+ return {
99
+ kind: "inline",
100
+ cid: ref.cid,
101
+ name: ref.cid,
102
+ sizeBytes: sizeById.get(ref.fileRefId) ?? 0,
103
+ };
104
+ case "file":
105
+ return { kind: "file", name: ref.fileName, sizeBytes: sizeById.get(ref.fileRefId) ?? 0 };
106
+ case "rendered-pdf":
107
+ return { kind: "rendered-pdf", name: ref.fileName, templateSlug: ref.templateSlug };
108
+ }
109
+ }
110
+ // Part headers and multipart boundary lines around one attachment in the MIME message.
111
+ export const MIME_PART_OVERHEAD_BYTES = 512;
112
+ // The provider cap applies to the encoded message: attachments travel base64-encoded.
113
+ export function encodedAttachmentBytes(rawBytes) {
114
+ return Math.ceil(rawBytes / 3) * 4 + MIME_PART_OVERHEAD_BYTES;
115
+ }
116
+ const attachmentSizeOf = (attachment) => attachment.kind === "rendered-pdf" ? 0 : encodedAttachmentBytes(attachment.sizeBytes);
117
+ export function estimateMailBytes(args) {
118
+ return (utf8Bytes(args.html) +
119
+ utf8Bytes(args.text) +
120
+ args.attachments.reduce((sum, attachment) => sum + attachmentSizeOf(attachment), 0));
121
+ }
122
+ // The row an editor is working on, whatever its status: the stored template only resolves when
123
+ // active, so a draft would otherwise lose the images and attachments uploaded to it.
124
+ async function loadEditedRowResources(ctx, user, input) {
125
+ const rows = await selectMany(ctx.db, templateResourcesTable, { tenantId: user.tenantId, slug: input.slug, kind: "mail", locale: input.locale }, { limit: 1 });
126
+ const row = rows[0];
127
+ return row === undefined ? undefined : parseLinkedResourceEntries(row.linkedResources);
128
+ }
129
+ async function resolvePdfLayout(ctx, user, slug, locale) {
130
+ const api = createTemplateResolverApi(ctx.db);
131
+ const find = async (tenantId) => {
132
+ try {
133
+ return await api.resolveLayout({ tenantId, slug, kind: "pdf-layout", locale });
134
+ }
135
+ catch (e) {
136
+ if (e instanceof TemplateNotFoundError)
137
+ return null;
138
+ throw e;
139
+ }
140
+ };
141
+ let layout = await find(user.tenantId);
142
+ let html = layout?.content ?? "";
143
+ if (layout !== null && layout.scope !== "system") {
144
+ // Re-checked like the mail renderer does: a tenant row that predates the write gate must not pass.
145
+ const sanitized = sanitizeLayoutHtml(html);
146
+ if (sanitized.ok)
147
+ html = sanitized.html;
148
+ else {
149
+ layout = await find(SYSTEM_TENANT_ID);
150
+ html = layout?.content ?? "";
151
+ }
152
+ }
153
+ if (layout === null)
154
+ return null;
155
+ return { slug, scope: layout.scope, html, regions: readLayoutRegions(html) };
156
+ }
157
+ function missingResourceCheck(missing) {
158
+ return {
159
+ code: "resources_resolved",
160
+ ok: missing.length === 0,
161
+ i18nKey: "templateResolver.checks.resourcesResolved",
162
+ params: { missing },
163
+ };
164
+ }
165
+ function requiredPlaceholdersCheck(missing) {
166
+ return {
167
+ code: "required_placeholders",
168
+ ok: missing.length === 0,
169
+ i18nKey: "templateResolver.checks.requiredPlaceholders",
170
+ params: { missing },
171
+ };
172
+ }
173
+ function toRenderFailure(e) {
174
+ if (!(e instanceof RendererError))
175
+ throw e;
176
+ return failed(new UnprocessableError(TemplateResolverErrors.previewRenderFailed, {
177
+ i18nKey: "templateResolver.errors.preview.renderFailed",
178
+ details: { code: e.code },
179
+ }));
180
+ }
181
+ // Only a plugin that prints documents can show the filled-in page; without one the editor
182
+ // still gets the body and the raw layout, just without placeholder values.
183
+ function documentPreviewPlugin(ctx, tenantId) {
184
+ try {
185
+ const plugin = createRendererFoundationApi(collectRendererPlugins(ctx.registry)).createRendererForTenant({ tenantId, kind: "document-pdf" });
186
+ return plugin.previewDocument === undefined ? null : plugin;
187
+ }
188
+ catch (e) {
189
+ if (e instanceof RendererError && e.code === "no_plugin_for_kind")
190
+ return null;
191
+ throw e;
192
+ }
193
+ }
194
+ async function renderDocumentPreview(ctx, user, args) {
195
+ const { input, content, meta, validation, sampleValues } = args;
196
+ const pdfMeta = meta.ok ? readPdfTemplateMeta(meta.meta) : null;
197
+ const requiredCheck = requiredPlaceholdersCheck(validation.missingRequired);
198
+ const plugin = documentPreviewPlugin(ctx, user.tenantId);
199
+ if (plugin?.previewDocument === undefined) {
200
+ return {
201
+ ok: true,
202
+ mailAttachments: [],
203
+ sender: {},
204
+ preview: {
205
+ kind: "document-pdf",
206
+ subject: null,
207
+ previewText: null,
208
+ html: content,
209
+ text: null,
210
+ attachments: [],
211
+ layout: await resolvePdfLayout(ctx, user, pdfMeta?.layoutSlug ?? DEFAULT_PDF_LAYOUT_SLUG, input.locale),
212
+ sampleValues,
213
+ checks: [missingResourceCheck(validation.warnings.map((w) => w.detail)), requiredCheck],
214
+ validation,
215
+ },
216
+ };
217
+ }
218
+ let document;
219
+ try {
220
+ document = await plugin.previewDocument({
221
+ slug: input.slug,
222
+ locale: input.locale,
223
+ variables: sampleValues,
224
+ draft: { content, meta: pdfMeta, variableSchema: args.variableSchema },
225
+ }, {
226
+ db: ctx.db,
227
+ registry: ctx.registry,
228
+ tenantId: user.tenantId,
229
+ ...(ctx.config !== undefined && { config: ctx.config }),
230
+ filesForTenant: (fileTenantId) => fileContextForTenant(ctx, ctx.user.tenantId, fileTenantId),
231
+ });
232
+ }
233
+ catch (e) {
234
+ return toRenderFailure(e);
235
+ }
236
+ const layout = document.layout;
237
+ return {
238
+ ok: true,
239
+ mailAttachments: [],
240
+ sender: {},
241
+ preview: {
242
+ kind: "document-pdf",
243
+ subject: null,
244
+ previewText: null,
245
+ html: document.bodyHtml,
246
+ text: null,
247
+ attachments: [],
248
+ layout: layout === null
249
+ ? null
250
+ : {
251
+ slug: layout.slug,
252
+ scope: layout.scope,
253
+ html: `${layout.headerHtml}${layout.signatureHtml}${layout.footerHtml}`,
254
+ regions: {
255
+ header: layout.headerHtml,
256
+ signature: layout.signatureHtml,
257
+ footer: layout.footerHtml,
258
+ },
259
+ },
260
+ sampleValues,
261
+ checks: [missingResourceCheck(document.missingResourceKeys), requiredCheck],
262
+ validation,
263
+ },
264
+ };
265
+ }
266
+ // The caller derives `scope` once from the role (SystemAdmin edits the system row), so the write
267
+ // gates and the print see the same row: a failing draft is rejected with the same problems as a save.
268
+ export async function evaluateDocumentDraft(ctx, user, scope, draft) {
269
+ const { response, evaluation } = await validateTemplateDraft(ctx, user, {
270
+ slug: draft.slug,
271
+ kind: "document-pdf",
272
+ contentFormat: "html",
273
+ content: draft.content,
274
+ meta: draft.meta,
275
+ variableSchema: draft.variableSchema,
276
+ locale: draft.locale,
277
+ scope,
278
+ });
279
+ if (!evaluation.ok) {
280
+ return { ok: false, error: new ValidationError({ fields: [...evaluation.issues] }) };
281
+ }
282
+ const meta = parseTemplateMeta("document-pdf", parseStoredJson(evaluation.metaJson));
283
+ return {
284
+ ok: true,
285
+ content: evaluation.content ?? "",
286
+ meta: meta.ok ? readPdfTemplateMeta(meta.meta) : null,
287
+ variableSchema: draft.variableSchema,
288
+ sampleValues: sampleValuesFor(response.variables),
289
+ };
290
+ }
291
+ // The posted draft when it carries content, else the stored template; null when none is stored.
292
+ async function resolveDraftSource(ctx, user, input) {
293
+ if (input.content !== undefined) {
294
+ return { content: input.content, meta: input.meta, variableSchema: input.variableSchema };
295
+ }
296
+ const stored = await loadStoredDraft(ctx, user, input);
297
+ if (stored === null)
298
+ return null;
299
+ return { content: stored.content, meta: stored.meta, variableSchema: stored.variableSchema };
300
+ }
301
+ async function renderMailPreview(ctx, user, args) {
302
+ const { input, content, meta, validation, sampleValues, variableSchema: draftVariableSchema, } = args;
303
+ if (!meta.ok || !isMailTemplateMeta(meta.meta)) {
304
+ return failed(new UnprocessableError(TemplateResolverErrors.previewRenderFailed, {
305
+ i18nKey: "templateResolver.errors.preview.renderFailed",
306
+ details: { code: "invalid_meta" },
307
+ }));
308
+ }
309
+ const mailMeta = meta.meta;
310
+ const editedResources = input.content === undefined ? undefined : await loadEditedRowResources(ctx, user, input);
311
+ let rendered;
312
+ try {
313
+ rendered = await createTemplateMailRenderer(ctx.db, ctx.registry, user.tenantId, ctx.config)({
314
+ slug: input.slug,
315
+ locale: input.locale,
316
+ variables: sampleValues,
317
+ draft: {
318
+ content,
319
+ meta: mailMeta,
320
+ variableSchema: draftVariableSchema,
321
+ ...(editedResources !== undefined && { linkedResources: editedResources }),
322
+ },
323
+ });
324
+ }
325
+ catch (e) {
326
+ return toRenderFailure(e);
327
+ }
328
+ const mailAttachments = rendered.attachments ?? [];
329
+ const inline = mailAttachments.filter((ref) => ref.kind === "inline");
330
+ const { attachments, missing: missingAttachments } = await describeAttachments(ctx, mailAttachments);
331
+ const displayHtml = await buildDisplayHtml(rendered.html, inline, createEmailAttachmentReader(ctx.db, ctx.registry, user.tenantId, ctx.config, (tenantId) => fileContextForTenant(ctx, ctx.user.tenantId, tenantId)));
332
+ const resolvedCids = new Set(inline.map((ref) => ref.cid));
333
+ const unresolved = collectDocumentResourceKeys(content).filter((key) => !resolvedCids.has(key));
334
+ const totalBytes = estimateMailBytes({ html: rendered.html, text: rendered.text, attachments });
335
+ return {
336
+ ok: true,
337
+ mailAttachments,
338
+ sender: {
339
+ ...(rendered.fromName !== undefined && { fromName: rendered.fromName }),
340
+ ...(rendered.replyTo !== undefined && { replyTo: rendered.replyTo }),
341
+ },
342
+ preview: {
343
+ kind: "mail",
344
+ subject: rendered.subject,
345
+ previewText: rendered.previewText ?? null,
346
+ html: rendered.html,
347
+ displayHtml,
348
+ text: rendered.text,
349
+ attachments,
350
+ layout: null,
351
+ mailLayout: rendered.layout === undefined
352
+ ? null
353
+ : {
354
+ slug: mailMeta.layoutSlug ?? DEFAULT_MAIL_LAYOUT_SLUG,
355
+ scope: rendered.layout.scope,
356
+ version: rendered.layout.templateVersion,
357
+ },
358
+ sampleValues,
359
+ checks: [
360
+ missingResourceCheck([...unresolved, ...missingAttachments]),
361
+ {
362
+ code: "text_present",
363
+ ok: rendered.text.trim() !== "",
364
+ i18nKey: "templateResolver.checks.textPresent",
365
+ },
366
+ {
367
+ code: "size_under_cap",
368
+ ok: totalBytes <= MAIL_PREVIEW_MAX_BYTES,
369
+ i18nKey: "templateResolver.checks.sizeUnderCap",
370
+ params: { totalBytes, maxBytes: MAIL_PREVIEW_MAX_BYTES },
371
+ },
372
+ requiredPlaceholdersCheck(validation.missingRequired),
373
+ ],
374
+ validation,
375
+ },
376
+ };
377
+ }
378
+ // Renders a draft (or, without content, the stored template) with sample values, going through
379
+ // the same write gates and the same mail renderer as a real send. Nothing is stored or sent.
380
+ export async function renderTemplatePreview(ctx, user, input) {
381
+ const draftSource = await resolveDraftSource(ctx, user, input);
382
+ if (draftSource === null)
383
+ return failed(new NotFoundError("template-resource", input.slug));
384
+ const { content: draftContent, meta: draftMeta, variableSchema: draftVariableSchema, } = draftSource;
385
+ const { response: validation, evaluation } = await validateTemplateDraft(ctx, user, {
386
+ slug: input.slug,
387
+ kind: input.kind,
388
+ contentFormat: "html",
389
+ content: draftContent,
390
+ meta: draftMeta,
391
+ variableSchema: draftVariableSchema,
392
+ locale: input.locale,
393
+ scope: "tenant",
394
+ });
395
+ if (!evaluation.ok)
396
+ return failed(new ValidationError({ fields: [...evaluation.issues] }));
397
+ const content = evaluation.content ?? "";
398
+ const meta = parseTemplateMeta(input.kind, parseStoredJson(evaluation.metaJson));
399
+ const sampleValues = sampleValuesFor(validation.variables);
400
+ if (input.kind === "document-pdf") {
401
+ return renderDocumentPreview(ctx, user, {
402
+ input,
403
+ content,
404
+ meta,
405
+ variableSchema: draftVariableSchema,
406
+ sampleValues,
407
+ validation,
408
+ });
409
+ }
410
+ return renderMailPreview(ctx, user, {
411
+ input,
412
+ content,
413
+ meta,
414
+ variableSchema: draftVariableSchema,
415
+ sampleValues,
416
+ validation,
417
+ });
418
+ }
@@ -0,0 +1,19 @@
1
+ import type { HandlerContext, SessionUser, TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
2
+ import type { LinkedResourceEntry } from "./linked-resources.js";
3
+ export type TemplateResourceFile = {
4
+ readonly fileName: string;
5
+ readonly contentType: string;
6
+ readonly bytes: Uint8Array;
7
+ };
8
+ export declare function readTemplateResource(ctx: Pick<HandlerContext, "db" | "files" | "user">, fileRefId: string): Promise<TemplateResourceFile | null>;
9
+ export declare function duplicateLinkedResourceFiles(ctx: HandlerContext, args: {
10
+ readonly user: SessionUser;
11
+ readonly tenantId: TenantId;
12
+ readonly resources: Readonly<Record<string, LinkedResourceEntry>>;
13
+ }): Promise<WriteResult<Record<string, LinkedResourceEntry>>>;
14
+ export declare function attachTemplateResources(ctx: Pick<HandlerContext, "forTenant">, args: {
15
+ readonly user: SessionUser;
16
+ readonly tenantId: TenantId;
17
+ readonly templateId: string;
18
+ readonly resources: Readonly<Record<string, LinkedResourceEntry>>;
19
+ }): Promise<WriteResult<null>>;
@@ -0,0 +1,73 @@
1
+ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import { NotFoundError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
3
+ import { createFileRefFromBytes, fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
4
+ import { fileContextForTenant } from "../file-foundation/index.js";
5
+ import { requireForTenant } from "../shared/index.js";
6
+ import { executor } from "./handlers/shared.js";
7
+ import { templateResourcesTable } from "./table.js";
8
+ // Reads the file behind a template resource. Null when the fileRef is unknown
9
+ // to the caller's tenant or has no bytes.
10
+ export async function readTemplateResource(ctx, fileRefId) {
11
+ if (!ctx.files)
12
+ return null;
13
+ const row = await fetchOne(ctx.db, fileRefsTable, {
14
+ id: fileRefId,
15
+ isDeleted: false,
16
+ });
17
+ if (!row)
18
+ return null;
19
+ const handle = ctx.files.refFromFileRef(row);
20
+ if (!(await handle.exists()))
21
+ return null;
22
+ return { fileName: row.fileName, contentType: row.mimeType, bytes: await handle.read() };
23
+ }
24
+ // Gives the target tenant its own copy of every file behind the entries, so a template that was
25
+ // copied from the system default keeps working once the tenant exports it or measures its
26
+ // attachments. Rendered-PDF links carry no file and pass through. An entry whose source file is
27
+ // gone is left out: it could not be resolved at render time either.
28
+ export async function duplicateLinkedResourceFiles(ctx, args) {
29
+ const { user, tenantId } = args;
30
+ const { db } = requireForTenant(ctx, tenantId);
31
+ const copies = {};
32
+ for (const [key, entry] of Object.entries(args.resources)) {
33
+ if (typeof entry !== "string" && entry.kind === "rendered-pdf") {
34
+ copies[key] = entry;
35
+ continue;
36
+ }
37
+ const sourceFileRefId = typeof entry === "string" ? entry : entry.fileRefId;
38
+ const file = await readTemplateResource(ctx, sourceFileRefId);
39
+ // skip: the source file is gone, there is nothing to copy
40
+ if (file === null)
41
+ continue;
42
+ const created = await createFileRefFromBytes({
43
+ db,
44
+ files: fileContextForTenant(ctx, ctx.user.tenantId, tenantId),
45
+ registry: ctx.registry,
46
+ user: { ...user, tenantId },
47
+ }, { fileName: file.fileName, mimeType: file.contentType, data: file.bytes });
48
+ if (!created.isSuccess)
49
+ return created;
50
+ copies[key] =
51
+ typeof entry === "string" ? created.data.id : { ...entry, fileRefId: created.data.id };
52
+ }
53
+ return { isSuccess: true, data: copies };
54
+ }
55
+ // Replaces the whole key map of one row with files that are already stored (bundle import,
56
+ // copy-locale). Single keys go through storeTemplateResource and resource-remove.
57
+ export async function attachTemplateResources(ctx, args) {
58
+ if (Object.keys(args.resources).length === 0)
59
+ return { isSuccess: true, data: null };
60
+ const { db, streamTenantId } = requireForTenant(ctx, args.tenantId);
61
+ const row = await fetchOne(db, templateResourcesTable, {
62
+ id: args.templateId,
63
+ tenantId: args.tenantId,
64
+ });
65
+ if (!row)
66
+ return writeFailure(new NotFoundError("template-resource", args.templateId));
67
+ const result = await executor.update({
68
+ id: row.id,
69
+ version: row.version,
70
+ changes: { linkedResources: JSON.stringify(args.resources) },
71
+ }, args.user, db, { streamTenantId });
72
+ return result.isSuccess ? { isSuccess: true, data: null } : result;
73
+ }
@@ -0,0 +1,16 @@
1
+ import { type Registry } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { type TemplateKind } from "./constants.js";
3
+ type UsageRole = "template" | "layout";
4
+ export type TemplateUsageEntry = {
5
+ readonly qualifiedName: string;
6
+ readonly feature: string;
7
+ readonly notification: string;
8
+ readonly trigger: string | null;
9
+ readonly role: UsageRole;
10
+ };
11
+ /** Layout slug of the effective stored mail template, by notification qualified
12
+ * name and locale. The renderer reads the layout from the stored row's meta,
13
+ * so it wins over the code-registered notification meta. */
14
+ export type EffectiveMailLayoutSlugs = ReadonlyMap<string, ReadonlyMap<string, string>>;
15
+ export declare function collectTemplateUsage(registry: Registry, slug: string, kind: TemplateKind, effectiveLayouts?: EffectiveMailLayoutSlugs): TemplateUsageEntry[];
16
+ export {};
@@ -0,0 +1,35 @@
1
+ import { parseQn, } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { DEFAULT_MAIL_LAYOUT_SLUG } from "./constants.js";
3
+ function layoutSlugsOf(qualifiedName, notification, effectiveLayouts) {
4
+ const stored = effectiveLayouts.get(qualifiedName);
5
+ const locales = new Set([
6
+ ...Object.keys(notification.template ?? {}),
7
+ ...Object.keys(notification.meta ?? {}),
8
+ ...(stored?.keys() ?? []),
9
+ ]);
10
+ return [...locales].map((locale) => stored?.get(locale) ?? notification.meta?.[locale]?.layoutSlug ?? DEFAULT_MAIL_LAYOUT_SLUG);
11
+ }
12
+ export function collectTemplateUsage(registry, slug, kind, effectiveLayouts = new Map()) {
13
+ const usedBy = [];
14
+ if (kind === "mail" || kind === "mail-layout") {
15
+ for (const [qualifiedName, notification] of registry.getAllNotifications()) {
16
+ const role = kind === "mail" ? "template" : "layout";
17
+ // Notifications without a code template are not rendered through the resolver.
18
+ const isUsed = kind === "mail"
19
+ ? qualifiedName === slug && notification.template !== undefined
20
+ : layoutSlugsOf(qualifiedName, notification, effectiveLayouts).includes(slug);
21
+ if (!isUsed)
22
+ continue;
23
+ const parsed = parseQn(qualifiedName);
24
+ usedBy.push({
25
+ qualifiedName,
26
+ feature: parsed.scope,
27
+ notification: parsed.name,
28
+ trigger: notification.trigger?.on ?? null,
29
+ role,
30
+ });
31
+ }
32
+ }
33
+ usedBy.sort((a, b) => a.qualifiedName.localeCompare(b.qualifiedName));
34
+ return usedBy;
35
+ }
@@ -0,0 +1,22 @@
1
+ import type { Registry, TemplateVariables } from "@cosmicdrift/kumiko-framework/engine";
2
+ import type { TemplateMeta } from "./template-meta.js";
3
+ export declare const SYSTEM_PLACEHOLDERS: ReadonlySet<string>;
4
+ export declare function systemPlaceholdersFor(kind: string): ReadonlySet<string>;
5
+ export type TemplateValidation = {
6
+ readonly unknown: readonly string[];
7
+ readonly missingRequired: readonly string[];
8
+ readonly used: readonly string[];
9
+ /** data-resource keys with no uploaded file; a warning, never a blocker. */
10
+ readonly unknownResources: readonly string[];
11
+ };
12
+ export declare function resolveTemplateVariables(registry: Registry, slug: string, variableSchema: Readonly<Record<string, unknown>>): TemplateVariables;
13
+ export declare function variablesFromSchema(variableSchema: Readonly<Record<string, unknown>>): TemplateVariables;
14
+ export declare function validateTemplatePlaceholders(input: {
15
+ readonly kind: string;
16
+ readonly body: string;
17
+ readonly meta: TemplateMeta | null;
18
+ readonly variables: TemplateVariables;
19
+ /** data-resource keys with an uploaded file besides the built-in `logo`. */
20
+ readonly resourceKeys?: readonly string[];
21
+ }): TemplateValidation;
22
+ export declare function hasPlaceholderProblems(validation: TemplateValidation): boolean;