@cosmicdrift/kumiko-bundled-features 0.363.0 → 0.365.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 (361) hide show
  1. package/dist/audit/feature.js +1 -1
  2. package/dist/audit/handlers/list.query.js +9 -5
  3. package/dist/audit/run-escape-hatch-retention.d.ts +1 -0
  4. package/dist/audit/run-escape-hatch-retention.js +39 -12
  5. package/dist/auth-email-password/bootstrap.d.ts +9 -8
  6. package/dist/auth-email-password/bootstrap.js +12 -4
  7. package/dist/auth-email-password/email-rate-limits.d.ts +4 -0
  8. package/dist/auth-email-password/email-rate-limits.js +13 -0
  9. package/dist/auth-email-password/feature.js +2 -0
  10. package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
  11. package/dist/auth-email-password/handlers/change-password.write.js +6 -2
  12. package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
  13. package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
  14. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +2 -3
  15. package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
  16. package/dist/auth-email-password/handlers/invite-create.write.d.ts +4 -5
  17. package/dist/auth-email-password/handlers/invite-create.write.js +4 -1
  18. package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
  19. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  20. package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
  21. package/dist/auth-email-password/handlers/logout.write.js +6 -2
  22. package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
  23. package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
  24. package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
  25. package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
  26. package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
  27. package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
  28. package/dist/auth-email-password/handlers/signup-request.write.js +2 -1
  29. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +2 -1
  30. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -1
  31. package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
  32. package/dist/auth-email-password/handlers/token-request-handler.js +2 -1
  33. package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
  34. package/dist/auth-email-password/magic-link-mail.js +6 -3
  35. package/dist/auth-email-password/mail-i18n.d.ts +3 -0
  36. package/dist/auth-email-password/mail-i18n.js +10 -0
  37. package/dist/auth-email-password/web/auth-gate.js +16 -4
  38. package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
  39. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
  40. package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
  41. package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
  42. package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
  43. package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
  44. package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
  45. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +52 -5
  46. package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +3 -0
  47. package/dist/billing-foundation/consumer-protection/consent-gate.js +21 -2
  48. package/dist/billing-foundation/consumer-protection/mail-notifications.js +1 -1
  49. package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
  50. package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +5 -3
  51. package/dist/billing-foundation/consumer-protection/termination-mail.js +1 -2
  52. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +6 -0
  53. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +34 -0
  54. package/dist/billing-foundation/consumer-protection/termination-record.d.ts +2 -0
  55. package/dist/billing-foundation/consumer-protection/termination-record.js +15 -2
  56. package/dist/billing-foundation/consumer-protection/termination-request.js +30 -20
  57. package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
  58. package/dist/billing-foundation/consumer-protection/termination-texts.js +6 -0
  59. package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +10 -0
  60. package/dist/billing-foundation/consumer-protection/withdrawal-period.js +26 -0
  61. package/dist/billing-foundation/handlers/create-checkout-session.write.js +11 -5
  62. package/dist/billing-foundation/handlers/process-event.write.js +10 -6
  63. package/dist/billing-foundation/i18n.js +9 -0
  64. package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
  65. package/dist/billing-foundation/subscription-tier-sync.js +21 -7
  66. package/dist/billing-foundation/types.d.ts +7 -2
  67. package/dist/cap-counter/book-cap-usage.js +8 -0
  68. package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
  69. package/dist/cap-counter/cap-reservation.js +7 -3
  70. package/dist/cap-counter/enforce-cap.d.ts +18 -1
  71. package/dist/cap-counter/enforce-cap.js +49 -6
  72. package/dist/cap-counter/handlers/increment-rolling.write.d.ts +1 -0
  73. package/dist/cap-counter/handlers/increment-rolling.write.js +2 -0
  74. package/dist/cap-counter/index.d.ts +1 -1
  75. package/dist/cap-counter/index.js +1 -1
  76. package/dist/cap-counter/with-cap-enforcement.d.ts +2 -0
  77. package/dist/cap-overview/handlers/tenant-caps-list.query.js +3 -1
  78. package/dist/cap-overview/handlers/tenant-options.query.js +3 -1
  79. package/dist/channel-discord/discord-channel.js +3 -2
  80. package/dist/channel-email/email-channel.js +40 -23
  81. package/dist/channel-email/smtp-transport.js +6 -1
  82. package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
  83. package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
  84. package/dist/channel-slack/slack-channel.js +3 -2
  85. package/dist/channel-teams/teams-channel.js +3 -2
  86. package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
  87. package/dist/config/handlers/reset.write.d.ts +1 -1
  88. package/dist/config/handlers/set.write.d.ts +1 -1
  89. package/dist/config/handlers/set.write.js +2 -2
  90. package/dist/config/resolver.d.ts +1 -0
  91. package/dist/config/resolver.js +11 -14
  92. package/dist/config/write-helpers.d.ts +2 -0
  93. package/dist/config/write-helpers.js +19 -0
  94. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
  95. package/dist/crypto-shredding/handlers/forget-subject.write.js +30 -20
  96. package/dist/custom-fields/db/queries/retention.d.ts +1 -1
  97. package/dist/custom-fields/db/queries/retention.js +2 -1
  98. package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
  99. package/dist/data-retention/run-retention-cleanup.js +4 -5
  100. package/dist/delivery/attachment-ref.d.ts +2 -0
  101. package/dist/delivery/attachment-ref.js +30 -1
  102. package/dist/delivery/channel-context.js +19 -1
  103. package/dist/delivery/delivery-service.js +71 -82
  104. package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
  105. package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
  106. package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
  107. package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
  108. package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
  109. package/dist/delivery/index.d.ts +1 -1
  110. package/dist/delivery/index.js +1 -1
  111. package/dist/delivery/jobs.js +43 -11
  112. package/dist/delivery/types.d.ts +7 -1
  113. package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
  114. package/dist/feature-toggles/toggle-runtime.d.ts +5 -3
  115. package/dist/feature-toggles/toggle-runtime.js +25 -12
  116. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  117. package/dist/file-foundation/read-file-ref-content.d.ts +1 -1
  118. package/dist/file-foundation/read-file-ref-content.js +6 -4
  119. package/dist/files-tenant-data/hooks.js +4 -0
  120. package/dist/files-tenant-data/index.d.ts +1 -0
  121. package/dist/files-tenant-data/index.js +2 -1
  122. package/dist/form-draft/db/queries/owned-file-refs.d.ts +1 -1
  123. package/dist/form-draft/db/queries/owned-file-refs.js +9 -2
  124. package/dist/form-draft/handlers/discard.write.d.ts +1 -1
  125. package/dist/form-draft/handlers/discard.write.js +1 -1
  126. package/dist/form-draft/handlers/save.write.d.ts +1 -1
  127. package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
  128. package/dist/inbound-mail-foundation/watch-supervisor.js +3 -1
  129. package/dist/jobs/db/queries/stale-run-sweep.d.ts +1 -1
  130. package/dist/jobs/db/queries/stale-run-sweep.js +12 -5
  131. package/dist/jobs/feature.js +5 -3
  132. package/dist/jobs/handlers/list.query.js +3 -1
  133. package/dist/jobs/handlers/retry.write.d.ts +1 -1
  134. package/dist/jobs/handlers/stale-run-sweep.job.d.ts +1 -1
  135. package/dist/jobs/handlers/stale-run-sweep.job.js +2 -2
  136. package/dist/jobs/handlers/trigger.write.d.ts +1 -1
  137. package/dist/jobs/job-run-logger.js +35 -10
  138. package/dist/jobs/tenant-job-run-table.js +3 -2
  139. package/dist/ledger/entity.js +3 -1
  140. package/dist/managed-pages/handlers/set.write.d.ts +1 -1
  141. package/dist/metrics/filters.d.ts +16 -1
  142. package/dist/metrics/filters.js +27 -11
  143. package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
  144. package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
  145. package/dist/personal-access-tokens/revoke-for-user.js +3 -7
  146. package/dist/renderer-foundation/types.d.ts +2 -0
  147. package/dist/renderer-pdf/render-table-pdf.d.ts +27 -33
  148. package/dist/renderer-pdf/render-table-pdf.js +95 -34
  149. package/dist/secrets/feature.js +4 -3
  150. package/dist/secrets/handlers/delete.write.d.ts +1 -1
  151. package/dist/secrets/handlers/delete.write.js +3 -1
  152. package/dist/secrets/handlers/set.write.d.ts +1 -1
  153. package/dist/secrets/secrets-context.js +15 -3
  154. package/dist/secrets/write-gate.d.ts +3 -1
  155. package/dist/secrets/write-gate.js +6 -3
  156. package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
  157. package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
  158. package/dist/sessions/handlers/revoke.write.d.ts +1 -1
  159. package/dist/shared/chunked-entity-migration.d.ts +1 -0
  160. package/dist/shared/chunked-entity-migration.js +1 -1
  161. package/dist/shared/index.d.ts +1 -0
  162. package/dist/shared/index.js +1 -0
  163. package/dist/shared/mail-template-body.d.ts +3 -0
  164. package/dist/shared/mail-template-body.js +10 -0
  165. package/dist/shared/resource-image-formats.d.ts +23 -0
  166. package/dist/shared/resource-image-formats.js +21 -0
  167. package/dist/shared/sharp-input-limits.js +7 -8
  168. package/dist/step-dispatcher/webhook-runner.js +2 -2
  169. package/dist/subscription-stripe/plugin-methods.js +18 -8
  170. package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
  171. package/dist/template-resolver/api.js +3 -2
  172. package/dist/template-resolver/compile-document-body.d.ts +7 -0
  173. package/dist/template-resolver/compile-document-body.js +114 -21
  174. package/dist/template-resolver/compile-steps-block.d.ts +10 -0
  175. package/dist/template-resolver/compile-steps-block.js +45 -0
  176. package/dist/template-resolver/constants.d.ts +6 -3
  177. package/dist/template-resolver/constants.js +13 -2
  178. package/dist/template-resolver/document-sanitizer.js +3 -0
  179. package/dist/template-resolver/document-schema.d.ts +3 -2
  180. package/dist/template-resolver/document-schema.js +29 -4
  181. package/dist/template-resolver/document-steps.d.ts +17 -0
  182. package/dist/template-resolver/document-steps.js +93 -0
  183. package/dist/template-resolver/feature.d.ts +2 -0
  184. package/dist/template-resolver/feature.js +13 -1
  185. package/dist/template-resolver/handlers/bundle-import.d.ts +7 -1
  186. package/dist/template-resolver/handlers/bundle-import.js +63 -38
  187. package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
  188. package/dist/template-resolver/handlers/collection-set.write.js +4 -3
  189. package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
  190. package/dist/template-resolver/handlers/copy-locale.write.js +95 -49
  191. package/dist/template-resolver/handlers/diff-to-system.query.js +5 -38
  192. package/dist/template-resolver/handlers/discard-draft.write.d.ts +13 -0
  193. package/dist/template-resolver/handlers/discard-draft.write.js +37 -0
  194. package/dist/template-resolver/handlers/export-bundle.query.js +51 -1
  195. package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -0
  196. package/dist/template-resolver/handlers/find-by-id.query.js +6 -3
  197. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
  198. package/dist/template-resolver/handlers/import-bundle-system.write.js +16 -5
  199. package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
  200. package/dist/template-resolver/handlers/import-bundle.write.js +11 -1
  201. package/dist/template-resolver/handlers/layout-source.query.d.ts +5 -0
  202. package/dist/template-resolver/handlers/layout-source.query.js +46 -23
  203. package/dist/template-resolver/handlers/list.query.js +4 -1
  204. package/dist/template-resolver/handlers/override-diff.d.ts +20 -0
  205. package/dist/template-resolver/handlers/override-diff.js +41 -0
  206. package/dist/template-resolver/handlers/overview.query.d.ts +9 -0
  207. package/dist/template-resolver/handlers/overview.query.js +60 -12
  208. package/dist/template-resolver/handlers/reset-to-system.write.d.ts +1 -1
  209. package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +1 -1
  210. package/dist/template-resolver/handlers/resource-remove.write.d.ts +1 -1
  211. package/dist/template-resolver/handlers/resource-upload.write.d.ts +2 -2
  212. package/dist/template-resolver/handlers/resource-upload.write.js +36 -6
  213. package/dist/template-resolver/handlers/send-test.write.d.ts +3 -1
  214. package/dist/template-resolver/handlers/send-test.write.js +37 -10
  215. package/dist/template-resolver/handlers/set.write.d.ts +1 -1
  216. package/dist/template-resolver/handlers/template-gates.js +13 -2
  217. package/dist/template-resolver/handlers/template-write.js +36 -4
  218. package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +32 -0
  219. package/dist/template-resolver/handlers/tenant-overrides.query.js +80 -16
  220. package/dist/template-resolver/handlers/toggle-status.write.d.ts +17 -4
  221. package/dist/template-resolver/handlers/toggle-status.write.js +35 -19
  222. package/dist/template-resolver/handlers/upsert-system.write.d.ts +1 -1
  223. package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +1 -1
  224. package/dist/template-resolver/handlers/upsert-tenant.write.js +1 -1
  225. package/dist/template-resolver/handlers/usage.query.js +11 -2
  226. package/dist/template-resolver/handlers/validate.query.js +4 -1
  227. package/dist/template-resolver/index.d.ts +5 -4
  228. package/dist/template-resolver/index.js +5 -4
  229. package/dist/template-resolver/layout-appearance.d.ts +10 -0
  230. package/dist/template-resolver/layout-appearance.js +36 -0
  231. package/dist/template-resolver/mail-constants.d.ts +2 -0
  232. package/dist/template-resolver/mail-constants.js +3 -0
  233. package/dist/template-resolver/pdf-constants.d.ts +7 -0
  234. package/dist/template-resolver/pdf-constants.js +8 -0
  235. package/dist/template-resolver/qualified-names.d.ts +4 -0
  236. package/dist/template-resolver/qualified-names.js +4 -0
  237. package/dist/template-resolver/resource-image.d.ts +2 -9
  238. package/dist/template-resolver/resource-image.js +2 -11
  239. package/dist/template-resolver/seeding.js +23 -4
  240. package/dist/template-resolver/store-template-resource.d.ts +53 -9
  241. package/dist/template-resolver/store-template-resource.js +250 -97
  242. package/dist/template-resolver/table.d.ts +103 -0
  243. package/dist/template-resolver/table.js +9 -2
  244. package/dist/template-resolver/template-bundle.js +4 -0
  245. package/dist/template-resolver/template-draft.d.ts +54 -0
  246. package/dist/template-resolver/template-draft.js +72 -0
  247. package/dist/template-resolver/template-meta.d.ts +35 -6
  248. package/dist/template-resolver/template-meta.js +67 -16
  249. package/dist/template-resolver/template-preview.d.ts +19 -2
  250. package/dist/template-resolver/template-preview.js +98 -55
  251. package/dist/template-resolver/template-resources.js +3 -4
  252. package/dist/template-resolver/template-usage.d.ts +5 -1
  253. package/dist/template-resolver/template-usage.js +14 -9
  254. package/dist/template-resolver/template-validation.js +13 -9
  255. package/dist/template-resolver/web/client-plugin.js +4 -2
  256. package/dist/template-resolver/web/document-editor-extensions.d.ts +5 -1
  257. package/dist/template-resolver/web/document-editor-extensions.js +76 -13
  258. package/dist/template-resolver/web/document-editor.d.ts +2 -1
  259. package/dist/template-resolver/web/document-editor.js +12 -2
  260. package/dist/template-resolver/web/document-tiptap-editor.d.ts +1 -1
  261. package/dist/template-resolver/web/document-tiptap-editor.js +173 -34
  262. package/dist/template-resolver/web/document-toolbar-blocks.d.ts +13 -5
  263. package/dist/template-resolver/web/document-toolbar-blocks.js +6 -0
  264. package/dist/template-resolver/web/i18n.js +24 -4
  265. package/dist/template-resolver/web/index.d.ts +2 -0
  266. package/dist/template-resolver/web/index.js +2 -0
  267. package/dist/template-resolver/web/placeholder-line-editor.d.ts +6 -2
  268. package/dist/template-resolver/web/placeholder-line-editor.js +9 -7
  269. package/dist/tenant/handlers/add-member.write.d.ts +1 -1
  270. package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
  271. package/dist/tenant/handlers/create.write.d.ts +1 -1
  272. package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
  273. package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
  274. package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
  275. package/dist/tenant/handlers/update-member-roles.write.js +3 -6
  276. package/dist/tenant/handlers/update.write.d.ts +1 -1
  277. package/dist/tenant/seeding.js +23 -24
  278. package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
  279. package/dist/tenant-handover/move-entity-graph.js +14 -4
  280. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
  281. package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
  282. package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +1 -0
  283. package/dist/tenant-lifecycle/run-tenant-destroy.js +8 -1
  284. package/dist/tenant-lifecycle/stages.js +3 -2
  285. package/dist/tier-engine/constants.d.ts +2 -0
  286. package/dist/tier-engine/constants.js +4 -0
  287. package/dist/tier-engine/entity.d.ts +15 -14
  288. package/dist/tier-engine/entity.js +4 -3
  289. package/dist/tier-engine/feature.js +9 -3
  290. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +6 -1
  291. package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
  292. package/dist/tier-engine/index.d.ts +1 -1
  293. package/dist/tier-engine/index.js +1 -1
  294. package/dist/tier-engine/tier-resolver.d.ts +13 -13
  295. package/dist/user/constants.d.ts +1 -0
  296. package/dist/user/handlers/create.write.d.ts +1 -1
  297. package/dist/user/handlers/update.write.d.ts +1 -1
  298. package/dist/user/invited-global-roles.d.ts +2 -4
  299. package/dist/user/resolve-display-names.js +5 -9
  300. package/dist/user/seeding.js +20 -1
  301. package/dist/user-data-rights/email-templates.js +0 -1
  302. package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
  303. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
  304. package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
  305. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
  306. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
  307. package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
  308. package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
  309. package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
  310. package/dist/user-data-rights/i18n.js +5 -0
  311. package/dist/user-data-rights/lib/update-user-lifecycle.d.ts +2 -0
  312. package/dist/user-data-rights/lib/update-user-lifecycle.js +6 -2
  313. package/dist/user-data-rights/run-forget-cleanup.js +2 -2
  314. package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
  315. package/dist/waitlist/feature.js +1 -1
  316. package/dist/waitlist/handlers/invite.write.d.ts +1 -1
  317. package/dist/waitlist/handlers/invite.write.js +21 -3
  318. package/dist/waitlist/handlers/reject.write.d.ts +1 -1
  319. package/dist/waitlist/handlers/reject.write.js +3 -16
  320. package/dist/waitlist/handlers/submit.write.d.ts +1 -1
  321. package/dist/waitlist/handlers/submit.write.js +29 -7
  322. package/dist/waitlist/i18n.js +9 -0
  323. package/dist/waitlist/lib.d.ts +2 -1
  324. package/dist/waitlist/lib.js +15 -0
  325. package/dist/waitlist/options.js +1 -1
  326. package/dist/workflow-runner/event-trigger.js +2 -2
  327. package/dist/workflow-runner/feature.js +1 -1
  328. package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
  329. package/package.json +19 -10
  330. package/src/audit/changes.json +18 -0
  331. package/src/auth-email-password/changes.json +55 -0
  332. package/src/billing-foundation/changes.json +48 -0
  333. package/src/cap-counter/changes.json +19 -0
  334. package/src/cap-overview/changes.json +6 -0
  335. package/src/channel-email/changes.json +18 -0
  336. package/src/config/changes.json +6 -0
  337. package/src/crypto-shredding/changes.json +6 -0
  338. package/src/custom-fields/changes.json +6 -0
  339. package/src/delivery/changes.json +30 -0
  340. package/src/document-ingest-foundation/changes.json +6 -0
  341. package/src/feature-toggles/changes.json +7 -0
  342. package/src/file-foundation/changes.json +6 -0
  343. package/src/files-tenant-data/changes.json +6 -0
  344. package/src/inbound-mail-foundation/changes.json +6 -0
  345. package/src/jobs/changes.json +31 -0
  346. package/src/ledger/changes.json +7 -0
  347. package/src/metrics/changes.json +6 -0
  348. package/src/personal-access-tokens/changes.json +6 -0
  349. package/src/renderer-pdf/changes.json +12 -0
  350. package/src/secrets/changes.json +12 -0
  351. package/src/shared/changes.json +6 -0
  352. package/src/subscription-stripe/changes.json +6 -0
  353. package/src/template-resolver/changes.json +149 -0
  354. package/src/tenant/changes.json +6 -0
  355. package/src/tenant-lifecycle/changes.json +6 -0
  356. package/src/tier-engine/changes.json +18 -0
  357. package/src/user/changes.json +6 -0
  358. package/src/user-data-rights/changes.json +12 -0
  359. package/src/user-profile/changes.json +1 -1
  360. package/src/waitlist/changes.json +18 -0
  361. package/src/workflow-runner/changes.json +6 -0
