@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,173 @@
1
+ import * as z from "zod";
2
+ import { withParsedDocument } from "./document-sanitizer.js";
3
+ import { isDocumentPlaceholderHref, isDocumentReferenceName } from "./document-schema.js";
4
+ export const SYSTEM_PLACEHOLDERS = new Set([
5
+ "tenantName",
6
+ "signature",
7
+ "imprint",
8
+ "unsubscribeUrl",
9
+ ]);
10
+ const PAGE_PLACEHOLDERS = ["page", "pages"];
11
+ const PAGED_KINDS = new Set(["document-pdf", "pdf-layout"]);
12
+ export function systemPlaceholdersFor(kind) {
13
+ return PAGED_KINDS.has(kind)
14
+ ? new Set([...SYSTEM_PLACEHOLDERS, ...PAGE_PLACEHOLDERS])
15
+ : SYSTEM_PLACEHOLDERS;
16
+ }
17
+ const BASE_RESOURCE_KEYS = ["logo"];
18
+ // Mirrors TemplateVariableDef: a stored or draft schema keeps its label and example, which the
19
+ // preview uses as sample values.
20
+ const variableCommon = {
21
+ label: z.string().optional(),
22
+ required: z.boolean().optional(),
23
+ };
24
+ const textLikeExample = z.string().optional();
25
+ const scalarVariableSchema = z.discriminatedUnion("type", [
26
+ z.object({ ...variableCommon, type: z.literal("text"), example: textLikeExample }),
27
+ z.object({ ...variableCommon, type: z.literal("link"), example: textLikeExample }),
28
+ z.object({ ...variableCommon, type: z.literal("richText"), example: textLikeExample }),
29
+ z.object({
30
+ ...variableCommon,
31
+ type: z.literal("money"),
32
+ example: z
33
+ .union([z.number(), z.object({ amount: z.number(), currency: z.string() })])
34
+ .optional(),
35
+ }),
36
+ z.object({ ...variableCommon, type: z.literal("date"), example: textLikeExample }),
37
+ ]);
38
+ const variableDefSchema = z.union([
39
+ scalarVariableSchema,
40
+ z.object({
41
+ ...variableCommon,
42
+ type: z.literal("list"),
43
+ item: z.record(z.string(), scalarVariableSchema),
44
+ example: z.array(z.record(z.string(), z.unknown())).optional(),
45
+ }),
46
+ ]);
47
+ // A stored variableSchema value is either a bare string (legacy "this is a text
48
+ // variable") or a TemplateVariableDef object; anything else is not declared and
49
+ // therefore shows up as an unknown placeholder instead of being guessed at.
50
+ function toVariableDef(raw) {
51
+ if (typeof raw === "string")
52
+ return { type: "text" };
53
+ const parsed = variableDefSchema.safeParse(raw);
54
+ return parsed.success ? parsed.data : null;
55
+ }
56
+ export function resolveTemplateVariables(registry, slug, variableSchema) {
57
+ const declared = registry.getAllNotifications().get(slug)?.variables;
58
+ return declared ?? variablesFromSchema(variableSchema);
59
+ }
60
+ export function variablesFromSchema(variableSchema) {
61
+ const variables = {};
62
+ for (const [name, raw] of Object.entries(variableSchema)) {
63
+ const def = toVariableDef(raw);
64
+ if (def !== null)
65
+ variables[name] = def;
66
+ }
67
+ return variables;
68
+ }
69
+ function rootName(name) {
70
+ return name.split(".")[0] ?? name;
71
+ }
72
+ function listItemFields(variables, listName) {
73
+ const def = listName !== null && Object.hasOwn(variables, listName) ? variables[listName] : undefined;
74
+ return def?.type === "list" ? def.item : {};
75
+ }
76
+ function isKnownName(name, scope, itemFields) {
77
+ const { variables } = scope;
78
+ const root = rootName(name);
79
+ if (Object.hasOwn(itemFields, name) || Object.hasOwn(itemFields, root)) {
80
+ return { known: true, used: "" };
81
+ }
82
+ if (scope.systemPlaceholders.has(root))
83
+ return { known: true, used: "" };
84
+ if (Object.hasOwn(variables, name))
85
+ return { known: true, used: name };
86
+ if (Object.hasOwn(variables, root))
87
+ return { known: true, used: root };
88
+ return { known: false, used: "" };
89
+ }
90
+ function recordName(name, itemFields, scope) {
91
+ const resolution = isKnownName(name, scope, itemFields);
92
+ if (!resolution.known)
93
+ scope.unknown.add(name);
94
+ else if (resolution.used !== "")
95
+ scope.used.add(resolution.used);
96
+ }
97
+ function enclosingEachName(element) {
98
+ return element.closest("tr[data-each]")?.getAttribute("data-each") ?? null;
99
+ }
100
+ function scanBodyPlaceholders(body, scope) {
101
+ withParsedDocument(body, (root) => {
102
+ for (const element of Array.from(root.querySelectorAll("tr[data-each]"))) {
103
+ const listName = element.getAttribute("data-each") ?? "";
104
+ const def = Object.hasOwn(scope.variables, listName) ? scope.variables[listName] : undefined;
105
+ if (def?.type === "list")
106
+ scope.used.add(listName);
107
+ else
108
+ scope.unknown.add(listName);
109
+ }
110
+ for (const element of Array.from(root.querySelectorAll("span[data-variable], a[href], img[data-resource]"))) {
111
+ const itemFields = listItemFields(scope.variables, enclosingEachName(element));
112
+ const tag = element.tagName.toLowerCase();
113
+ if (tag === "span") {
114
+ recordName(element.getAttribute("data-variable") ?? "", itemFields, scope);
115
+ }
116
+ else if (tag === "a") {
117
+ const href = element.getAttribute("href") ?? "";
118
+ if (isDocumentPlaceholderHref(href))
119
+ recordName(href.slice(2, -2), itemFields, scope);
120
+ }
121
+ else {
122
+ const key = element.getAttribute("data-resource") ?? "";
123
+ if (!scope.resourceKeys.has(key))
124
+ scope.unknownResources.add(key);
125
+ }
126
+ }
127
+ });
128
+ }
129
+ const HANDLEBARS_EXPRESSION = /\{\{([^{}]*)\}\}/g;
130
+ // Meta text supports only plain `{{name}}` / `{{name.field}}`; every other
131
+ // Handlebars construct is reported as unknown so it can never reach the compiler.
132
+ function scanMetaPlaceholders(text, scope) {
133
+ const leftover = text.replace(HANDLEBARS_EXPRESSION, (_match, inner) => {
134
+ const name = inner.trim();
135
+ if (isDocumentReferenceName(name))
136
+ recordName(name, {}, scope);
137
+ else
138
+ scope.unknown.add(`{{${inner}}}`);
139
+ return "";
140
+ });
141
+ if (/[{}]/.test(leftover))
142
+ scope.unknown.add("{");
143
+ }
144
+ function metaTexts(meta) {
145
+ if (meta === null || !("subject" in meta))
146
+ return [];
147
+ return [meta.subject, ...(meta.previewText === undefined ? [] : [meta.previewText])];
148
+ }
149
+ export function validateTemplatePlaceholders(input) {
150
+ const scope = {
151
+ variables: input.variables,
152
+ systemPlaceholders: systemPlaceholdersFor(input.kind),
153
+ resourceKeys: new Set([...BASE_RESOURCE_KEYS, ...(input.resourceKeys ?? [])]),
154
+ used: new Set(),
155
+ unknown: new Set(),
156
+ unknownResources: new Set(),
157
+ };
158
+ scanBodyPlaceholders(input.body, scope);
159
+ for (const text of metaTexts(input.meta))
160
+ scanMetaPlaceholders(text, scope);
161
+ const missingRequired = Object.entries(input.variables)
162
+ .filter(([name, def]) => def.required === true && !scope.used.has(name))
163
+ .map(([name]) => name);
164
+ return {
165
+ unknown: [...scope.unknown].sort(),
166
+ missingRequired: missingRequired.sort(),
167
+ used: [...scope.used].sort(),
168
+ unknownResources: [...scope.unknownResources].sort(),
169
+ };
170
+ }
171
+ export function hasPlaceholderProblems(validation) {
172
+ return validation.unknown.length > 0 || validation.missingRequired.length > 0;
173
+ }
@@ -37,7 +37,7 @@ export async function assertConsumerHandlesNotFound(consumer, opts) {
37
37
  await consumer.resolve({
38
38
  tenantId,
39
39
  slug,
40
- kind: "mail-html",
40
+ kind: "mail",
41
41
  locale: "de",
42
42
  });
43
43
  }