@@ -1,4 +1,6 @@
1
1
  import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { isResourceImageContentType } from "../shared/index.js";
3
+ import { rewriteStepsBlock } from "./compile-steps-block.js";
2
4
  import { withParsedDocument } from "./document-sanitizer.js";
3
5
  import { DOCUMENT_TEXT_ALIGNMENTS, isAllowedDocumentLinkHref, isDocumentPlaceholderHref, isDocumentReferenceName, isDocumentResourceKey, } from "./document-schema.js";
4
6
  // Private-use characters mark where a Handlebars expression goes. Author text
@@ -37,6 +39,8 @@ function valueExpression(path, def) {
37
39
  return `{{money ${path}}}`;
38
40
  if (def?.type === "date")
39
41
  return `{{date ${path}}}`;
42
+ if (def?.type === "richText")
43
+ return `{{richText ${path}}}`;
40
44
  return `{{${path}}}`;
41
45
  }
42
46
  function rewriteVariable(element, state) {
@@ -50,6 +54,33 @@ function pathAndDef(name, element, state) {
50
54
  const { path, def } = resolveReference(name, rowScopeOf(element, state), state.variables);
51
55
  return [path, def];
52
56
  }
57
+ const VARIABLE_ONLY_BLOCK_SELECTOR = "p, h1, h2, h3, h4";
58
+ function soleVariableName(block) {
59
+ const names = new Set(Array.from(block.querySelectorAll("span[data-variable]")).map((span) => span.getAttribute("data-variable") ?? ""));
60
+ const [name] = [...names];
61
+ if (names.size !== 1 || name === undefined || !isDocumentReferenceName(name))
62
+ return null;
63
+ const withoutVariables = block.cloneNode(true);
64
+ for (const span of Array.from(withoutVariables.querySelectorAll("span[data-variable]"))) {
65
+ span.remove();
66
+ }
67
+ const hasStaticContent = (withoutVariables.textContent ?? "").trim() !== "" ||
68
+ withoutVariables.querySelector("img") !== null;
69
+ return hasStaticContent ? null : name;
70
+ }
71
+ // A line that holds nothing but one optional placeholder would otherwise leave
72
+ // an empty paragraph (a visible gap) when the value is missing. includeZero
73
+ // keeps an amount of 0.
74
+ function wrapVariableOnlyBlock(block, state) {
75
+ const name = soleVariableName(block);
76
+ // skip: a line with static content or several placeholders stays as it is
77
+ if (name === null)
78
+ return;
79
+ const [path] = pathAndDef(name, block, state);
80
+ const document = block.ownerDocument;
81
+ block.before(document.createTextNode(marker(state, `{{#if ${path} includeZero=true}}`)));
82
+ block.after(document.createTextNode(marker(state, "{{/if}}")));
83
+ }
53
84
  function rewriteLink(element, state) {
54
85
  const href = element.getAttribute("href") ?? "";
55
86
  if (isDocumentPlaceholderHref(href)) {
@@ -88,12 +119,21 @@ function stripActiveContent(element) {
88
119
  function neutralizeBraces(html) {
89
120
  return html.replace(/[{}]/g, (brace) => (brace === "{" ? "{" : "}"));
90
121
  }
91
- const RESOURCE_IMAGE_CONTENT_TYPES = new Set([
92
- "image/png",
93
- "image/jpeg",
94
- "image/gif",
95
- "image/webp",
96
- ]);
122
+ const MAIL_BUTTON_STYLE = "display:inline-block;padding:10px 20px;border-radius:6px;background-color:#1f5468;color:#ffffff;font-weight:600;text-decoration:none";
123
+ const MAIL_TABLE_STYLE = "width:100%;border-collapse:collapse";
124
+ const MAIL_TABLE_CELL_STYLE = "padding:6px 12px;vertical-align:top;border-bottom:1px solid #e5e5e5";
125
+ const MAIL_TABLE_HEAD_STYLE = "font-size:12px;font-weight:600;color:#6b7280;text-align:left";
126
+ const MAIL_SMALL_STYLE = "font-size:13px;color:#4A515E";
127
+ const MAIL_HEADING_STYLES = {
128
+ h1: "margin:0 0 12px 0;font-size:30px;line-height:1.15;font-weight:700;letter-spacing:-0.035em;color:#0B0D12",
129
+ h2: "margin:0 0 12px 0;font-size:24px;line-height:1.2;font-weight:700;letter-spacing:-0.02em;color:#0B0D12",
130
+ h3: "margin:20px 0 8px 0;font-size:17px;line-height:1.3;font-weight:600;color:#0B0D12",
131
+ };
132
+ // Appended after an existing style (data-align) so the alignment survives.
133
+ function appendStyle(element, style) {
134
+ const existing = element.getAttribute("style");
135
+ element.setAttribute("style", existing === null ? style : `${existing};${style}`);
136
+ }
97
137
  // Unique valid resource keys in document order.
98
138
  export function collectDocumentResourceKeys(html) {
99
139
  return withParsedDocument(html, (root) => {
@@ -107,7 +147,7 @@ export function collectDocumentResourceKeys(html) {
107
147
  });
108
148
  }
109
149
  export function toResourceDataUri(contentType, bytes) {
110
- if (!RESOURCE_IMAGE_CONTENT_TYPES.has(contentType)) {
150
+ if (!isResourceImageContentType(contentType)) {
111
151
  throw new Error(`resource content type "${contentType}" cannot be embedded as an image`);
112
152
  }
113
153
  return `data:${contentType};base64,${Buffer.from(bytes).toString("base64")}`;
@@ -118,7 +158,7 @@ function rewriteAlignment(element) {
118
158
  if (isOneOf(DOCUMENT_TEXT_ALIGNMENTS, align))
119
159
  element.setAttribute("style", `text-align:${align}`);
120
160
  }
121
- function rewriteResourceImage(element, resourceSrc) {
161
+ function rewriteResourceImage(element, resourceSrc, maxWidthPx, maxHeightPx) {
122
162
  const key = element.getAttribute("data-resource") ?? "";
123
163
  const src = isDocumentResourceKey(key) ? resourceSrc?.(key) : undefined;
124
164
  if (src === undefined) {
@@ -128,6 +168,56 @@ function rewriteResourceImage(element, resourceSrc) {
128
168
  }
129
169
  element.setAttribute("src", src);
130
170
  element.removeAttribute("data-resource");
171
+ const caps = [
172
+ ...(maxWidthPx === undefined ? [] : [`max-width:${maxWidthPx}px`]),
173
+ ...(maxHeightPx === undefined ? [] : [`max-height:${maxHeightPx}px;width:auto`]),
174
+ ];
175
+ if (caps.length > 0)
176
+ element.setAttribute("style", `${caps.join(";")};height:auto`);
177
+ }
178
+ function stripScriptsAndActiveContent(root, options) {
179
+ for (const element of Array.from(root.querySelectorAll("script")))
180
+ element.remove();
181
+ for (const element of Array.from(root.querySelectorAll("img[data-resource]"))) {
182
+ rewriteResourceImage(element, options.resourceSrc, options.resourceImageMaxWidthPx, options.resourceImageMaxHeightPx);
183
+ }
184
+ for (const element of Array.from(root.querySelectorAll("*")))
185
+ stripActiveContent(element);
186
+ }
187
+ function applyAlignmentInlineStyles(root) {
188
+ for (const element of Array.from(root.querySelectorAll("[data-align]"))) {
189
+ rewriteAlignment(element);
190
+ }
191
+ for (const element of Array.from(root.querySelectorAll("[data-size]"))) {
192
+ element.removeAttribute("data-size");
193
+ appendStyle(element, MAIL_SMALL_STYLE);
194
+ }
195
+ }
196
+ function applyHeadingInlineStyles(root) {
197
+ for (const heading of Array.from(root.querySelectorAll("h1, h2, h3"))) {
198
+ const style = MAIL_HEADING_STYLES[heading.tagName.toLowerCase()];
199
+ if (style !== undefined)
200
+ appendStyle(heading, style);
201
+ }
202
+ }
203
+ function applyTableInlineStyles(root) {
204
+ for (const table of Array.from(root.querySelectorAll("table"))) {
205
+ appendStyle(table, MAIL_TABLE_STYLE);
206
+ }
207
+ for (const cell of Array.from(root.querySelectorAll("td, th"))) {
208
+ appendStyle(cell, MAIL_TABLE_CELL_STYLE);
209
+ }
210
+ for (const head of Array.from(root.querySelectorAll("th"))) {
211
+ appendStyle(head, MAIL_TABLE_HEAD_STYLE);
212
+ }
213
+ }
214
+ function rewriteLinks(root, state, options) {
215
+ for (const element of Array.from(root.querySelectorAll("a[href]"))) {
216
+ rewriteLink(element, state);
217
+ if (options.buttonAsInlineStyle === true && element.hasAttribute("data-button")) {
218
+ element.setAttribute("style", options.buttonInlineStyle ?? MAIL_BUTTON_STYLE);
219
+ }
220
+ }
131
221
  }
132
222
  // Turns a sanitized document body into Handlebars source. Rows and links are
133
223
  // rewritten in document order; list rows last so their placeholders still see
@@ -136,24 +226,27 @@ export function compileDocumentBodyToHandlebars(html, variables, options = {}) {
136
226
  const state = { variables, expressions: [] };
137
227
  const scrubbed = html.replaceAll(EXPRESSION_OPEN, "").replaceAll(EXPRESSION_CLOSE, "");
138
228
  const compiled = withParsedDocument(scrubbed, (root) => {
139
- for (const element of Array.from(root.querySelectorAll("script")))
140
- element.remove();
141
- for (const element of Array.from(root.querySelectorAll("img[data-resource]"))) {
142
- rewriteResourceImage(element, options.resourceSrc);
229
+ stripScriptsAndActiveContent(root, options);
230
+ if (options.alignmentAsInlineStyle === true)
231
+ applyAlignmentInlineStyles(root);
232
+ if (options.headingAsInlineStyle === true)
233
+ applyHeadingInlineStyles(root);
234
+ if (options.tableAsInlineStyle === true)
235
+ applyTableInlineStyles(root);
236
+ for (const element of Array.from(root.querySelectorAll("div[data-steps]"))) {
237
+ rewriteStepsBlock(element, {
238
+ expression: (source) => marker(state, source),
239
+ pathOf: (name, at) => pathAndDef(name, at, state)[0],
240
+ color: options.stepsColor,
241
+ });
143
242
  }
144
- for (const element of Array.from(root.querySelectorAll("*")))
145
- stripActiveContent(element);
146
- if (options.alignmentAsInlineStyle === true) {
147
- for (const element of Array.from(root.querySelectorAll("[data-align]"))) {
148
- rewriteAlignment(element);
149
- }
243
+ for (const block of Array.from(root.querySelectorAll(VARIABLE_ONLY_BLOCK_SELECTOR))) {
244
+ wrapVariableOnlyBlock(block, state);
150
245
  }
151
246
  for (const element of Array.from(root.querySelectorAll("span[data-variable]"))) {
152
247
  rewriteVariable(element, state);
153
248
  }
154
- for (const element of Array.from(root.querySelectorAll("a[href]"))) {
155
- rewriteLink(element, state);
156
- }
249
+ rewriteLinks(root, state, options);
157
250
  for (const element of Array.from(root.querySelectorAll("tr[data-each]"))) {
158
251
  rewriteRepeatRow(element, state);
159
252
  }
@@ -0,0 +1,10 @@
1
+ import type { Element as ScanElement } from "happy-dom";
2
+ export declare const DEFAULT_STEPS_COLOR = "#1f5468";
3
+ export type StepsCompileHooks = {
4
+ /** Registers a Handlebars expression and returns the marker that stands for it. */
5
+ readonly expression: (source: string) => string;
6
+ /** Resolves a variable name to its Handlebars path at the element's position. */
7
+ readonly pathOf: (name: string, element: ScanElement) => string;
8
+ readonly color: string | undefined;
9
+ };
10
+ export declare function rewriteStepsBlock(element: ScanElement, hooks: StepsCompileHooks): void;
@@ -0,0 +1,45 @@
1
+ import { isDocumentReferenceName } from "./document-schema.js";
2
+ import { readDocumentSteps } from "./document-steps.js";
3
+ import { isHexColor } from "./layout-appearance.js";
4
+ export const DEFAULT_STEPS_COLOR = "#1f5468";
5
+ const TODO_BAR_COLOR = "#ECEDEA";
6
+ const REACHED_LABEL_COLOR = "#0B0D12";
7
+ const TODO_LABEL_COLOR = "#5A6170";
8
+ // Inline-styled table in every output (mail, PDF, preview): mail clients ignore
9
+ // document CSS, and one rendering keeps the three outputs identical. The labels
10
+ // stay static text; only the reached/current switch is a Handlebars block, and
11
+ // its arguments are a validated path and validated keys.
12
+ export function rewriteStepsBlock(element, hooks) {
13
+ const name = element.getAttribute("data-steps") ?? "";
14
+ const steps = readDocumentSteps(element);
15
+ if (!isDocumentReferenceName(name) || steps === null) {
16
+ element.remove();
17
+ // skip: an invalid steps block renders nothing
18
+ return;
19
+ }
20
+ const path = hooks.pathOf(name, element);
21
+ const keys = steps.map((step) => step.key).join(",");
22
+ const color = hooks.color !== undefined && isHexColor(hooks.color) ? hooks.color : DEFAULT_STEPS_COLOR;
23
+ const when = (index, state, yes, no) => `${hooks.expression(`{{#stepIs ${path} ${index} "${keys}" "${state}"}}`)}${yes}${hooks.expression("{{else}}")}${no}${hooks.expression("{{/stepIs}}")}`;
24
+ const document = element.ownerDocument;
25
+ const table = document.createElement("table");
26
+ table.setAttribute("role", "presentation");
27
+ table.setAttribute("width", "100%");
28
+ table.setAttribute("cellpadding", "0");
29
+ table.setAttribute("cellspacing", "0");
30
+ table.setAttribute("style", "table-layout:fixed;border-collapse:collapse;margin:4px 0 24px 0");
31
+ const row = document.createElement("tr");
32
+ steps.forEach((step, index) => {
33
+ const cell = document.createElement("td");
34
+ cell.setAttribute("style", `padding:0 ${index === steps.length - 1 ? 0 : 4}px 0 0;vertical-align:top`);
35
+ const bar = document.createElement("div");
36
+ bar.setAttribute("style", `height:4px;line-height:4px;font-size:0;border-radius:2px;background-color:${when(index, "reached", color, TODO_BAR_COLOR)}`);
37
+ const label = document.createElement("div");
38
+ label.setAttribute("style", `padding-top:8px;font-size:12px;line-height:1.3;color:${when(index, "reached", REACHED_LABEL_COLOR, TODO_LABEL_COLOR)};font-weight:${when(index, "current", "600", "400")}`);
39
+ label.textContent = step.label;
40
+ cell.append(bar, label);
41
+ row.append(cell);
42
+ });
43
+ table.append(row);
44
+ element.replaceWith(table);
45
+ }
@@ -9,16 +9,19 @@ export type TemplateKind = (typeof TEMPLATE_KINDS)[number];
9
9
  export declare const UPSERT_KINDS: readonly ["mail", "document-pdf", "image-snapshot", "ai-prompt", "mail-layout", "pdf-layout"];
10
10
  export type UpsertKind = (typeof UPSERT_KINDS)[number];
11
11
  export declare const TEXT_BLOCK_KIND: "text-block";
12
- export declare const COLLECTION_REFUSED_KINDS: readonly string[];
12
+ export declare const COLLECTION_REFUSED_KINDS: readonly ["mail", "mail-layout", "pdf-layout"];
13
+ export declare function isCollectionRefusedKind(kind: string): boolean;
13
14
  export declare const CONTENT_FORMATS: readonly ["markdown", "mjml", "html", "plain"];
14
15
  export type ContentFormat = (typeof CONTENT_FORMATS)[number];
15
16
  export declare const TEMPLATE_SCOPES: readonly ["system", "tenant"];
16
17
  export type TemplateScope = (typeof TEMPLATE_SCOPES)[number];
17
18
  export declare const TEMPLATE_STATUSES: readonly ["draft", "active", "archived"];
18
19
  export type TemplateStatus = (typeof TEMPLATE_STATUSES)[number];
19
- export declare const PDF_PAGE_FORMATS: readonly ["A4", "Letter"];
20
- export type PdfPageFormat = (typeof PDF_PAGE_FORMATS)[number];
20
+ export declare const ACTIVE_TEMPLATE_STATUS = "active";
21
+ export { PDF_DEFAULT_MARGIN_MM, PDF_FOOTER_MARGIN_MM, PDF_HEADER_MARGIN_MM, PDF_ORIENTATIONS, PDF_PAGE_FORMATS, type PdfOrientation, type PdfPageFormat, } from "./pdf-constants.js";
22
+ export declare const HEX_COLOR_PATTERN: RegExp;
21
23
  export declare const DEFAULT_MAIL_LAYOUT_SLUG = "default";
24
+ export declare const DEFAULT_PDF_LAYOUT_SLUG = "default";
22
25
  export declare const MAIL_PREVIEW_MAX_BYTES: number;
23
26
  export declare const TEST_MAIL_NOTIFICATION_TYPE = "template-resolver:test-mail";
24
27
  export declare const TEST_MAIL_SUBJECT_PREFIX = "[TEST] ";
@@ -40,12 +40,23 @@ export const TEXT_BLOCK_KIND = "text-block";
40
40
  // Kinds a content collection must not write: a tenant row of these kinds would
41
41
  // shadow the system notification template or layout without the meta and
42
42
  // placeholder gates that upsertTenant applies.
43
- export const COLLECTION_REFUSED_KINDS = ["mail", "mail-layout", "pdf-layout"];
43
+ export const COLLECTION_REFUSED_KINDS = [
44
+ "mail",
45
+ "mail-layout",
46
+ "pdf-layout",
47
+ ];
48
+ export function isCollectionRefusedKind(kind) {
49
+ return COLLECTION_REFUSED_KINDS.some((refused) => refused === kind);
50
+ }
44
51
  export const CONTENT_FORMATS = ["markdown", "mjml", "html", "plain"];
45
52
  export const TEMPLATE_SCOPES = ["system", "tenant"];
46
53
  export const TEMPLATE_STATUSES = ["draft", "active", "archived"];
47
- export const PDF_PAGE_FORMATS = ["A4", "Letter"];
54
+ export const ACTIVE_TEMPLATE_STATUS = "active";
55
+ export { PDF_DEFAULT_MARGIN_MM, PDF_FOOTER_MARGIN_MM, PDF_HEADER_MARGIN_MM, PDF_ORIENTATIONS, PDF_PAGE_FORMATS, } from "./pdf-constants.js";
56
+ // Colors end up inside MJML attributes, so only a strict hex is ever written there.
57
+ export const HEX_COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
48
58
  export const DEFAULT_MAIL_LAYOUT_SLUG = "default";
59
+ export const DEFAULT_PDF_LAYOUT_SLUG = "default";
49
60
  // The mail transport enforces no size limit of its own; 10 MiB is a common provider ceiling.
50
61
  export const MAIL_PREVIEW_MAX_BYTES = 10 * 1024 * 1024;
51
62
  export const TEST_MAIL_NOTIFICATION_TYPE = "template-resolver:test-mail";
@@ -3,6 +3,7 @@ import { generateHTML, generateJSON } from "@tiptap/html/server";
3
3
  import { Window } from "happy-dom";
4
4
  import { DOCUMENT_HTML_KINDS } from "./constants.js";
5
5
  import { documentExtensions, isAllowedDocumentLinkHref, isDocumentPlaceholderHref, isDocumentReferenceName, isDocumentResourceKey, } from "./document-schema.js";
6
+ import { isDocumentStepKey } from "./document-steps.js";
6
7
  export function violation(code, detail, message) {
7
8
  return { code, detail, message };
8
9
  }
@@ -56,6 +57,8 @@ const REFERENCE_ATTRIBUTES = [
56
57
  ["data-variable", isDocumentReferenceName],
57
58
  ["data-each", isDocumentReferenceName],
58
59
  ["data-resource", isDocumentResourceKey],
60
+ ["data-steps", isDocumentReferenceName],
61
+ ["data-step", isDocumentStepKey],
59
62
  ];
60
63
  function scanReferences(tag, element, out) {
61
64
  for (const [attribute, isValid] of REFERENCE_ATTRIBUTES) {
@@ -3,14 +3,14 @@ export declare function isDocumentPlaceholderHref(href: string): boolean;
3
3
  export declare function isAllowedDocumentLinkHref(href: string): boolean;
4
4
  export declare function isDocumentReferenceName(value: string): boolean;
5
5
  export declare function isDocumentResourceKey(value: string): boolean;
6
- export declare const DOCUMENT_NODE_NAMES: readonly ["variable", "resource", "button", "divider", "pageBreak"];
6
+ export declare const DOCUMENT_NODE_NAMES: readonly ["variable", "resource", "button", "divider", "pageBreak", "steps"];
7
7
  export declare const DocumentVariable: Node<any, any>;
8
8
  export declare const DocumentResource: Node<any, any>;
9
9
  export declare const DocumentButton: Node<any, any>;
10
10
  export declare const DocumentDivider: Node<any, any>;
11
11
  export declare const DocumentPageBreak: Node<any, any>;
12
+ export declare const DocumentSteps: Node<any, any>;
12
13
  export declare const DocumentTableRow: Node<import("@tiptap/extension-table").TableRowOptions, any>;
13
- export declare const DocumentRepeatRow: Node<import("@tiptap/extension-table").TableRowOptions, any>;
14
14
  export declare const DocumentTable: Node<import("@tiptap/extension-table").TableOptions, any>;
15
15
  export declare const DocumentTableCell: Node<import("@tiptap/extension-table").TableCellOptions, any>;
16
16
  export declare const DocumentTableHeader: Node<import("@tiptap/extension-table").TableHeaderOptions, any>;
@@ -18,4 +18,5 @@ export declare const DocumentLink: import("@tiptap/core").Mark<import("@tiptap/e
18
18
  export declare const DOCUMENT_TEXT_ALIGNMENTS: readonly ["left", "center", "right", "justify"];
19
19
  export type DocumentTextAlignment = (typeof DOCUMENT_TEXT_ALIGNMENTS)[number];
20
20
  export declare const DocumentTextAlign: Extension<any, any>;
21
+ export declare const DocumentTextSize: Extension<any, any>;
21
22
  export declare const documentExtensions: Extensions;
@@ -8,12 +8,14 @@ import { Extension, Node } from "@tiptap/core";
8
8
  import { Link } from "@tiptap/extension-link";
9
9
  import { Table, TableCell, TableHeader, TableRow } from "@tiptap/extension-table";
10
10
  import StarterKit from "@tiptap/starter-kit";
11
- const PLACEHOLDER_HREF = /^\{\{[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*\}\}$/;
11
+ import { createDocumentStepsNode } from "./document-steps.js";
12
+ const PLACEHOLDER_HREF = /^\{\{(.+)\}\}$/;
12
13
  const REFERENCE_NAME = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
13
14
  const RESOURCE_KEY = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
14
15
  const ALLOWED_LINK_SCHEMES = ["https://", "http://", "mailto:", "tel:"];
15
16
  export function isDocumentPlaceholderHref(href) {
16
- return PLACEHOLDER_HREF.test(href);
17
+ const name = PLACEHOLDER_HREF.exec(href)?.[1];
18
+ return name !== undefined && isDocumentReferenceName(name);
17
19
  }
18
20
  const UNSAFE_URL_CHARACTERS = new Set(["<", ">", '"']);
19
21
  const FORBIDDEN_REFERENCE_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
@@ -62,6 +64,7 @@ export const DOCUMENT_NODE_NAMES = [
62
64
  "button",
63
65
  "divider",
64
66
  "pageBreak",
67
+ "steps",
65
68
  ];
66
69
  export const DocumentVariable = Node.create({
67
70
  name: "variable",
@@ -120,7 +123,8 @@ export const DocumentResource = Node.create({
120
123
  export const DocumentButton = Node.create({
121
124
  name: "button",
122
125
  group: "block",
123
- content: "text*",
126
+ // A placeholder in the label ("View {{pageName}}") must survive the round trip.
127
+ content: "(text | variable)*",
124
128
  marks: "",
125
129
  defining: true,
126
130
  addAttributes() {
@@ -165,6 +169,7 @@ export const DocumentPageBreak = Node.create({
165
169
  return ["div", { "data-page-break": "" }];
166
170
  },
167
171
  });
172
+ export const DocumentSteps = createDocumentStepsNode(isDocumentReferenceName);
168
173
  // A repeat row is a table row carrying `each`: prosemirror-tables requires
169
174
  // the tableRow node type, so a separate node type is not an option.
170
175
  export const DocumentTableRow = TableRow.extend({
@@ -189,7 +194,6 @@ export const DocumentTableRow = TableRow.extend({
189
194
  };
190
195
  },
191
196
  });
192
- export const DocumentRepeatRow = DocumentTableRow;
193
197
  export const DocumentTable = Table.extend({
194
198
  renderHTML() {
195
199
  return ["table", ["tbody", 0]];
@@ -261,6 +265,25 @@ export const DocumentTextAlign = Extension.create({
261
265
  ];
262
266
  },
263
267
  });
268
+ // Only "small" exists: the sender line above an address block. Serialized as data-size for the
269
+ // same reason as data-align.
270
+ export const DocumentTextSize = Extension.create({
271
+ name: "documentTextSize",
272
+ addGlobalAttributes() {
273
+ return [
274
+ {
275
+ types: ["paragraph"],
276
+ attributes: {
277
+ textSize: {
278
+ default: null,
279
+ parseHTML: (element) => element.getAttribute("data-size") === "small" ? "small" : null,
280
+ renderHTML: (attributes) => attributes["textSize"] === "small" ? { "data-size": "small" } : {},
281
+ },
282
+ },
283
+ },
284
+ ];
285
+ },
286
+ });
264
287
  export const documentExtensions = [
265
288
  StarterKit.configure({
266
289
  blockquote: false,
@@ -292,5 +315,7 @@ export const documentExtensions = [
292
315
  DocumentButton,
293
316
  DocumentDivider,
294
317
  DocumentPageBreak,
318
+ DocumentSteps,
295
319
  DocumentTextAlign,
320
+ DocumentTextSize,
296
321
  ];
@@ -0,0 +1,17 @@
1
+ import { Node } from "@tiptap/core";
2
+ export declare const DOCUMENT_STEPS_MAX = 8;
3
+ export type DocumentStep = {
4
+ readonly key: string;
5
+ readonly label: string;
6
+ };
7
+ export declare function isDocumentStepKey(value: string): boolean;
8
+ export declare function documentStepKeyOf(label: string): string;
9
+ export declare function parseDocumentStepsInput(text: string): readonly DocumentStep[] | null;
10
+ export declare function readDocumentSteps(element: {
11
+ querySelectorAll(selector: string): ArrayLike<{
12
+ getAttribute(name: string): string | null;
13
+ textContent: string | null;
14
+ }>;
15
+ }): readonly DocumentStep[] | null;
16
+ export declare function readDocumentStepsAttribute(value: unknown): readonly DocumentStep[];
17
+ export declare function createDocumentStepsNode(isReferenceName: (value: string) => boolean): Node<any, any>;
@@ -0,0 +1,93 @@
1
+ // @runtime client
2
+ // A progress row: fixed stages stored in the template, the reached stage read
3
+ // from a variable at render time. Universal like document-schema.ts.
4
+ import { Node } from "@tiptap/core";
5
+ // Keys end up as Handlebars string literals in the compiled body, so they are
6
+ // restricted to characters that cannot close or escape a literal.
7
+ const STEP_KEY = /^[A-Za-z0-9][A-Za-z0-9_-]{0,39}$/;
8
+ export const DOCUMENT_STEPS_MAX = 8;
9
+ const STEP_LABEL_MAX_LENGTH = 40;
10
+ export function isDocumentStepKey(value) {
11
+ return STEP_KEY.test(value);
12
+ }
13
+ export function documentStepKeyOf(label) {
14
+ return label
15
+ .normalize("NFKD")
16
+ .replace(/\p{M}/gu, "")
17
+ .toLowerCase()
18
+ .replace(/[^a-z0-9]+/g, "-")
19
+ .replace(/^-+|-+$/g, "")
20
+ .slice(0, 40);
21
+ }
22
+ // Editor input: "value=Label" entries separated by commas; a bare label derives its value.
23
+ export function parseDocumentStepsInput(text) {
24
+ const steps = text
25
+ .split(",")
26
+ .map((entry) => entry.trim())
27
+ .filter((entry) => entry !== "")
28
+ .map((entry) => {
29
+ const separator = entry.indexOf("=");
30
+ const label = (separator < 0 ? entry : entry.slice(separator + 1)).trim();
31
+ const key = separator < 0 ? documentStepKeyOf(entry) : entry.slice(0, separator).trim();
32
+ return { key, label: label.slice(0, STEP_LABEL_MAX_LENGTH) };
33
+ });
34
+ return areValidSteps(steps) ? steps : null;
35
+ }
36
+ function areValidSteps(steps) {
37
+ return (steps.length > 0 &&
38
+ steps.length <= DOCUMENT_STEPS_MAX &&
39
+ new Set(steps.map((step) => step.key)).size === steps.length &&
40
+ steps.every((step) => isDocumentStepKey(step.key) && step.label !== ""));
41
+ }
42
+ // Duplicate or invalid keys would make the reached stage ambiguous, so the whole
43
+ // list is refused rather than repaired.
44
+ export function readDocumentSteps(element) {
45
+ const steps = Array.from(element.querySelectorAll("span[data-step]")).map((span) => ({
46
+ key: span.getAttribute("data-step") ?? "",
47
+ label: (span.textContent ?? "").trim().slice(0, STEP_LABEL_MAX_LENGTH),
48
+ }));
49
+ return areValidSteps(steps) ? steps : null;
50
+ }
51
+ function isDocumentStep(step) {
52
+ return (typeof step === "object" &&
53
+ step !== null &&
54
+ "key" in step &&
55
+ "label" in step &&
56
+ typeof step.key === "string" &&
57
+ typeof step.label === "string");
58
+ }
59
+ // Node attrs are untyped JSON; anything that is not a step entry is dropped.
60
+ export function readDocumentStepsAttribute(value) {
61
+ return Array.isArray(value) ? value.filter(isDocumentStep) : [];
62
+ }
63
+ export function createDocumentStepsNode(isReferenceName) {
64
+ return Node.create({
65
+ name: "steps",
66
+ group: "block",
67
+ atom: true,
68
+ addAttributes() {
69
+ return { variable: { default: null }, steps: { default: [] } };
70
+ },
71
+ parseHTML() {
72
+ return [
73
+ {
74
+ tag: "div[data-steps]",
75
+ getAttrs: (element) => {
76
+ const variable = element.getAttribute("data-steps");
77
+ const steps = readDocumentSteps(element);
78
+ if (variable === null || !isReferenceName(variable) || steps === null)
79
+ return false;
80
+ return { variable, steps };
81
+ },
82
+ },
83
+ ];
84
+ },
85
+ renderHTML({ node }) {
86
+ return [
87
+ "div",
88
+ { "data-steps": node.attrs["variable"] },
89
+ ...readDocumentStepsAttribute(node.attrs["steps"]).map((step) => ["span", { "data-step": step.key }, step.label]),
90
+ ];
91
+ },
92
+ });
93
+ }
@@ -20,6 +20,8 @@ export declare function createTemplateResolverFeature(opts?: TemplateResolverOpt
20
20
  archive: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
21
21
  publishSystem: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
22
22
  archiveSystem: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
23
+ discardDraft: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
24
+ discardDraftSystem: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
23
25
  resetToSystem: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
24
26
  set: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
25
27
  importBundle: import("@cosmicdrift/kumiko-types/handlers").HandlerRef;
@@ -1,4 +1,5 @@
1
1
  import { createSystemConfig, defineFeature, } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { isCollectionRefusedKind } from "./constants.js";
2
3
  import { bySlugQuery } from "./handlers/by-slug.query.js";
3
4
  import { byTenantQuery } from "./handlers/by-tenant.query.js";
4
5
  import { makeCollectionItemQuery } from "./handlers/collection-item.query.js";
@@ -6,6 +7,7 @@ import { makeCollectionListQuery } from "./handlers/collection-list.query.js";
6
7
  import { makeCollectionSetWrite } from "./handlers/collection-set.write.js";
7
8
  import { copyLocaleWrite } from "./handlers/copy-locale.write.js";
8
9
  import { diffToSystemQuery } from "./handlers/diff-to-system.query.js";
10
+ import { discardDraftSystemWrite, discardDraftWrite } from "./handlers/discard-draft.write.js";
9
11
  import { exportBundleQuery } from "./handlers/export-bundle.query.js";
10
12
  import { findByIdQuery } from "./handlers/find-by-id.query.js";
11
13
  import { importBundleWrite } from "./handlers/import-bundle.write.js";
@@ -28,13 +30,21 @@ import { usageQuery } from "./handlers/usage.query.js";
28
30
  import { validateQuery } from "./handlers/validate.query.js";
29
31
  import { templateResourceEntity } from "./table.js";
30
32
  import { userContentEntryEntity } from "./user-content-table.js";
33
+ function assertCollectionKindsAllowed(collections) {
34
+ for (const collection of collections) {
35
+ if (isCollectionRefusedKind(collection.kind)) {
36
+ throw new Error(`template-resolver: collection "${collection.id}" uses kind "${collection.kind}", which collections may not write`);
37
+ }
38
+ }
39
+ }
31
40
  export function createTemplateResolverFeature(opts = {}) {
32
41
  const collections = opts.collections ?? [];
42
+ assertCollectionKindsAllowed(collections);
33
43
  const hasUserOwned = collections.some((c) => c.ownership === "user");
34
44
  return defineFeature("template-resolver", (r) => {
35
45
  r.describe([
36
46
  "Every piece of editable text lives here, in one entity: mail bodies, notification texts, PDF document templates, AI prompts and plain text blocks. What a record is used for is the `kind` (`notification`, `mail`, `mail-layout`, `document-pdf`, `pdf-layout`, `ai-prompt`, `text-block`, `snippet`, `image-snapshot`).",
37
- "Reading is one call — `ctx.templateResolver.resolveTemplate({ tenantId, slug, kind, locale })`. It walks four levels: tenant+locale, system+locale, tenant+fallback-locale, system+fallback-locale. A tenant overrides a system default by simply having its own record; no application code changes. Writing programmatically goes through the `upsertSystem`, `upsertTenant`, `publish` and `archive` write handlers; a SystemAdmin retires or restores a system default with `publishSystem` and `archiveSystem`, and a tenant drops its override with `resetToSystem`.",
47
+ "Reading is one call — `ctx.templateResolver.resolveTemplate({ tenantId, slug, kind, locale })`. It walks four levels: tenant+locale, system+locale, tenant+fallback-locale, system+fallback-locale. A tenant overrides a system default by simply having its own record; no application code changes. Writing programmatically goes through the `upsertSystem`, `upsertTenant`, `publish` and `archive` write handlers; a SystemAdmin retires or restores a system default with `publishSystem` and `archiveSystem`, and a tenant drops its override with `resetToSystem`. Saving a draft over an active template keeps the active version served and stores the edit beside it (column `draft`); `publish` makes it the served version, `discardDraft` / `discardDraftSystem` throws it away.",
38
48
  "Text an editor should be able to change belongs in a collection, declared at mount: `createTemplateResolverFeature({ collections: [{ id, kind, access: { roles }, nav }] })`. It appears in the navigation, and `access` is part of the mount because a bundled feature cannot know the host's roles. Each collection gets its own `<id>-list` / `<id>-item` / `<id>-set` handlers, so the dispatcher enforces the separation.",
39
49
  "How a collection is edited follows from `contentFormat`: `plain` gives a text area, `rich` a small WYSIWYG (bold, italic, headings, lists, links), `markdown` a text area that stores markdown text (same insertable chips as `plain`). All three offer the collection's `variableSchema` as insertable chips and a preview rendered with sample data — an editor sees what `{{firstName}}` becomes without sending a mail. An app can register its own editor for a format; the last `clientFeature` that registers a format wins (a conflict logs a warning).",
40
50
  'A collection is tenant-wide by default. With `ownership: "user"` every user keeps their own entries — mail signatures being the obvious case. Those rows live in the separate `user-content-entry` entity and count as user data, so mounting one also requires the `template-resolver-user-data` feature and a migration on the app side.',
@@ -67,6 +77,8 @@ export function createTemplateResolverFeature(opts = {}) {
67
77
  archive: r.writeHandler(archiveWrite),
68
78
  publishSystem: r.writeHandler(publishSystemWrite),
69
79
  archiveSystem: r.writeHandler(archiveSystemWrite),
80
+ discardDraft: r.writeHandler(discardDraftWrite),
81
+ discardDraftSystem: r.writeHandler(discardDraftSystemWrite),
70
82
  resetToSystem: r.writeHandler(resetToSystemWrite),
71
83
  set: r.writeHandler(setWrite),
72
84
  importBundle: r.writeHandler(importBundleWrite),
@@ -1,6 +1,7 @@
1
- import type { HandlerContext, SessionUser, TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
1
+ import type { BeforeTransactionResult, HandlerContext, SessionUser, TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { type FieldIssue } from "@cosmicdrift/kumiko-framework/errors";
3
3
  import type { TemplateWriteScope } from "../layout-regions.js";
4
+ import { type PreparedTemplateFile } from "../store-template-resource.js";
4
5
  import type { TemplateBundle } from "../template-bundle.js";
5
6
  export declare function targetExistsIssue(locale: string): FieldIssue;
6
7
  type BundleImport = {
@@ -12,8 +13,13 @@ type BundleImport = {
12
13
  readonly overwrite: boolean;
13
14
  };
14
15
  type BundleImportStatus = "draft" | "active";
16
+ export type PreparedBundleResources = ReadonlyMap<string, PreparedTemplateFile>;
17
+ export declare function prepareTemplateBundleBeforeTransaction(args: BundleImport & {
18
+ readonly status: BundleImportStatus;
19
+ }): Promise<BeforeTransactionResult<PreparedBundleResources>>;
15
20
  export declare function importTemplateBundle(args: BundleImport & {
16
21
  readonly status: BundleImportStatus;
22
+ readonly prepared: PreparedBundleResources;
17
23
  }): Promise<WriteResult<{
18
24
  ids: Record<string, string>;
19
25
  }>>;