@@ -60,7 +60,7 @@ export async function assertConsumerRespectsLocaleFallback(consumer, opts) {
60
60
  await seedTemplate(db, {
61
61
  tenantId: SYSTEM_TENANT_ID,
62
62
  slug,
63
- kind: "notification",
63
+ kind: "document-pdf",
64
64
  locale: FALLBACK_LOCALE,
65
65
  scope: "system",
66
66
  content: "conformance-fallback-content",
@@ -68,7 +68,7 @@ export async function assertConsumerRespectsLocaleFallback(consumer, opts) {
68
68
  const result = await consumer.resolve({
69
69
  tenantId,
70
70
  slug,
71
- kind: "notification",
71
+ kind: "document-pdf",
72
72
  locale: "tr",
73
73
  });
74
74
  if (result.locale !== FALLBACK_LOCALE) {
@@ -89,7 +89,7 @@ export async function assertConsumerHandlesMissingResourceKeys(consumer, opts) {
89
89
  await seedTemplate(db, {
90
90
  tenantId: SYSTEM_TENANT_ID,
91
91
  slug,
92
- kind: "mail-html",
92
+ kind: "mail",
93
93
  locale: "de",
94
94
  scope: "system",
95
95
  linkedResources: { logo: "file_missing" },
@@ -97,7 +97,7 @@ export async function assertConsumerHandlesMissingResourceKeys(consumer, opts) {
97
97
  const template = await consumer.resolve({
98
98
  tenantId,
99
99
  slug,
100
- kind: "mail-html",
100
+ kind: "mail",
101
101
  locale: "de",
102
102
  });
103
103
  try {
@@ -49,26 +49,29 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
49
49
  kind: {
50
50
  readonly type: "select";
51
51
  readonly description?: string;
52
- readonly options: readonly ["notification", "mail-html", "document-pdf", "image-snapshot", "text-block", "ai-prompt"];
52
+ readonly options: readonly ["mail", "document-pdf", "image-snapshot", "notification", "text-block", "snippet", "ai-prompt", "mail-layout", "pdf-layout"];
53
53
  readonly required?: boolean;
54
54
  readonly sortable?: boolean;
55
55
  readonly filterable?: boolean;
56
56
  readonly sensitive?: boolean;
57
- readonly default?: "ai-prompt" | "document-pdf" | "image-snapshot" | "mail-html" | "notification" | "text-block" | undefined;
57
+ readonly default?: "ai-prompt" | "document-pdf" | "image-snapshot" | "mail" | "mail-layout" | "notification" | "pdf-layout" | "snippet" | "text-block" | undefined;
58
58
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
59
59
  readonly display?: "radio" | "dropdown";
60
60
  readonly optionTones?: {
61
61
  readonly "ai-prompt"?: "bad" | "neutral" | "ok" | "warn" | undefined;
62
62
  readonly "document-pdf"?: "bad" | "neutral" | "ok" | "warn" | undefined;
63
63
  readonly "image-snapshot"?: "bad" | "neutral" | "ok" | "warn" | undefined;
64
- readonly "mail-html"?: "bad" | "neutral" | "ok" | "warn" | undefined;
64
+ readonly mail?: "bad" | "neutral" | "ok" | "warn" | undefined;
65
+ readonly "mail-layout"?: "bad" | "neutral" | "ok" | "warn" | undefined;
65
66
  readonly notification?: "bad" | "neutral" | "ok" | "warn" | undefined;
67
+ readonly "pdf-layout"?: "bad" | "neutral" | "ok" | "warn" | undefined;
68
+ readonly snippet?: "bad" | "neutral" | "ok" | "warn" | undefined;
66
69
  readonly "text-block"?: "bad" | "neutral" | "ok" | "warn" | undefined;
67
70
  } | undefined;
68
71
  readonly optionsQuery?: string;
69
72
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
70
73
  readonly optionsAvailabilityQuery?: string;
71
- readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"ai-prompt" | "document-pdf" | "image-snapshot" | "mail-html" | "notification" | "text-block">[] | undefined;
74
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"ai-prompt" | "document-pdf" | "image-snapshot" | "mail" | "mail-layout" | "notification" | "pdf-layout" | "snippet" | "text-block">[] | undefined;
72
75
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
73
76
  required: true;
74
77
  };
@@ -232,26 +235,29 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
232
235
  kind: {
233
236
  readonly type: "select";
234
237
  readonly description?: string;
235
- readonly options: readonly ["notification", "mail-html", "document-pdf", "image-snapshot", "text-block", "ai-prompt"];
238
+ readonly options: readonly ["mail", "document-pdf", "image-snapshot", "notification", "text-block", "snippet", "ai-prompt", "mail-layout", "pdf-layout"];
236
239
  readonly required?: boolean;
237
240
  readonly sortable?: boolean;
238
241
  readonly filterable?: boolean;
239
242
  readonly sensitive?: boolean;
240
- readonly default?: "ai-prompt" | "document-pdf" | "image-snapshot" | "mail-html" | "notification" | "text-block" | undefined;
243
+ readonly default?: "ai-prompt" | "document-pdf" | "image-snapshot" | "mail" | "mail-layout" | "notification" | "pdf-layout" | "snippet" | "text-block" | undefined;
241
244
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
242
245
  readonly display?: "radio" | "dropdown";
243
246
  readonly optionTones?: {
244
247
  readonly "ai-prompt"?: "bad" | "neutral" | "ok" | "warn" | undefined;
245
248
  readonly "document-pdf"?: "bad" | "neutral" | "ok" | "warn" | undefined;
246
249
  readonly "image-snapshot"?: "bad" | "neutral" | "ok" | "warn" | undefined;
247
- readonly "mail-html"?: "bad" | "neutral" | "ok" | "warn" | undefined;
250
+ readonly mail?: "bad" | "neutral" | "ok" | "warn" | undefined;
251
+ readonly "mail-layout"?: "bad" | "neutral" | "ok" | "warn" | undefined;
248
252
  readonly notification?: "bad" | "neutral" | "ok" | "warn" | undefined;
253
+ readonly "pdf-layout"?: "bad" | "neutral" | "ok" | "warn" | undefined;
254
+ readonly snippet?: "bad" | "neutral" | "ok" | "warn" | undefined;
249
255
  readonly "text-block"?: "bad" | "neutral" | "ok" | "warn" | undefined;
250
256
  } | undefined;
251
257
  readonly optionsQuery?: string;
252
258
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
253
259
  readonly optionsAvailabilityQuery?: string;
254
- readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"ai-prompt" | "document-pdf" | "image-snapshot" | "mail-html" | "notification" | "text-block">[] | undefined;
260
+ readonly conditionalOptions?: readonly import("@cosmicdrift/kumiko-types/fields").SelectConditionalOptions<"ai-prompt" | "document-pdf" | "image-snapshot" | "mail" | "mail-layout" | "notification" | "pdf-layout" | "snippet" | "text-block">[] | undefined;
255
261
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
256
262
  required: true;
257
263
  };
@@ -9,6 +9,7 @@ export type BlockSummary = {
9
9
  readonly modifiedAt: string | null;
10
10
  };
11
11
  export declare function groupBlocksByFolder(blocks: readonly BlockSummary[], tenantIdOverride?: string, collectionId?: string): readonly TreeNode[];
12
+ export declare function resolveWriteContentFormat(collectionContentFormat: string | undefined, loadedContentFormat: string | undefined): string | undefined;
12
13
  export declare function textBlocksClient(opts?: {
13
14
  readonly navId?: string;
14
15
  readonly tenantId?: string;
@@ -16,6 +16,8 @@ import { ContentPreview, useAppFeatures, useContentEditor, useDispatcher, usePri
16
16
  import { PlainContentEditor, RichContentEditor, } from "@cosmicdrift/kumiko-renderer-web";
17
17
  import { useEffect, useId, useState } from "react";
18
18
  import { collectionHandlerName, collectionQueryName, TemplateResolverHandlers, TemplateResolverQueries, } from "../qualified-names.js";
19
+ import { DocumentBlocksContext, DocumentContentEditor } from "./document-editor.js";
20
+ import { documentBlocksForKind } from "./document-toolbar-blocks.js";
19
21
  import { defaultTranslations } from "./i18n.js";
20
22
  function newFolderNode() {
21
23
  return { leaves: [], subFolders: new Map() };
@@ -163,19 +165,20 @@ function findContentFormat(features, collectionId) {
163
165
  function findVariableExamples(features, collectionId) {
164
166
  return findCollection(features, collectionId)?.variableSchema ?? {};
165
167
  }
166
- // The collection's declared contentFormat ("plain" | "rich" | "markdown",
167
- // see ContentCollectionDefinition.contentFormat) uses a different vocabulary
168
+ // The collection's declared contentFormat ("plain" | "rich" | "markdown" |
169
+ // "document", see ContentCollectionDefinition.contentFormat) uses a different vocabulary
168
170
  // than the stored entry's contentFormat ("plain" | "html" | "markdown" |
169
- // "mjml", see CONTENT_FORMATS) — "rich" always stores HTML. Without this,
171
+ // "mjml", see CONTENT_FORMATS) — "rich" and "document" always store HTML. Without this,
170
172
  // handleSave omitting contentFormat let the write schema's
171
173
  // contentFormatSchema.default("markdown") silently stamp every save as
172
174
  // markdown, clobbering e.g. a "plain" entry. Undefined (no collection, or
173
175
  // the collection didn't declare a format) keeps whatever was already saved
174
176
  // instead of guessing.
175
- function resolveWriteContentFormat(collectionContentFormat, loadedContentFormat) {
177
+ // Exported for the unit test — resolveWriteContentFormat is a pure function.
178
+ export function resolveWriteContentFormat(collectionContentFormat, loadedContentFormat) {
176
179
  if (collectionContentFormat === "plain")
177
180
  return "plain";
178
- if (collectionContentFormat === "rich")
181
+ if (collectionContentFormat === "rich" || collectionContentFormat === "document")
179
182
  return "html";
180
183
  if (collectionContentFormat === "markdown")
181
184
  return "markdown";
@@ -203,6 +206,7 @@ function TextBlockEditor({ target, }) {
203
206
  const contentFormat = findContentFormat(features, collectionId);
204
207
  const variableExamples = findVariableExamples(features, collectionId);
205
208
  const variables = Object.keys(variableExamples);
209
+ const documentBlocks = documentBlocksForKind(findCollection(features, collectionId)?.kind);
206
210
  const ContentEditor = useContentEditor(contentFormat);
207
211
  const canWrite = user?.roles.includes("TenantAdmin") === true || user?.roles.includes("SystemAdmin") === true;
208
212
  // Same split as the tree provider: a node without a collection reads through
@@ -266,7 +270,7 @@ function TextBlockEditor({ target, }) {
266
270
  void handleSave();
267
271
  };
268
272
  const disabled = submitting || loading || !canWrite;
269
- return (_jsxs(Form, { onSubmit: onSubmit, testId: "text-block-editor", title: title || slug || "—", subtitle: locale !== "" ? `${slug} · (${locale})` : slug, actions: canWrite ? (_jsx(Button, { type: "submit", loading: submitting, disabled: disabled, children: submitting ? t("template-resolver.editor.saving") : t("template-resolver.editor.save") })) : undefined, children: [loading && _jsx(Banner, { variant: "loading", children: t("template-resolver.editor.loading") }), loadError !== null && (_jsxs(Banner, { variant: "error", children: [t("template-resolver.editor.loadFailed"), ": ", loadError.code] })), !canWrite && !loading && (_jsx(Banner, { variant: "info", children: t("template-resolver.editor.readOnly") })), _jsx(Field, { id: "text-block-title", label: t("template-resolver.editor.titleLabel"), required: true, children: _jsx(Input, { kind: "text", id: "text-block-title", name: "text-block-title", value: title, onChange: setTitle, disabled: disabled, required: true }) }), _jsx(Field, { id: contentEditorId, label: t("template-resolver.editor.contentLabel"), labelAppendix: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setPreviewing((p) => !p), children: previewing ? t("kumiko.contentEditor.editMode") : t("kumiko.contentEditor.preview") }), children: previewing ? (_jsx(ContentPreview, { content: content, variables: variableExamples, contentFormat: contentFormat })) : (_jsx(ContentEditor, { id: contentEditorId, value: content, onChange: setContent, variables: variables, readOnly: disabled })) }), saveError !== null && _jsx(Banner, { variant: "error", children: saveError }), savedMsg !== null && _jsx(Banner, { variant: "info", children: savedMsg })] }));
273
+ return (_jsxs(Form, { onSubmit: onSubmit, testId: "text-block-editor", title: title || slug || "—", subtitle: locale !== "" ? `${slug} · (${locale})` : slug, actions: canWrite ? (_jsx(Button, { type: "submit", loading: submitting, disabled: disabled, children: submitting ? t("template-resolver.editor.saving") : t("template-resolver.editor.save") })) : undefined, children: [loading && _jsx(Banner, { variant: "loading", children: t("template-resolver.editor.loading") }), loadError !== null && (_jsxs(Banner, { variant: "error", children: [t("template-resolver.editor.loadFailed"), ": ", loadError.code] })), !canWrite && !loading && (_jsx(Banner, { variant: "info", children: t("template-resolver.editor.readOnly") })), _jsx(Field, { id: "text-block-title", label: t("template-resolver.editor.titleLabel"), required: true, children: _jsx(Input, { kind: "text", id: "text-block-title", name: "text-block-title", value: title, onChange: setTitle, disabled: disabled, required: true }) }), _jsx(Field, { id: contentEditorId, label: t("template-resolver.editor.contentLabel"), labelAppendix: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setPreviewing((p) => !p), children: previewing ? t("kumiko.contentEditor.editMode") : t("kumiko.contentEditor.preview") }), children: previewing ? (_jsx(ContentPreview, { content: content, variables: variableExamples, contentFormat: contentFormat })) : (_jsx(DocumentBlocksContext.Provider, { value: documentBlocks, children: _jsx(ContentEditor, { id: contentEditorId, value: content, onChange: setContent, variables: variables, readOnly: disabled }) })) }), saveError !== null && _jsx(Banner, { variant: "error", children: saveError }), savedMsg !== null && _jsx(Banner, { variant: "info", children: savedMsg })] }));
270
274
  }
271
275
  // `navId` = the QN of the r.nav({ provider: true }) node the app registers for
272
276
  // the content tree (e.g. "publicstatus:nav:content"). The app owns the node's
@@ -301,12 +305,13 @@ export function textBlocksClient(opts) {
301
305
  resolvers: {
302
306
  "template-resolver:edit": TextBlockEditor,
303
307
  },
304
- // The default for every "plain" collection (ai-prompt, mail-html without
308
+ // The default for every "plain" collection (ai-prompt, mail without
305
309
  // an explicit rich override) — an app-registered "plain" editor on
306
310
  // another clientFeature still wins, last-wins same as columnRenderers.
307
311
  contentEditors: {
308
312
  plain: PlainContentEditor,
309
313
  rich: RichContentEditor,
314
+ document: DocumentContentEditor,
310
315
  // ponytail: same textarea+chips as "plain" — markdown is stored as
311
316
  // text either way. Shares PlainContentEditor's fixed-DOM-id ceiling
312
317
  // (see its own file comment); swap for a live-preview widget or a
@@ -0,0 +1,18 @@
1
+ import type { Extensions, NodeViewRenderer } from "@tiptap/core";
2
+ import type { DocumentResourcePreview } from "./document-toolbar-blocks.js";
3
+ export declare const DOCUMENT_BLOCK_ATTRIBUTE = "data-document-block";
4
+ export type DocumentEditorLabels = {
5
+ readonly placeholder: string;
6
+ readonly image: string;
7
+ readonly pageBreak: string;
8
+ readonly button?: string;
9
+ /** Canvas block label of an image, e.g. "Image · inline". */
10
+ readonly imageBlock?: string;
11
+ readonly variableLabels?: Readonly<Record<string, string>>;
12
+ readonly systemVariables?: readonly string[];
13
+ readonly resourcePreviews?: Readonly<Record<string, DocumentResourcePreview>>;
14
+ /** Canvas chrome labels every block with its type. */
15
+ readonly blockLabels?: boolean;
16
+ };
17
+ export declare function createVariableView(variableLabels: Readonly<Record<string, string>>, systemVariables: readonly string[], labelOnly?: boolean): NodeViewRenderer;
18
+ export declare function createDocumentEditorExtensions(labels: DocumentEditorLabels): Extensions;
@@ -0,0 +1,145 @@
1
+ import { ListKeymap } from "@tiptap/extension-list";
2
+ import { Dropcursor, Gapcursor, Placeholder, UndoRedo } from "@tiptap/extensions";
3
+ import { DocumentButton, DocumentPageBreak, DocumentResource, DocumentVariable, documentExtensions, } from "../document-schema.js";
4
+ const DOCUMENT_RESOURCE_SIZE_TOKEN = "{size}";
5
+ export const DOCUMENT_BLOCK_ATTRIBUTE = "data-document-block";
6
+ function createElementWithClass(tag, className, text) {
7
+ const element = document.createElement(tag);
8
+ element.className = className;
9
+ if (text !== undefined)
10
+ element.textContent = text;
11
+ return element;
12
+ }
13
+ const CHIP_CLASS = "mx-0.5 inline-flex items-center gap-1 rounded border px-1.5 py-0.5 text-xs";
14
+ const USER_CHIP_CLASS = `${CHIP_CLASS} border-primary/30 bg-primary/10 text-primary`;
15
+ const SYSTEM_CHIP_CLASS = `${CHIP_CLASS} border-input bg-muted text-muted-foreground`;
16
+ // A chip inside a repeat row names an item field; its label is registered as `list.field`.
17
+ function repeatRowListAt(doc, position) {
18
+ if (position === undefined)
19
+ return null;
20
+ const resolved = doc.resolve(position);
21
+ for (let depth = resolved.depth; depth > 0; depth--) {
22
+ const each = resolved.node(depth).attrs["each"];
23
+ if (typeof each === "string" && each !== "")
24
+ return each;
25
+ }
26
+ return null;
27
+ }
28
+ // The chip is display only: the stored node carries just the name, so a
29
+ // label never reaches the document.
30
+ export function createVariableView(variableLabels, systemVariables, labelOnly = false) {
31
+ return ({ node, editor, getPos }) => {
32
+ const name = String(node.attrs["name"]);
33
+ const listName = repeatRowListAt(editor.state.doc, getPos());
34
+ const labelKey = listName === null ? name : `${listName}.${name}`;
35
+ const label = Object.hasOwn(variableLabels, labelKey) ? variableLabels[labelKey] : undefined;
36
+ const dom = createElementWithClass("span", systemVariables.includes(name) ? SYSTEM_CHIP_CLASS : USER_CHIP_CLASS);
37
+ dom.contentEditable = "false";
38
+ dom.append(document.createTextNode(label !== undefined && label !== "" ? label : name));
39
+ if (!labelOnly && label !== undefined && label !== "" && label !== name) {
40
+ dom.append(createElementWithClass("small", "font-mono text-[10px] opacity-75", name));
41
+ }
42
+ return { dom };
43
+ };
44
+ }
45
+ // Same rule the sanitizer's image handling follows: previews never carry a
46
+ // scriptable scheme.
47
+ const PREVIEW_SRC = /^(data:image\/(png|jpeg|gif|webp);|blob:|https:\/\/|\/)/;
48
+ function createBlockLabel(text) {
49
+ const label = createElementWithClass("span", "absolute -top-2 left-2 bg-card px-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", text);
50
+ label.contentEditable = "false";
51
+ return label;
52
+ }
53
+ function createResourceView(labels) {
54
+ return ({ node }) => {
55
+ const key = String(node.attrs["resource"]);
56
+ const previews = labels.resourcePreviews ?? {};
57
+ const preview = Object.hasOwn(previews, key) ? previews[key] : undefined;
58
+ const hasImage = preview !== undefined && PREVIEW_SRC.test(preview.src);
59
+ // A rendered image needs no frame until the node is selected (ProseMirror marks it).
60
+ const dom = createElementWithClass("div", hasImage
61
+ ? "relative my-1 flex items-center gap-2 rounded border border-transparent px-2 py-1 text-sm text-muted-foreground [.ProseMirror-focused_&.ProseMirror-selectednode]:border-dashed [.ProseMirror-focused_&.ProseMirror-selectednode]:border-input"
62
+ : "relative my-3 flex items-center gap-3 rounded border border-dashed border-input px-3 py-3 text-sm text-muted-foreground");
63
+ dom.setAttribute(DOCUMENT_BLOCK_ATTRIBUTE, "");
64
+ dom.contentEditable = "false";
65
+ if (labels.blockLabels) {
66
+ const label = createBlockLabel(labels.imageBlock ?? labels.image);
67
+ if (hasImage)
68
+ label.classList.add("hidden", "[.ProseMirror-focused_.ProseMirror-selectednode_&]:block");
69
+ dom.append(label);
70
+ }
71
+ if (hasImage) {
72
+ const image = createElementWithClass("img", "max-w-[180px] rounded");
73
+ image.src = preview.src;
74
+ image.alt = typeof node.attrs["alt"] === "string" ? node.attrs["alt"] : key;
75
+ dom.append(image);
76
+ // An empty caption means "no caption", not the key.
77
+ const caption = preview.caption ?? key;
78
+ if (caption !== "") {
79
+ const captionElement = createElementWithClass("span", "whitespace-nowrap text-[11px]", caption.replace(` · ${DOCUMENT_RESOURCE_SIZE_TOKEN}`, ""));
80
+ // The size is only known once the browser decoded the image (possibly already, from cache).
81
+ const showSize = () => {
82
+ captionElement.textContent = caption.replace(DOCUMENT_RESOURCE_SIZE_TOKEN, `${image.naturalWidth} × ${image.naturalHeight} px`);
83
+ };
84
+ image.addEventListener("load", showSize);
85
+ if (image.complete && image.naturalWidth > 0)
86
+ showSize();
87
+ dom.append(captionElement);
88
+ }
89
+ }
90
+ else {
91
+ dom.append(createElementWithClass("span", "font-medium", labels.image), createElementWithClass("code", "text-xs", key));
92
+ }
93
+ return { dom };
94
+ };
95
+ }
96
+ function createButtonView(buttonLabel) {
97
+ return () => {
98
+ const dom = createElementWithClass("div", "relative my-3 rounded border border-dashed border-input p-3");
99
+ dom.setAttribute(DOCUMENT_BLOCK_ATTRIBUTE, "");
100
+ const button = document.createElement("a");
101
+ button.setAttribute("data-button", "");
102
+ dom.append(createBlockLabel(buttonLabel), button);
103
+ return { dom, contentDOM: button };
104
+ };
105
+ }
106
+ function createPageBreakView(pageBreakLabel) {
107
+ return () => {
108
+ const dom = createElementWithClass("div", "my-3 border-t border-dashed border-input pt-1 text-center text-xs uppercase tracking-wide text-muted-foreground", pageBreakLabel);
109
+ dom.contentEditable = "false";
110
+ return { dom };
111
+ };
112
+ }
113
+ // Replaced by name, not appended: the schema must keep exactly one node per
114
+ // name, and only the editor view differs from the shared document schema.
115
+ export function createDocumentEditorExtensions(labels) {
116
+ const base = documentExtensions.map((extension) => {
117
+ switch (extension.name) {
118
+ case DocumentVariable.name:
119
+ return DocumentVariable.extend({
120
+ addNodeView: () => createVariableView(labels.variableLabels ?? {}, labels.systemVariables ?? [], labels.blockLabels === true),
121
+ });
122
+ case DocumentResource.name:
123
+ return DocumentResource.extend({ addNodeView: () => createResourceView(labels) });
124
+ case DocumentButton.name:
125
+ return labels.blockLabels
126
+ ? DocumentButton.extend({ addNodeView: () => createButtonView(labels.button ?? "") })
127
+ : extension;
128
+ case DocumentPageBreak.name:
129
+ return DocumentPageBreak.extend({
130
+ addNodeView: () => createPageBreakView(labels.pageBreak),
131
+ });
132
+ default:
133
+ return extension;
134
+ }
135
+ });
136
+ // No TrailingNode: it appends an empty paragraph that breaks the HTML roundtrip.
137
+ return [
138
+ ...base,
139
+ UndoRedo,
140
+ Gapcursor,
141
+ Dropcursor,
142
+ ListKeymap,
143
+ Placeholder.configure({ placeholder: labels.placeholder }),
144
+ ];
145
+ }
@@ -0,0 +1,4 @@
1
+ import { type ReactNode } from "react";
2
+ import type { DocumentEditorProps, DocumentToolbarBlock } from "./document-toolbar-blocks.js";
3
+ export declare const DocumentBlocksContext: import("react").Context<readonly DocumentToolbarBlock[] | undefined>;
4
+ export declare function DocumentContentEditor(props: DocumentEditorProps): ReactNode;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // @runtime client
3
+ //
4
+ // Lazy boundary: tiptap only loads when a "document" collection is rendered.
5
+ // Never import document-tiptap-editor or document-editor-extensions statically here.
6
+ import { TextareaContentEditor } from "@cosmicdrift/kumiko-renderer";
7
+ import { createContext, lazy, Suspense, useContext } from "react";
8
+ const LazyDocumentTiptapEditor = lazy(() => import("./document-tiptap-editor.js"));
9
+ // The editor registry only hands ContentEditorProps to an editor, so the
10
+ // collection's block set (mail vs. PDF) reaches it through this context. An
11
+ // explicit `blocks` prop still wins.
12
+ export const DocumentBlocksContext = createContext(undefined);
13
+ export function DocumentContentEditor(props) {
14
+ const contextBlocks = useContext(DocumentBlocksContext);
15
+ const editorProps = props.blocks === undefined ? { ...props, blocks: contextBlocks } : props;
16
+ return (_jsx(Suspense, { fallback: _jsx(TextareaContentEditor, { id: props.id, value: props.value, onChange: props.onChange, variables: props.variables, readOnly: props.readOnly }), children: _jsx(LazyDocumentTiptapEditor, { ...editorProps }) }));
17
+ }
@@ -0,0 +1,3 @@
1
+ import { type ReactNode } from "react";
2
+ import { type DocumentEditorProps } from "./document-toolbar-blocks.js";
3
+ export default function DocumentTiptapEditor({ id, value, onChange, variables, readOnly, blocks, variableLabels, systemVariables, resourcePreviews, chrome, toolbar, renderPaper, toolbarHost, onEditorReady, }: DocumentEditorProps): ReactNode;