ghost 6.60.0 → 6.62.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 (613) hide show
  1. package/components/ghost-storage-base-3.0.0.tgz +0 -0
  2. package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
  3. package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
  4. package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
  5. package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
  6. package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
  7. package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
  8. package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
  9. package/components/tryghost-checkout-0.0.0.tgz +0 -0
  10. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  11. package/components/tryghost-i18n-0.0.0.tgz +0 -0
  12. package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
  13. package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
  14. package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
  15. package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
  16. package/components/tryghost-kg-default-nodes-2.2.1.tgz +0 -0
  17. package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
  18. package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
  19. package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
  20. package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
  21. package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
  22. package/components/tryghost-nql-string-0.0.0.tgz +0 -0
  23. package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
  24. package/content/settings/README.md +1 -1
  25. package/content/themes/casper/.gitignore +28 -0
  26. package/content/themes/casper/assets/built/casper.js +1 -1
  27. package/content/themes/casper/assets/built/screen.css +1 -1
  28. package/content/themes/casper/assets/css/screen.css +2 -1
  29. package/content/themes/casper/assets/js/lightbox.js +63 -46
  30. package/content/themes/casper/package.json +6 -6
  31. package/content/themes/casper/page.hbs +1 -0
  32. package/content/themes/casper/post.hbs +1 -0
  33. package/content/themes/source/.gitignore +28 -0
  34. package/content/themes/source/assets/built/screen.css +1 -1
  35. package/content/themes/source/assets/built/source.js +1 -1
  36. package/content/themes/source/assets/css/screen.css +2 -1
  37. package/content/themes/source/assets/js/lightbox.js +62 -45
  38. package/content/themes/source/assets/js/main.js +2 -2
  39. package/content/themes/source/package.json +4 -4
  40. package/content/themes/source/partials/feature-image.hbs +1 -0
  41. package/core/boot.js +62 -21
  42. package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
  43. package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
  44. package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
  45. package/core/built/admin/assets/{PolarAngleAxis-C4qtHy1B.js → PolarAngleAxis-pkMyqz8g.js} +1 -1
  46. package/core/built/admin/assets/{RadialBarChart-Bh09Stsj.js → RadialBarChart-JAKoS4KT.js} +1 -1
  47. package/core/built/admin/assets/{about-BgpmhCuM.js → about-Dyae_UT6.js} +1 -1
  48. package/core/built/admin/assets/account-migration-CESa0mnX.js +1 -0
  49. package/core/built/admin/assets/{action-list-lGzZLHc8.js → action-list-C4v0-du8.js} +1 -1
  50. package/core/built/admin/assets/activitypub/{account-migration-DMHuPnJn.js → account-migration-By69CgCp.js} +16 -16
  51. package/core/built/admin/assets/activitypub/activitypub.js +9 -10
  52. package/core/built/admin/assets/activitypub/{avatar-DhTo5D1B.js → avatar-DLPL3Abs.js} +59 -68
  53. package/core/built/admin/assets/activitypub/{avatar-flipboard-DnVgbEfa.js → avatar-flipboard-C9iDY-QV.js} +2 -2
  54. package/core/built/admin/assets/activitypub/{bluesky-sharing-BT4zAlW7.js → bluesky-sharing-BJcPMrDd.js} +37 -37
  55. package/core/built/admin/assets/activitypub/{content-formatters-BeDZ-DIa.js → content-formatters-3rTHg3mp.js} +18 -18
  56. package/core/built/admin/assets/activitypub/{copy-8sqxUuNd.js → copy-DIAOQoCZ.js} +2 -2
  57. package/core/built/admin/assets/activitypub/{domain-xQDDu1CS.js → domain-DUR4xSkV.js} +121 -121
  58. package/core/built/admin/assets/activitypub/{edit-profile-BXdF_r2G.js → edit-profile-BqtfzxCp.js} +40 -40
  59. package/core/built/admin/assets/activitypub/{explore-CMt7gKlL.js → explore-BxuJd-J4.js} +27 -27
  60. package/core/built/admin/assets/activitypub/{feed-Bwqdddp_.js → feed-BM1TcksA.js} +51 -51
  61. package/core/built/admin/assets/activitypub/{field-4co_8FVg.js → field-Dn7HHx2-.js} +3 -3
  62. package/core/built/admin/assets/activitypub/{hash-DwakMs4X.js → hash-Bm3WxYN4.js} +2 -2
  63. package/core/built/admin/assets/activitypub/inbox-CCjkuow2.js +1284 -0
  64. package/core/built/admin/assets/activitypub/{inbox-ckD4wo_r.js → inbox-d8oJRTf4.js} +2 -2
  65. package/core/built/admin/assets/activitypub/label-1AA_s9Fa.js +16 -0
  66. package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +1 -0
  67. package/core/built/admin/assets/activitypub/{moderation-KCCO7L2J.js → moderation-CnmxEB8w.js} +91 -91
  68. package/core/built/admin/assets/activitypub/{note-BX_Bz5A0.js → note-ZKc_Bnba.js} +24 -24
  69. package/core/built/admin/assets/activitypub/{notifications-BQOYGvRv.js → notifications-CB2tJZau.js} +51 -51
  70. package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-BsJ9E5qf.js} +2 -2
  71. package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-CSUJJIlh.js} +2 -2
  72. package/core/built/admin/assets/activitypub/{preferences-6h43G9_q.js → preferences-0-JZnUkI.js} +1519 -1519
  73. package/core/built/admin/assets/activitypub/{profile-BMN9VVZ2.js → profile-C35cFIhM.js} +256 -256
  74. package/core/built/admin/assets/activitypub/{reply-ZNo04xsO.js → reply-DYK5vVqb.js} +2 -2
  75. package/core/built/admin/assets/activitypub/{routes-3B9mUQ7i.js → routes-BD4vscyc.js} +796 -790
  76. package/core/built/admin/assets/activitypub/{separator-D9taVCCa.js → separator-BbQqTm-p.js} +3 -3
  77. package/core/built/admin/assets/activitypub/settings-BsMROdqh.js +315 -0
  78. package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-O0RL9YCN.js} +2 -2
  79. package/core/built/admin/assets/activitypub/{step-1-BsGGnBuT.js → step-1-BMSx7c_X.js} +6 -6
  80. package/core/built/admin/assets/activitypub/{step-2-teosIS0k.js → step-2-DtPQzlTH.js} +6 -6
  81. package/core/built/admin/assets/activitypub/{step-3-BjD5TokC.js → step-3-CLlOZTyt.js} +11 -11
  82. package/core/built/admin/assets/activitypub/{tabs-Cvwl1wXU.js → tabs-Dxk-b9M-.js} +306 -306
  83. package/core/built/admin/assets/activitypub/{topic-filter-C3LZJc2X.js → topic-filter-5yZH6tId.js} +3 -3
  84. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-Dp9f0gov.js → use-activity-pub-queries-CV50qe03.js} +3 -3
  85. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-C4RI0sa9.js → use-navigate-with-base-path-Bmk5h4lW.js} +220 -220
  86. package/core/built/admin/assets/activitypub/{use-reply-chain-data-C2oNDMUH.js → use-reply-chain-data-Cbvzgjzd.js} +30 -30
  87. package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-BjcoP_HC.js} +7 -5
  88. package/core/built/admin/assets/activitypub/{x-Dxm6pV9X.js → x-DbQZOLiI.js} +2 -2
  89. package/core/built/admin/assets/add-integration-modal-BlF6rs_f.js +1 -0
  90. package/core/built/admin/assets/add-newsletter-modal-CglVQqah.js +1 -0
  91. package/core/built/admin/assets/add-recommendation-modal-wz7BsuaG.js +1 -0
  92. package/core/built/admin/assets/analytics-C4HE1gBg.js +1 -0
  93. package/core/built/admin/assets/{announcement-bar-modal-BTHiy1ZO.js → announcement-bar-modal-Ci4zz8jO.js} +1 -1
  94. package/core/built/admin/assets/{api-keys-Boy5pLbD.js → api-keys-Ck-Yteiv.js} +1 -1
  95. package/core/built/admin/assets/audience-CtbA54Sn.js +1 -0
  96. package/core/built/admin/assets/{automated-emails-te1D5kq-.js → automated-emails-D5VVMND9.js} +1 -1
  97. package/core/built/admin/assets/automations-CzKt5fb8.js +1 -0
  98. package/core/built/admin/assets/bluesky-sharing-BEtJ_gL_.js +1 -0
  99. package/core/built/admin/assets/{box-By_EgGtm.js → box-CdfQAP_F.js} +1 -1
  100. package/core/built/admin/assets/{brand-icon-09YqFZjV.js → brand-icon-CrIGOA3Z.js} +1 -1
  101. package/core/built/admin/assets/{breadcrumb-CcFr_ltt.js → breadcrumb-zUrZrBtW.js} +1 -1
  102. package/core/built/admin/assets/{button-BshWPHvn.js → button-eNMKQWXr.js} +1 -1
  103. package/core/built/admin/assets/{card-CiiE7QfM.js → card--VNbfgo3.js} +1 -1
  104. package/core/built/admin/assets/{chart-BnXyfH11.js → chart-BrhA41Zi.js} +1 -1
  105. package/core/built/admin/assets/chart-helpers-CfBKt0vT.js +1 -0
  106. package/core/built/admin/assets/{checkbox-Dwwr_3Py.js → checkbox-CgOn_tKW.js} +1 -1
  107. package/core/built/admin/assets/{chunk.524.91ecb33e6afc3bd8949b.js → chunk.524.a697dee88db67fdf6021.js} +4 -4
  108. package/core/built/admin/assets/{chunk.582.512549909741883f960c.js → chunk.582.5ed5d124cfca9c259b9c.js} +6 -6
  109. package/core/built/admin/assets/{chunk.16.f76a6314082d813d67e0.js → chunk.598.7e056fabb47ce7dbd73f.js} +14 -14
  110. package/core/built/admin/assets/{code-editor-view-BwIdPy_k.js → code-editor-view-BYNoAoNV.js} +1 -1
  111. package/core/built/admin/assets/{code-editor-view-CtkF82Fv.js → code-editor-view-K2wI-I5f.js} +1 -1
  112. package/core/built/admin/assets/color-picker-Ddw80xX6.js +3 -0
  113. package/core/built/admin/assets/color-picker-field-D9nUhvA7.js +1 -0
  114. package/core/built/admin/assets/combobox-w9j8PPQT.js +1 -0
  115. package/core/built/admin/assets/command-BfpHORBv.js +1 -0
  116. package/core/built/admin/assets/comments-BLmCmyCs.js +1 -0
  117. package/core/built/admin/assets/confirmation-context-CBM8G0Vj.js +1 -0
  118. package/core/built/admin/assets/{container-LiZf3XdY.js → container-BuQG1wuP.js} +1 -1
  119. package/core/built/admin/assets/{content-api-modal-BtpNI92-.js → content-api-modal-TXRz3U7S.js} +1 -1
  120. package/core/built/admin/assets/context-menu-_QMdd3kp.js +1 -0
  121. package/core/built/admin/assets/{copy-field-CglgRZyG.js → copy-field-6N01S07E.js} +1 -1
  122. package/core/built/admin/assets/create-ghost-browse-value-source-qIBymvvf.js +1 -0
  123. package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +1 -0
  124. package/core/built/admin/assets/custom-integration-modal-DmNquxUV.js +1 -0
  125. package/core/built/admin/assets/{data-list-CupF7bjM.js → data-list-CA2jYGPq.js} +1 -1
  126. package/core/built/admin/assets/{date-range-select-JPwM0yUj.js → date-range-select-uvz_DtPh.js} +1 -1
  127. package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +2 -0
  128. package/core/built/admin/assets/{detail-page-D8lx2SdP.js → detail-page-Dnv-CdR1.js} +1 -1
  129. package/core/built/admin/assets/dirty-confirm-dialog-BZ1iytEd.js +1 -0
  130. package/core/built/admin/assets/{disabled-sources-indicator-BTJ2ClQu.js → disabled-sources-indicator-DMRrxsiJ.js} +1 -1
  131. package/core/built/admin/assets/{dist-DzcfoDJ3.js → dist-8YJUlf3B.js} +1 -1
  132. package/core/built/admin/assets/{dist-Cb5RSHSg.js → dist-B3keLvjj.js} +1 -1
  133. package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
  134. package/core/built/admin/assets/{dist-BT_Z-eCn.js → dist-Dgt2cr6a.js} +1 -1
  135. package/core/built/admin/assets/domain-BNYrt0aD.js +1 -0
  136. package/core/built/admin/assets/dropzone-D7nFdAe5.js +5 -0
  137. package/core/built/admin/assets/edit-profile-C5Asm4LA.js +1 -0
  138. package/core/built/admin/assets/editor-REokrzci.js +8 -0
  139. package/core/built/admin/assets/{embed-signup-form-modal-BFTPf04i.js → embed-signup-form-modal-Ds6mPEi5.js} +1 -1
  140. package/core/built/admin/assets/{empty-indicator-WB1WgT9h.js → empty-indicator-D9QdpuNX.js} +1 -1
  141. package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
  142. package/core/built/admin/assets/explore-UY10Egsp.js +1 -0
  143. package/core/built/admin/assets/feature-image-placeholder-D1iF_8bc.js +1 -0
  144. package/core/built/admin/assets/feed-81C8mJWR.js +1 -0
  145. package/core/built/admin/assets/{field-LmNk7wZh.js → field-Dihp5p18.js} +1 -1
  146. package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
  147. package/core/built/admin/assets/filter-sources-BO9xUPj7.js +1 -0
  148. package/core/built/admin/assets/filters-CRUWecfY.js +1 -0
  149. package/core/built/admin/assets/filters-DirqNXjH.js +1 -0
  150. package/core/built/admin/assets/first-promoter-modal-CT6MxYZb.js +1 -0
  151. package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8eabfc1ece81b03cf7b90780d31487e8.css} +1 -1
  152. package/core/built/admin/assets/{ghost-403cb39badad3df411fc0ba27d220047.js → ghost-c2848508292937415f20f5a830eea59e.js} +31 -28
  153. package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css} +1 -1
  154. package/core/built/admin/assets/gift-link-modal-CQVAye17.js +1 -0
  155. package/core/built/admin/assets/{grid-CUciBfjf.js → grid-Dj29PY05.js} +1 -1
  156. package/core/built/admin/assets/growth-BWIu-6xj.js +3 -0
  157. package/core/built/admin/assets/growth-DU29oxeA.js +1 -0
  158. package/core/built/admin/assets/{heading-CRZZ2FUe.js → heading-Dh5SFVjL.js} +1 -1
  159. package/core/built/admin/assets/history-modal-CFd1Hvx6.js +1 -0
  160. package/core/built/admin/assets/html-field-CJBCLTvv.js +1 -0
  161. package/core/built/admin/assets/{images-C0XvUYMb.js → images-scfkWZiK.js} +1 -1
  162. package/core/built/admin/assets/{inbox-DZXDUt4s.js → inbox-Mcp4DXZN.js} +14 -14
  163. package/core/built/admin/assets/index-6MkPdCtr.js +4 -0
  164. package/core/built/admin/assets/index-Bn7QhcZu.css +2 -0
  165. package/core/built/admin/assets/index.es-CNnuNH0d.js +1 -0
  166. package/core/built/admin/assets/{inline-Cw-gigDW.js → inline-BWSs_VzR.js} +1 -1
  167. package/core/built/admin/assets/{input-group-BPAxETpQ.js → input-group-CMhe2DvY.js} +1 -1
  168. package/core/built/admin/assets/{input-surface-BdOe4KzO.js → input-surface-BsrIZeSF.js} +1 -1
  169. package/core/built/admin/assets/{integration-header-B4DyXV06.js → integration-header-Bm6IDb1A.js} +1 -1
  170. package/core/built/admin/assets/invite-user-modal-DfheMXs0.js +1 -0
  171. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
  172. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
  173. package/core/built/admin/assets/koenig-lexical/style.css +1 -1
  174. package/core/built/admin/assets/{koenig-lexical-60O_8aiO.js → koenig-lexical-DBSqAuiI.js} +3 -3
  175. package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +2 -0
  176. package/core/built/admin/assets/kpi-card-DBRMfGOW.js +1 -0
  177. package/core/built/admin/assets/{kpi-card-fUHZlzBX.js → kpi-card-DFafD1qq.js} +1 -1
  178. package/core/built/admin/assets/kpi-helpers-Cv8i8hns.js +1 -0
  179. package/core/built/admin/assets/kpi-tabs-BJItvLYm.js +1 -0
  180. package/core/built/admin/assets/{label-VIG969IC.js → label-BnSCvffu.js} +1 -1
  181. package/core/built/admin/assets/label-picker-KVoo91sh.js +1 -0
  182. package/core/built/admin/assets/layout-Bjv2Ll-5.js +1 -0
  183. package/core/built/admin/assets/{link-helpers-Dtq9fxQg.js → link-helpers-MVDdnFCY.js} +1 -1
  184. package/core/built/admin/assets/{list-page-CdsnQR9N.js → list-page-BucXJ0hi.js} +1 -1
  185. package/core/built/admin/assets/member-custom-fields-DDQV6QeG.js +1 -0
  186. package/core/built/admin/assets/member-detail-CdJF6Agw.js +1 -0
  187. package/core/built/admin/assets/members-DjQbq9Z2.js +4 -0
  188. package/core/built/admin/assets/{modal-page-BMTzFHVO.js → modal-page-xCivTMvI.js} +1 -1
  189. package/core/built/admin/assets/moderation-AgCIEso4.js +1 -0
  190. package/core/built/admin/assets/{multi-select-combobox-CGohnsxJ.js → multi-select-combobox-BcITkUVr.js} +1 -1
  191. package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +1 -0
  192. package/core/built/admin/assets/newsletter-BMMAuvgq.js +1 -0
  193. package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +1 -0
  194. package/core/built/admin/assets/{newsletter-emails-D5ql1rud.js → newsletter-emails-DKzwafuh.js} +1 -1
  195. package/core/built/admin/assets/newsletters-BRBb_uQC.js +1 -0
  196. package/core/built/admin/assets/note-DyGHtS9a.js +1 -0
  197. package/core/built/admin/assets/notifications-ZgWniKna.js +1 -0
  198. package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
  199. package/core/built/admin/assets/offers-container-modal-B-E8TIWr.js +1 -0
  200. package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-CzIsre-d.js} +1 -1
  201. package/core/built/admin/assets/onboarding-qg8Akovm.js +1 -0
  202. package/core/built/admin/assets/{onboarding-route-Cu_ROWPn.js → onboarding-route-CaavZulg.js} +1 -1
  203. package/core/built/admin/assets/overview-BW8GLeYi.js +1 -0
  204. package/core/built/admin/assets/overview-BkXZNzaZ.js +1 -0
  205. package/core/built/admin/assets/{page-header-Cfds8pCg.js → page-header-De0VzKBi.js} +1 -1
  206. package/core/built/admin/assets/pagemenu-B5KKCpbA.js +1 -0
  207. package/core/built/admin/assets/pages-jgaFm69d.js +1 -0
  208. package/core/built/admin/assets/pages-route-CIUAQxtY.js +1 -0
  209. package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
  210. package/core/built/admin/assets/{pintura-modal-TDTrJ89x.js → pintura-modal-BZ2J9w3n.js} +1 -1
  211. package/core/built/admin/assets/portal-frame-B6Hr9wHc.js +1 -0
  212. package/core/built/admin/assets/portal-modal-D4ceei3t.js +1 -0
  213. package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +1 -0
  214. package/core/built/admin/assets/post-analytics-ot7f6pih.js +1 -0
  215. package/core/built/admin/assets/{post-analytics-provider-Cu3jNeFh.js → post-analytics-provider-Bo1gmYaP.js} +1 -1
  216. package/core/built/admin/assets/{post-share-modal-P1_M6o-H.js → post-share-modal-C-PaYZ_e.js} +1 -1
  217. package/core/built/admin/assets/posts-DuGazgTb.js +1 -0
  218. package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +2 -0
  219. package/core/built/admin/assets/posts-route-D6xmS7Vf.js +1 -0
  220. package/core/built/admin/assets/preferences-C5YJLp74.js +5 -0
  221. package/core/built/admin/assets/{preview-chrome-BOVO6_PE.js → preview-chrome-BljUBlOk.js} +1 -1
  222. package/core/built/admin/assets/{preview-modal-MlTjP8UU.js → preview-modal-BfeFqi5y.js} +1 -1
  223. package/core/built/admin/assets/profile-B8mx2yAu.js +1 -0
  224. package/core/built/admin/assets/{radio-group-CjtmsCPl.js → radio-group-DczlLKOn.js} +1 -1
  225. package/core/built/admin/assets/{router-provider-CJC58zTy.js → router-provider-B0gZyiGJ.js} +1 -1
  226. package/core/built/admin/assets/{schemas-CRsC53ls.js → schemas-DppqmpeJ.js} +2 -2
  227. package/core/built/admin/assets/select-cb0t_TAJ.js +1 -0
  228. package/core/built/admin/assets/settings-DZqxR0Da.js +1 -0
  229. package/core/built/admin/assets/settings-DvfTNdHQ.js +4 -0
  230. package/core/built/admin/assets/{settings-breadcrumbs-sLDNedBW.js → settings-breadcrumbs-DLzbRRSt.js} +1 -1
  231. package/core/built/admin/assets/{shade-app-F7WxOyfk-nufqw1hd.js → shade-app-CcS3f8MZ-vwURazk8.js} +2 -2
  232. package/core/built/admin/assets/share-modal-D2-Bz1_I.js +1 -0
  233. package/core/built/admin/assets/{skeleton-D7hwjdry.js → skeleton-Ce9GgUpf.js} +1 -1
  234. package/core/built/admin/assets/slack-modal-DYDTUria.js +1 -0
  235. package/core/built/admin/assets/sort-button-VVeQjjbu.js +1 -0
  236. package/core/built/admin/assets/{source-icon-BPsD5ees.js → source-icon-Ck5_5Kgq.js} +1 -1
  237. package/core/built/admin/assets/{stack-CIfOAAVf.js → stack-B2eszIiq.js} +1 -1
  238. package/core/built/admin/assets/stats-Rjmk5owg.js +1 -0
  239. package/core/built/admin/assets/stats-view-BGGmkmgv.js +1 -0
  240. package/core/built/admin/assets/{step-1-CMta6YFO.js → step-1-CsaBjeN4.js} +1 -1
  241. package/core/built/admin/assets/{step-2-BG04nh5q.js → step-2-D2vnxOOM.js} +1 -1
  242. package/core/built/admin/assets/{step-3-ZnMaVIPj.js → step-3-wBoLdGEb.js} +2 -2
  243. package/core/built/admin/assets/{stripe-button-DDoUKN5a.js → stripe-button-CDgLz0S0.js} +1 -1
  244. package/core/built/admin/assets/stripe-connect-modal-CSjMJetP.js +1 -0
  245. package/core/built/admin/assets/{switch-C9djUIMO.js → switch-CI4gLWqb.js} +1 -1
  246. package/core/built/admin/assets/{table-CrqQtkR2.js → table-Do6iQ83Q.js} +1 -1
  247. package/core/built/admin/assets/{tabs-CGisSbWq.js → tabs-D2yWLMp_.js} +1 -1
  248. package/core/built/admin/assets/tag-detail-BhXlOsCa.js +2 -0
  249. package/core/built/admin/assets/tags-ldukRApe.js +1 -0
  250. package/core/built/admin/assets/testimonials-modal-CtnZBJKl.js +1 -0
  251. package/core/built/admin/assets/{text-DU9Hv33_.js → text-BQDSDsKs.js} +1 -1
  252. package/core/built/admin/assets/{textarea-d9qEkMOP.js → textarea-DJ1Q5V39.js} +1 -1
  253. package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +1 -0
  254. package/core/built/admin/assets/{toggle-group-CtKZURJd.js → toggle-group-ByUJsxwD.js} +1 -1
  255. package/core/built/admin/assets/{topic-filter-D8JT8Tq1.js → topic-filter-BgC3g5_v.js} +1 -1
  256. package/core/built/admin/assets/{transistor-modal-6PUf0idx.js → transistor-modal-Drr0SNfo.js} +1 -1
  257. package/core/built/admin/assets/trend-badge-6ye93YAW.js +1 -0
  258. package/core/built/admin/assets/{unsplash-modal-BPuDBPja.js → unsplash-modal-p6AoQoWp.js} +1 -1
  259. package/core/built/admin/assets/{use-analytics-data--opuzwcz.js → use-analytics-data-CWCMAESo.js} +1 -1
  260. package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
  261. package/core/built/admin/assets/use-check-theme-limit-error-Cw3WQHy8.js +2 -0
  262. package/core/built/admin/assets/use-filter-params-BIni9taJ.js +1 -0
  263. package/core/built/admin/assets/use-growth-stats-DCVG3s9w.js +1 -0
  264. package/core/built/admin/assets/{use-limiter-TDSNiHPB.js → use-limiter-D2WRRx0H.js} +1 -1
  265. package/core/built/admin/assets/{use-navigate-with-base-path-CK989MlC.js → use-navigate-with-base-path-BHPA228f.js} +1 -1
  266. package/core/built/admin/assets/{use-pintura-config-CnCGrDFk.js → use-pintura-config-h-J0Lvta.js} +1 -1
  267. package/core/built/admin/assets/{use-pintura-editor-ntphvtUd.js → use-pintura-editor-BfN1UwzH.js} +1 -1
  268. package/core/built/admin/assets/use-post-referrers-Czg47m8s.js +1 -0
  269. package/core/built/admin/assets/use-reply-chain-data-ns0caK8k.js +1 -0
  270. package/core/built/admin/assets/{use-router-history-entry-BwfQS2mq.js → use-router-history-entry-uldn7zJO.js} +1 -1
  271. package/core/built/admin/assets/{use-setting-group-DQxV42-O.js → use-setting-group-DpcuCUDg.js} +1 -1
  272. package/core/built/admin/assets/use-settings-navigation-CZjGrhsp.js +1 -0
  273. package/core/built/admin/assets/{use-simple-pagination-B2TdNX67.js → use-simple-pagination-DzJWobPR.js} +1 -1
  274. package/core/built/admin/assets/{use-staff-users-QrR9voeC.js → use-staff-users-Csipb-O0.js} +1 -1
  275. package/core/built/admin/assets/use-tinybird-query-CSnGXzZh.js +1 -0
  276. package/core/built/admin/assets/use-tinybird-token-DTOKRR7e.js +1 -0
  277. package/core/built/admin/assets/{use-unsaved-changes-guard-DpYJcj1d.js → use-unsaved-changes-guard-CBzs-KPi.js} +1 -1
  278. package/core/built/admin/assets/{use-url-input-WnIqckzS.js → use-url-input-DFr8lCbS.js} +1 -1
  279. package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +1 -0
  280. package/core/built/admin/assets/users-Cg-OeGzC.js +1 -0
  281. package/core/built/admin/assets/{virtual-list-D3MfPNOh.js → virtual-list-BW-padMe.js} +1 -1
  282. package/core/built/admin/assets/web-C1v33ehA.js +2 -0
  283. package/core/built/admin/assets/web-C4BKzn2T.js +1 -0
  284. package/core/built/admin/assets/{zapier-modal-BO6PS_Vd.js → zapier-modal-BBFfI6Ml.js} +1 -1
  285. package/core/built/admin/index.html +24 -30
  286. package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
  287. package/core/frontend/helpers/cancel_link.js +2 -2
  288. package/core/frontend/helpers/pagination.js +2 -2
  289. package/core/frontend/helpers/social_accounts.js +2 -2
  290. package/core/frontend/public/ghost.css +1 -1
  291. package/core/frontend/public/ghost.min.css +1 -1
  292. package/core/frontend/services/rendering/renderer.js +15 -0
  293. package/core/frontend/services/sitemap/user-map-generator.js +9 -2
  294. package/core/server/adapters/storage/LocalStorageBase.js +8 -5
  295. package/core/server/api/endpoints/db.js +19 -5
  296. package/core/server/api/endpoints/index.js +6 -2
  297. package/core/server/api/endpoints/mail.js +1 -1
  298. package/core/server/api/endpoints/member-metafields.js +89 -0
  299. package/core/server/api/endpoints/members.js +4 -4
  300. package/core/server/api/endpoints/posts.js +4 -3
  301. package/core/server/api/endpoints/tiers-checkout-config.js +59 -0
  302. package/core/server/api/endpoints/utils/serializers/input/members.js +4 -7
  303. package/core/server/api/endpoints/utils/serializers/output/config.js +1 -0
  304. package/core/server/api/endpoints/utils/serializers/output/index.js +6 -2
  305. package/core/server/api/endpoints/utils/serializers/output/{member-custom-fields.js → member-metafields.js} +2 -4
  306. package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
  307. package/core/server/api/endpoints/utils/serializers/output/tiers-checkout-config.js +12 -0
  308. package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
  309. package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
  310. package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
  311. package/core/server/data/exporter/table-lists.js +3 -0
  312. package/core/server/data/importer/content-files.js +57 -0
  313. package/core/server/data/importer/handlers/image.js +2 -2
  314. package/core/server/data/importer/import-archive.js +131 -0
  315. package/core/server/data/importer/import-manager.js +18 -180
  316. package/core/server/data/importer/importers/content-file-importer.js +66 -4
  317. package/core/server/data/importer/importers/data/data-importer.js +3 -3
  318. package/core/server/data/migrations/utils/migrations.js +1 -1
  319. package/core/server/data/migrations/versions/6.61/2026-08-19-14-30-20-add-written-by-to-members-custom-field-values.js +42 -0
  320. package/core/server/data/migrations/versions/6.61/2026-08-19-14-38-53-add-members-custom-field-bindings-table.js +26 -0
  321. package/core/server/data/migrations/versions/6.61/2026-08-19-14-48-06-add-tier-checkout-field-tables.js +35 -0
  322. package/core/server/data/schema/schema.js +90 -0
  323. package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -3
  324. package/core/server/data/seeders/importers/automation-actions-importer.js +3 -3
  325. package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -3
  326. package/core/server/data/seeders/importers/automation-runs-importer.js +5 -5
  327. package/core/server/data/seeders/importers/automations-importer.js +3 -3
  328. package/core/server/data/seeders/importers/comment-reports-importer.js +1 -1
  329. package/core/server/data/seeders/importers/comments-importer.js +1 -1
  330. package/core/server/data/seeders/importers/email-batches-importer.js +1 -1
  331. package/core/server/data/seeders/importers/email-recipients-importer.js +1 -1
  332. package/core/server/data/seeders/importers/emails-importer.js +1 -1
  333. package/core/server/data/seeders/importers/labels-importer.js +1 -1
  334. package/core/server/data/seeders/importers/members-click-events-importer.js +1 -1
  335. package/core/server/data/seeders/importers/members-created-events-importer.js +1 -1
  336. package/core/server/data/seeders/importers/members-feedback-importer.js +1 -1
  337. package/core/server/data/seeders/importers/members-importer.js +1 -1
  338. package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
  339. package/core/server/data/seeders/importers/members-products-importer.js +1 -1
  340. package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
  341. package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
  342. package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
  343. package/core/server/data/seeders/importers/members-subscribe-events-importer.js +1 -1
  344. package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
  345. package/core/server/data/seeders/importers/offer-redemptions-importer.js +1 -1
  346. package/core/server/data/seeders/importers/offers-importer.js +1 -1
  347. package/core/server/data/seeders/importers/posts-importer.js +1 -1
  348. package/core/server/data/seeders/importers/table-importer.js +2 -2
  349. package/core/server/data/seeders/importers/tags-importer.js +1 -1
  350. package/core/server/data/seeders/importers/users-importer.js +1 -1
  351. package/core/server/data/seeders/importers/web-mentions-importer.js +1 -1
  352. package/core/server/data/seeders/utils/random.js +3 -3
  353. package/core/server/ghost-server.js +3 -2
  354. package/core/server/lib/db-types/boolean.js +18 -0
  355. package/core/server/lib/image/image-size.js +24 -0
  356. package/core/server/lib/package-json/parse.js +2 -2
  357. package/core/server/lib/promise-pool.js +31 -0
  358. package/core/server/models/member.js +6 -17
  359. package/core/server/models/post.js +2 -2
  360. package/core/server/models/relations/authors.js +2 -16
  361. package/core/server/models/user.js +9 -2
  362. package/core/server/services/auth/passwordreset.js +13 -0
  363. package/core/server/services/automations/database-automations-repository.js +26 -26
  364. package/core/server/services/automations/poll.js +1 -0
  365. package/core/server/services/comments/comments-controller.js +30 -9
  366. package/core/server/services/comments/comments-service.js +55 -5
  367. package/core/server/services/content-import/csv/formula.js +12 -0
  368. package/core/server/services/content-import/csv/index.js +6 -1
  369. package/core/server/services/content-import/csv/parse.js +27 -6
  370. package/core/server/services/content-import/csv/serialize.js +13 -0
  371. package/core/server/services/content-import/import/assets.js +74 -0
  372. package/core/server/services/content-import/import/completion-email.js +164 -0
  373. package/core/server/services/content-import/import/errors-file.js +36 -0
  374. package/core/server/services/content-import/import/importer.js +204 -54
  375. package/core/server/services/content-import/import/local-media-url.js +63 -0
  376. package/core/server/services/content-import/import/media.js +254 -0
  377. package/core/server/services/content-import/import/post-data.js +1 -1
  378. package/core/server/services/content-import/import/post-link.js +11 -0
  379. package/core/server/services/content-import/import/post-repository.js +100 -0
  380. package/core/server/services/content-import/import/reader.js +9 -2
  381. package/core/server/services/content-import/import/relations.js +149 -0
  382. package/core/server/services/content-import/import/report.js +34 -0
  383. package/core/server/services/content-import/import/row.js +11 -2
  384. package/core/server/services/content-import/import/schema.js +30 -0
  385. package/core/server/services/content-import/import/source.js +125 -0
  386. package/core/server/services/content-import/import/staged-file.js +29 -0
  387. package/core/server/services/content-import/import/store.js +25 -2
  388. package/core/server/services/content-import/index.js +58 -34
  389. package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
  390. package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
  391. package/core/server/services/custom-redirects/redirects-service.js +1 -1
  392. package/core/server/services/custom-redirects/validation.js +1 -1
  393. package/core/server/services/email-address/email-address-parser.js +30 -38
  394. package/core/server/services/email-address/email-address-service.js +37 -4
  395. package/core/server/services/email-analytics/email-analytics-service.js +8 -1
  396. package/core/server/services/email-analytics/index.js +2 -7
  397. package/core/server/services/email-analytics/lib/queries.js +7 -1
  398. package/core/server/services/email-service/batch-sending-service.js +0 -4
  399. package/core/server/services/email-service/email-renderer.js +14 -4
  400. package/core/server/services/email-service/email-service-wrapper.js +0 -9
  401. package/core/server/services/email-service/mailgun-email-provider.js +20 -9
  402. package/core/server/services/gift-links/schema.js +3 -3
  403. package/core/server/services/gifts/CONTEXT.md +1 -1
  404. package/core/server/services/gifts/constants.js +1 -1
  405. package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
  406. package/core/server/services/gifts/gift-bookshelf-repository.js +2 -2
  407. package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +18 -9
  408. package/core/server/services/gifts/gift-delivery-schema.js +5 -5
  409. package/core/server/services/gifts/gift-delivery-service.js +4 -0
  410. package/core/server/services/gifts/gift-email-service.js +0 -1
  411. package/core/server/services/gifts/gift-schema.js +10 -10
  412. package/core/server/services/gifts/gift-service.js +4 -2
  413. package/core/server/services/gifts/index.js +3 -3
  414. package/core/server/services/gifts/jobs/index.js +4 -4
  415. package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
  416. package/core/server/services/invitations/accept.js +4 -2
  417. package/core/server/services/jobs/job-service.js +0 -9
  418. package/core/server/services/jobs-service/register-job-handlers.js +25 -15
  419. package/core/server/services/lib/mailgun-client.js +44 -8
  420. package/core/server/services/mail/ghost-mailer.js +4 -4
  421. package/core/server/services/media-inliner/external-media-inliner-job.js +12 -0
  422. package/core/server/services/media-inliner/external-media-inliner.js +103 -29
  423. package/core/server/services/media-inliner/service.js +13 -41
  424. package/core/server/services/media-inliner/types.js +2 -0
  425. package/core/server/services/member-welcome-emails/service.js +7 -7
  426. package/core/server/services/members/api.js +1 -0
  427. package/core/server/services/members/import-export/csv/parse.js +0 -7
  428. package/core/server/services/members/import-export/export/exporter.js +4 -6
  429. package/core/server/services/members/import-export/import/completion-email.js +2 -5
  430. package/core/server/services/members/import-export/import/importer.js +21 -5
  431. package/core/server/services/members/import-export/import/row.js +0 -3
  432. package/core/server/services/members/import-export/import/stripe-utils.js +6 -1
  433. package/core/server/services/members/import-export/index.js +10 -12
  434. package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
  435. package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
  436. package/core/server/services/members/jobs/index.js +48 -24
  437. package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
  438. package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
  439. package/core/server/services/members/members-api/members-api.js +9 -6
  440. package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
  441. package/core/server/services/members/members-api/services/member-bread-service.js +53 -63
  442. package/core/server/services/members/members-api/services/payments-service.js +47 -1
  443. package/core/server/services/members/service.js +1 -5
  444. package/core/server/services/members-custom-fields/actions.js +27 -1
  445. package/core/server/services/members-custom-fields/bindings-service.js +120 -0
  446. package/core/server/services/members-custom-fields/codec.js +3 -4
  447. package/core/server/services/members-custom-fields/definitions-service.js +165 -22
  448. package/core/server/services/members-custom-fields/filter.js +75 -93
  449. package/core/server/services/members-custom-fields/index.js +11 -4
  450. package/core/server/services/members-custom-fields/key.js +2 -1
  451. package/core/server/services/members-custom-fields/models.js +1 -1
  452. package/core/server/services/members-custom-fields/queries.js +5 -0
  453. package/core/server/services/members-custom-fields/schema.js +37 -6
  454. package/core/server/services/members-custom-fields/serializers.js +3 -5
  455. package/core/server/services/members-custom-fields/values-service.js +65 -31
  456. package/core/server/services/mentions/mention-controller.js +23 -21
  457. package/core/server/services/mentions/process-webmention-job.js +16 -0
  458. package/core/server/services/mentions/service.js +8 -4
  459. package/core/server/services/public-config/config.js +7 -0
  460. package/core/server/services/route-settings/validation-errors.js +1 -1
  461. package/core/server/services/route-settings/yaml-parser.js +1 -1
  462. package/core/server/services/settings/settings-service.js +1 -8
  463. package/core/server/services/settings-helpers/settings-helpers.js +4 -6
  464. package/core/server/services/slack-ping/slack-ping-service.js +1 -1
  465. package/core/server/services/staff/staff-service-emails.js +2 -2
  466. package/core/server/services/stripe/config.js +14 -8
  467. package/core/server/services/stripe/service.js +10 -1
  468. package/core/server/services/stripe/services/checkout/completed-session.js +68 -0
  469. package/core/server/services/stripe/services/checkout/session-options.js +100 -0
  470. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +71 -1
  471. package/core/server/services/stripe/stripe-api.js +31 -0
  472. package/core/server/services/stripe/stripe-service.js +26 -9
  473. package/core/server/services/stripe/webhook-controller.js +10 -0
  474. package/core/server/services/tier-checkout-config/codec.js +99 -0
  475. package/core/server/services/tier-checkout-config/index.js +26 -0
  476. package/core/server/services/tier-checkout-config/models.js +63 -0
  477. package/core/server/services/tier-checkout-config/queries.js +80 -0
  478. package/core/server/services/tier-checkout-config/schema.js +33 -0
  479. package/core/server/services/tier-checkout-config/serializers.js +128 -0
  480. package/core/server/services/tier-checkout-config/service.js +304 -0
  481. package/core/server/services/tiers/service.js +21 -0
  482. package/core/server/services/update-check/index.js +13 -13
  483. package/core/server/services/update-check/jobs/update-check-job.js +7 -0
  484. package/core/server/services/update-check/update-check-service.js +2 -2
  485. package/core/server/web/admin/controller.js +1 -1
  486. package/core/server/web/api/endpoints/admin/routes.js +34 -20
  487. package/core/server/web/api/middleware/upload.js +2 -2
  488. package/core/server/web/parent/app.js +5 -0
  489. package/core/server/web/parent/middleware/filter-query-parameters.js +94 -0
  490. package/core/server/web/parent/middleware/index.js +1 -0
  491. package/core/server/web/query-parameter-policy/index.js +9 -0
  492. package/core/server/web/query-parameter-policy/policy.json +58 -0
  493. package/core/server/web/query-parameter-policy/schema.js +38 -0
  494. package/core/shared/config/defaults.json +3 -0
  495. package/core/shared/config/env/config.testing-mysql.json +4 -1
  496. package/core/shared/config/env/config.testing.json +3 -0
  497. package/core/shared/config/overrides.json +4 -2
  498. package/core/shared/labs.js +9 -15
  499. package/package.json +25 -25
  500. package/pnpm-lock.yaml +741 -108
  501. package/pnpm-workspace.yaml +12 -9
  502. package/tsconfig.json +2 -3
  503. package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
  504. package/core/built/admin/assets/Combination-gnJO3vIw.js +0 -41
  505. package/core/built/admin/assets/account-migration-8qTEI1i1.js +0 -1
  506. package/core/built/admin/assets/activitypub/inbox-DQ5CwLma.js +0 -1284
  507. package/core/built/admin/assets/activitypub/label-B72sfvEU.js +0 -16
  508. package/core/built/admin/assets/activitypub/layout-CXajhRsL.js +0 -1
  509. package/core/built/admin/assets/activitypub/settings-71gCm3SO.js +0 -315
  510. package/core/built/admin/assets/add-integration-modal--YpTq3c0.js +0 -1
  511. package/core/built/admin/assets/add-newsletter-modal-C8Ix2hol.js +0 -1
  512. package/core/built/admin/assets/add-recommendation-modal-CScuNFz4.js +0 -1
  513. package/core/built/admin/assets/alert-dialog-wEbVxANM.js +0 -1
  514. package/core/built/admin/assets/analytics-Cnji45H5.js +0 -1
  515. package/core/built/admin/assets/app-utils-D2Wp7ZSS.js +0 -1
  516. package/core/built/admin/assets/audience-DTLMXAFH.js +0 -1
  517. package/core/built/admin/assets/automations-BWpRCZ_f.js +0 -1
  518. package/core/built/admin/assets/bluesky-sharing-DHOpQxff.js +0 -1
  519. package/core/built/admin/assets/chart-helpers-B3OqQ6Ez.js +0 -1
  520. package/core/built/admin/assets/color-picker-Bmo1r9Fc.js +0 -3
  521. package/core/built/admin/assets/color-picker-field-DywowPhF.js +0 -1
  522. package/core/built/admin/assets/combobox-C4QCh6Yw.js +0 -1
  523. package/core/built/admin/assets/command-DcLbSifC.js +0 -1
  524. package/core/built/admin/assets/comments-Bxy_3WyE.js +0 -1
  525. package/core/built/admin/assets/confirmation-context-CLFHhyyH.js +0 -1
  526. package/core/built/admin/assets/custom-field-type-option-D0PXoSnP.js +0 -10
  527. package/core/built/admin/assets/custom-integration-modal-DCIHKN33.js +0 -1
  528. package/core/built/admin/assets/design-and-theme-modal-CZ-PkW7o.js +0 -23
  529. package/core/built/admin/assets/dialog-Db7c164G.js +0 -1
  530. package/core/built/admin/assets/dirty-confirm-dialog-CmARpZQS.js +0 -1
  531. package/core/built/admin/assets/dist-QLZuGq7o2.js +0 -1
  532. package/core/built/admin/assets/domain-D0SAtc-T.js +0 -1
  533. package/core/built/admin/assets/dropzone-CaPOeDRZ.js +0 -5
  534. package/core/built/admin/assets/edit-profile-By7v5XW5.js +0 -1
  535. package/core/built/admin/assets/editor-COXn4Atu.js +0 -8
  536. package/core/built/admin/assets/explore-BsNYrubV.js +0 -1
  537. package/core/built/admin/assets/feed-8RR1O1bK.js +0 -1
  538. package/core/built/admin/assets/filter-sources-4C0kF1NU.js +0 -1
  539. package/core/built/admin/assets/filters-CV5jCb9n.js +0 -1
  540. package/core/built/admin/assets/filters-Dmr0PoqU.js +0 -1
  541. package/core/built/admin/assets/first-promoter-modal-OCzSaL3B.js +0 -1
  542. package/core/built/admin/assets/gift-link-modal-0mzjn5oF.js +0 -1
  543. package/core/built/admin/assets/growth-55vWlGT_.js +0 -1
  544. package/core/built/admin/assets/growth-DA5AluYw.js +0 -3
  545. package/core/built/admin/assets/history-modal-G1s5hHz7.js +0 -1
  546. package/core/built/admin/assets/html-field-BegxsWpw.js +0 -1
  547. package/core/built/admin/assets/index-CDP9tIOk.js +0 -4
  548. package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
  549. package/core/built/admin/assets/index.es-BMUCj9TR.js +0 -1
  550. package/core/built/admin/assets/input-UHrKuvBT.js +0 -1
  551. package/core/built/admin/assets/invite-user-modal-BrAkxEO3.js +0 -1
  552. package/core/built/admin/assets/koenig-loader-DwVY_V-H.js +0 -1
  553. package/core/built/admin/assets/kpi-card-LERqWBoY.js +0 -1
  554. package/core/built/admin/assets/kpi-helpers-DB3ouhYF.js +0 -1
  555. package/core/built/admin/assets/kpi-tabs-BGvtMD8S.js +0 -1
  556. package/core/built/admin/assets/label-picker-BBWykUeA.js +0 -1
  557. package/core/built/admin/assets/layout-Di5K7doM.js +0 -1
  558. package/core/built/admin/assets/loading-indicator-SFTqyYVE.js +0 -1
  559. package/core/built/admin/assets/member-custom-fields-gOv6Hoh3.js +0 -1
  560. package/core/built/admin/assets/member-detail-D7mZDDp3.js +0 -1
  561. package/core/built/admin/assets/member-route-0Aa52Ntz.js +0 -1
  562. package/core/built/admin/assets/members-DvZG9t69.js +0 -2
  563. package/core/built/admin/assets/moderation-DzzPLDyl.js +0 -1
  564. package/core/built/admin/assets/navigation-modal-CI8Bx5vn.js +0 -1
  565. package/core/built/admin/assets/newsletter-Bg3cq5U-.js +0 -1
  566. package/core/built/admin/assets/newsletter-detail-modal-DQU3r4g2.js +0 -1
  567. package/core/built/admin/assets/newsletters-DMDfZbMS.js +0 -1
  568. package/core/built/admin/assets/note-i8k6pjaw.js +0 -1
  569. package/core/built/admin/assets/notifications-psA_2Ph4.js +0 -1
  570. package/core/built/admin/assets/offers-container-modal-BWfjSdyg.js +0 -1
  571. package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
  572. package/core/built/admin/assets/overview-C-6xOLaf.js +0 -1
  573. package/core/built/admin/assets/overview-DaLhpRrC.js +0 -1
  574. package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
  575. package/core/built/admin/assets/popover-CFgqFDvC.js +0 -1
  576. package/core/built/admin/assets/portal-frame-DjBF-uy2.js +0 -1
  577. package/core/built/admin/assets/portal-modal-Cf0pBAk8.js +0 -1
  578. package/core/built/admin/assets/post-analytics-Buz1COoY.js +0 -1
  579. package/core/built/admin/assets/post-analytics-header-B-P0eFnT.js +0 -1
  580. package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
  581. package/core/built/admin/assets/preferences-Cq1aOiLy.js +0 -5
  582. package/core/built/admin/assets/profile-D6dHYf2o.js +0 -1
  583. package/core/built/admin/assets/select-D_RQaXgt.js +0 -1
  584. package/core/built/admin/assets/settings-B5hJKZ5C.js +0 -1
  585. package/core/built/admin/assets/settings-DIo_F-MF.js +0 -4
  586. package/core/built/admin/assets/share-modal-CLBfj377.js +0 -1
  587. package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
  588. package/core/built/admin/assets/slack-modal-Bt9I_tEo.js +0 -1
  589. package/core/built/admin/assets/sort-button-DMBXF7tP.js +0 -1
  590. package/core/built/admin/assets/stats-B_YkF7bp.js +0 -1
  591. package/core/built/admin/assets/stats-view-BOCp9RG8.js +0 -1
  592. package/core/built/admin/assets/stripe-connect-modal-BeSukI8q.js +0 -1
  593. package/core/built/admin/assets/tag-detail-2a3BfzTW.js +0 -2
  594. package/core/built/admin/assets/tags-Dp2_8UzL.js +0 -1
  595. package/core/built/admin/assets/testimonials-modal-C9nrypNo.js +0 -1
  596. package/core/built/admin/assets/tier-detail-modal-CxRwgTPR.js +0 -1
  597. package/core/built/admin/assets/use-check-theme-limit-error-C9-QhGdV.js +0 -1
  598. package/core/built/admin/assets/use-filter-params-CSpAvgD2.js +0 -1
  599. package/core/built/admin/assets/use-growth-stats-BOmoPDHJ.js +0 -1
  600. package/core/built/admin/assets/use-post-referrers-CbXLFIex.js +0 -1
  601. package/core/built/admin/assets/use-reply-chain-data-JYFKSO83.js +0 -1
  602. package/core/built/admin/assets/use-settings-navigation-BBzro2-V.js +0 -1
  603. package/core/built/admin/assets/use-tinybird-query-D2Aeoty5.js +0 -39
  604. package/core/built/admin/assets/use-tinybird-token-DBuwMmln.js +0 -1
  605. package/core/built/admin/assets/user-detail-modal-59mBJqxN.js +0 -1
  606. package/core/built/admin/assets/users-5msE1WMs.js +0 -1
  607. package/core/built/admin/assets/web-DUuX7nE2.js +0 -2
  608. package/core/built/admin/assets/web-N8ucs2aG.js +0 -1
  609. package/core/server/api/endpoints/member-custom-fields.js +0 -100
  610. package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
  611. package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
  612. package/core/server/services/update-check/run-update-check.js +0 -63
  613. /package/core/server/lib/{db-date.js → db-types/date.js} +0 -0
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FIELDS_TABLE = exports.BINDINGS_TABLE = exports.CONFIG_TABLE = exports.QUESTIONS_TABLE = void 0;
4
+ exports.collectionRowsForTier = collectionRowsForTier;
5
+ exports.configuredCollectionRows = configuredCollectionRows;
6
+ exports.questionRows = questionRows;
7
+ const schema_1 = require("../members-custom-fields/schema");
8
+ const checkout_1 = require("@tryghost/checkout");
9
+ const schema_2 = require("./schema");
10
+ exports.QUESTIONS_TABLE = 'products_checkout_fields';
11
+ exports.CONFIG_TABLE = 'products_checkout_config';
12
+ exports.BINDINGS_TABLE = 'members_custom_field_bindings';
13
+ exports.FIELDS_TABLE = 'members_custom_fields';
14
+ const ACTIVE = schema_1.FIELD_STATUS.active;
15
+ const optionColumns = Object.keys(schema_2.DbCheckoutOptions.shape).map((column) => `${exports.CONFIG_TABLE}.${column}`);
16
+ function collectionQuery(db) {
17
+ const bindTo = (alias, port) => function () {
18
+ this.on(`${alias}.product_id`, 'products.id').andOn(db.raw(`${alias}.port = ?`, [port]));
19
+ };
20
+ const landsIn = (alias, binding) => function () {
21
+ this.on(`${alias}.key`, `${binding}.custom_field_key`).andOn(db.raw(`${alias}.status = ?`, [ACTIVE]));
22
+ };
23
+ const query = db('products')
24
+ .leftJoin(exports.CONFIG_TABLE, `${exports.CONFIG_TABLE}.product_id`, 'products.id')
25
+ .leftJoin({ name_binding: exports.BINDINGS_TABLE }, bindTo('name_binding', checkout_1.STRIPE_PORT.shippingName))
26
+ .leftJoin({ name_field: exports.FIELDS_TABLE }, landsIn('name_field', 'name_binding'))
27
+ .leftJoin({ shipping_binding: exports.BINDINGS_TABLE }, bindTo('shipping_binding', checkout_1.STRIPE_PORT.shippingAddress))
28
+ .leftJoin({ shipping_field: exports.FIELDS_TABLE }, landsIn('shipping_field', 'shipping_binding'))
29
+ .leftJoin({ phone_binding: exports.BINDINGS_TABLE }, bindTo('phone_binding', checkout_1.STRIPE_PORT.phone))
30
+ .leftJoin({ phone_field: exports.FIELDS_TABLE }, landsIn('phone_field', 'phone_binding'))
31
+ .orderBy('products.id', 'asc')
32
+ .select([
33
+ 'products.id as product_id',
34
+ `${exports.CONFIG_TABLE}.id as config_id`,
35
+ ...optionColumns,
36
+ 'name_binding.custom_field_key as shipping_name_key',
37
+ 'name_field.key as shipping_name_collectable',
38
+ 'shipping_binding.custom_field_key as shipping_address_key',
39
+ 'shipping_field.key as shipping_address_collectable',
40
+ 'phone_binding.custom_field_key as phone_key',
41
+ 'phone_field.key as phone_collectable',
42
+ ]);
43
+ return query;
44
+ }
45
+ /** One row, whether or not that tier has ever been configured. */
46
+ function collectionRowsForTier(db, productId) {
47
+ return collectionQuery(db).where('products.id', productId);
48
+ }
49
+ /**
50
+ * This query starts from products, so without a filter it would return every tier on the
51
+ * site, configured or not. A tier only gets a row in products_checkout_config when someone
52
+ * saves checkout settings for it, so requiring that row narrows the list to the tiers
53
+ * somebody has actually set up. A tier that was set up and then had everything switched off
54
+ * keeps its row and stays in the list, reporting that it collects nothing.
55
+ */
56
+ function configuredCollectionRows(db) {
57
+ return collectionQuery(db).whereNotNull(`${exports.CONFIG_TABLE}.id`);
58
+ }
59
+ function questionRows(db, productId) {
60
+ const query = db(exports.QUESTIONS_TABLE)
61
+ .join(exports.BINDINGS_TABLE, `${exports.BINDINGS_TABLE}.id`, `${exports.QUESTIONS_TABLE}.binding_id`)
62
+ .leftJoin({ question_field: exports.FIELDS_TABLE }, function () {
63
+ this.on('question_field.key', `${exports.BINDINGS_TABLE}.custom_field_key`).andOn(db.raw('question_field.status = ?', [ACTIVE]));
64
+ })
65
+ .orderBy(`${exports.BINDINGS_TABLE}.product_id`, 'asc')
66
+ .orderBy(`${exports.QUESTIONS_TABLE}.sort_order`, 'asc')
67
+ .orderBy(`${exports.QUESTIONS_TABLE}.id`, 'asc')
68
+ .select([
69
+ `${exports.BINDINGS_TABLE}.product_id`,
70
+ `${exports.BINDINGS_TABLE}.port`,
71
+ `${exports.QUESTIONS_TABLE}.label`,
72
+ `${exports.QUESTIONS_TABLE}.optional`,
73
+ 'question_field.name as question_name',
74
+ 'question_field.type as question_type',
75
+ ]);
76
+ if (productId) {
77
+ return query.where(`${exports.BINDINGS_TABLE}.product_id`, productId);
78
+ }
79
+ return query;
80
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbCheckoutOptions = exports.DbCheckoutConfig = exports.DbCheckoutQuestion = void 0;
4
+ const zod_1 = require("zod");
5
+ const boolean_1 = require("../../lib/db-types/boolean");
6
+ const date_1 = require("../../lib/db-types/date");
7
+ exports.DbCheckoutQuestion = zod_1.z.object({
8
+ id: zod_1.z.string(),
9
+ binding_id: zod_1.z.string(),
10
+ sort_order: zod_1.z.number(),
11
+ label: zod_1.z.string().nullable(),
12
+ optional: boolean_1.DbBoolean,
13
+ created_at: date_1.DbDate,
14
+ updated_at: date_1.DbDate.nullable(),
15
+ });
16
+ /**
17
+ * Options only. Whether a tier collects something it keeps is the binding; a tax number is
18
+ * the exception, since Stripe keeps it and Ghost never does, so there is nothing to bind.
19
+ */
20
+ exports.DbCheckoutConfig = zod_1.z.object({
21
+ id: zod_1.z.string(),
22
+ product_id: zod_1.z.string(),
23
+ shipping_allowed_countries: zod_1.z.string().nullable(),
24
+ tax_number_collect: boolean_1.DbBoolean,
25
+ created_at: date_1.DbDate,
26
+ updated_at: date_1.DbDate.nullable(),
27
+ });
28
+ exports.DbCheckoutOptions = exports.DbCheckoutConfig.omit({
29
+ id: true,
30
+ product_id: true,
31
+ created_at: true,
32
+ updated_at: true,
33
+ });
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toCheckoutConfigResponse = exports.CheckoutConfigInput = void 0;
4
+ const zod_1 = require("zod");
5
+ const checkout_1 = require("@tryghost/checkout");
6
+ const models_1 = require("./models");
7
+ // Every country Stripe will take, sent at once, was measured as accepted — so the only
8
+ // ceiling is the list itself, and a request naming more than there are countries is naming
9
+ // something twice. A request that means all of them omits the list instead.
10
+ const MAX_ALLOWED_COUNTRIES = checkout_1.STRIPE_ALLOWED_COUNTRIES.length;
11
+ const QuestionInput = zod_1.z.object({
12
+ key: zod_1.z.string().min(1, { error: 'Every checkout question needs a custom field key.' }),
13
+ label: zod_1.z.string().trim().min(1).nullish(),
14
+ optional: zod_1.z.boolean().optional(),
15
+ });
16
+ // Not checked against a list of countries: membership of that list is contested, and Ghost
17
+ // is not its arbiter.
18
+ const CountryCode = zod_1.z
19
+ .string()
20
+ .trim()
21
+ .regex(/^[A-Za-z]{2}$/, { error: 'Enter a 2-letter country code, like US.' })
22
+ .toUpperCase()
23
+ .refine(checkout_1.isStripeAllowedCountry, {
24
+ error: 'Stripe will not ship to that country, so a checkout cannot offer it.',
25
+ });
26
+ /**
27
+ * Where a collected value lands is the request's to state. Ghost keeps no convention about
28
+ * it, so a block that collects names its destination and one that does not carries nothing
29
+ * to name.
30
+ */
31
+ const DESTINATION_REQUIRED = 'Say which custom field this is collected into.';
32
+ const CustomFieldKey = zod_1.z
33
+ .string({ error: DESTINATION_REQUIRED })
34
+ .min(1, { error: DESTINATION_REQUIRED });
35
+ const Destination = zod_1.z.strictObject({ custom_field_key: CustomFieldKey }, { error: DESTINATION_REQUIRED });
36
+ exports.CheckoutConfigInput = zod_1.z.strictObject({
37
+ custom_fields: zod_1.z
38
+ .array(QuestionInput)
39
+ .max(checkout_1.MAX_CHECKOUT_CUSTOM_FIELDS, {
40
+ error: `A checkout can ask at most ${checkout_1.MAX_CHECKOUT_CUSTOM_FIELDS} questions.`,
41
+ })
42
+ .refine((questions) => new Set(questions.map((question) => question.key)).size === questions.length, { error: 'This checkout already asks for that field.' })
43
+ .optional(),
44
+ shipping: zod_1.z
45
+ .discriminatedUnion('collect', [
46
+ zod_1.z.strictObject({ collect: zod_1.z.literal(false) }),
47
+ zod_1.z.strictObject({
48
+ collect: zod_1.z.literal(true),
49
+ // Absent means everywhere the processor ships. Empty is refused rather than
50
+ // read as everywhere: a publisher who cleared the list said something, and it
51
+ // was not "deliver worldwide".
52
+ allowed_countries: zod_1.z
53
+ .array(CountryCode, { error: 'Choose at least one country you deliver to.' })
54
+ .min(1, { error: 'Choose at least one country you deliver to.' })
55
+ .max(MAX_ALLOWED_COUNTRIES)
56
+ .optional(),
57
+ name: Destination,
58
+ address: Destination,
59
+ }),
60
+ ])
61
+ .optional(),
62
+ tax_number: zod_1.z.strictObject({ collect: zod_1.z.boolean() }).optional(),
63
+ phone: zod_1.z
64
+ .discriminatedUnion('collect', [
65
+ zod_1.z.strictObject({ collect: zod_1.z.literal(false) }),
66
+ zod_1.z.strictObject({ collect: zod_1.z.literal(true), custom_field_key: CustomFieldKey }),
67
+ ])
68
+ .optional(),
69
+ });
70
+ const QuestionResource = zod_1.z.object({
71
+ key: zod_1.z.string(),
72
+ label: zod_1.z.string().nullable(),
73
+ optional: zod_1.z.boolean(),
74
+ });
75
+ const CollectionResource = zod_1.z.object({
76
+ collect: zod_1.z.literal(true),
77
+ custom_field_key: zod_1.z.string(),
78
+ });
79
+ const ShippingResource = zod_1.z.object({
80
+ collect: zod_1.z.literal(true),
81
+ /** Absent means everywhere, the same way it does on the way in. */
82
+ allowed_countries: zod_1.z.array(zod_1.z.string()).optional(),
83
+ name: zod_1.z.object({ custom_field_key: zod_1.z.string() }),
84
+ address: zod_1.z.object({ custom_field_key: zod_1.z.string() }),
85
+ });
86
+ const CheckoutConfigResource = zod_1.z.object({
87
+ tier_id: zod_1.z.string(),
88
+ custom_fields: zod_1.z.array(QuestionResource),
89
+ shipping: ShippingResource.optional(),
90
+ tax_number: zod_1.z.object({ collect: zod_1.z.literal(true) }).optional(),
91
+ phone: CollectionResource.optional(),
92
+ });
93
+ const CheckoutConfigResponse = zod_1.z.object({
94
+ tiers_checkout_config: zod_1.z.array(CheckoutConfigResource),
95
+ });
96
+ /** One resource per tier, so a browse and a read differ only in how many come back. */
97
+ exports.toCheckoutConfigResponse = zod_1.z
98
+ .array(models_1.TierCheckoutConfig)
99
+ .transform((configs) => ({
100
+ tiers_checkout_config: configs.map((config) => ({
101
+ tier_id: config.tierId,
102
+ custom_fields: config.customFields,
103
+ // A block appears only when the tier collects that thing, so a client reads
104
+ // presence rather than a flag it would have to check.
105
+ ...(config.shipping
106
+ ? {
107
+ shipping: {
108
+ collect: true,
109
+ ...(config.shipping.allowedCountries
110
+ ? { allowed_countries: config.shipping.allowedCountries }
111
+ : {}),
112
+ name: { custom_field_key: config.shipping.nameCustomFieldKey },
113
+ address: { custom_field_key: config.shipping.addressCustomFieldKey },
114
+ },
115
+ }
116
+ : {}),
117
+ ...(config.taxNumber ? { tax_number: { collect: true } } : {}),
118
+ ...(config.phone
119
+ ? {
120
+ phone: {
121
+ collect: true,
122
+ custom_field_key: config.phone.customFieldKey,
123
+ },
124
+ }
125
+ : {}),
126
+ })),
127
+ }))
128
+ .pipe(CheckoutConfigResponse);
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TierCheckoutConfigService = void 0;
7
+ const bson_objectid_1 = __importDefault(require("bson-objectid"));
8
+ const errors_1 = __importDefault(require("@tryghost/errors"));
9
+ const zod_1 = require("zod");
10
+ const schema_1 = require("../members-custom-fields/schema");
11
+ const checkout_1 = require("@tryghost/checkout");
12
+ const codec_1 = require("./codec");
13
+ const queries_1 = require("./queries");
14
+ const models_1 = require("./models");
15
+ const serializers_1 = require("./serializers");
16
+ /**
17
+ * A request states its settings in named sections: one for shipping, one for phone. Each
18
+ * section stands for one or more of the values Stripe hands back when a checkout is
19
+ * completed — shipping covers both the recipient's name and their address, phone covers
20
+ * only the phone number.
21
+ *
22
+ * Naming those values here means that a request which includes a section settles every
23
+ * value in it: each one is either given a destination or has its old one removed. Without
24
+ * this list, a value the request never mentioned could keep a destination from an earlier
25
+ * save that the publisher believes they have turned off.
26
+ */
27
+ const BLOCK_PORTS = {
28
+ shipping: [checkout_1.STRIPE_PORT.shippingName, checkout_1.STRIPE_PORT.shippingAddress],
29
+ phone: [checkout_1.STRIPE_PORT.phone],
30
+ };
31
+ class TierCheckoutConfigService {
32
+ knex;
33
+ bindings;
34
+ fields;
35
+ constructor({ knex, bindings, fields, }) {
36
+ this.knex = knex;
37
+ this.bindings = bindings;
38
+ this.fields = fields;
39
+ }
40
+ async browse() {
41
+ const rows = decodeCollection(await (0, queries_1.configuredCollectionRows)(this.knex));
42
+ if (rows.length === 0) {
43
+ return [];
44
+ }
45
+ const asked = await this.questions();
46
+ return rows.map((row) => assemble(row, asked.get(row.tierId) ?? []));
47
+ }
48
+ async read(productId) {
49
+ const [row] = decodeCollection(await (0, queries_1.collectionRowsForTier)(this.knex, productId));
50
+ if (!row) {
51
+ throw new errors_1.default.NotFoundError({ message: 'Tier not found.' });
52
+ }
53
+ if (!row.configured) {
54
+ return null;
55
+ }
56
+ const asked = await this.questions(productId);
57
+ return assemble(row, asked.get(productId) ?? []);
58
+ }
59
+ async resolve(productId) {
60
+ const [row] = decodeCollection(await (0, queries_1.collectionRowsForTier)(this.knex, productId));
61
+ if (!row?.configured) {
62
+ return { customFields: [], ...(0, models_1.emptyCollection)() };
63
+ }
64
+ const asked = await this.questions(productId);
65
+ const customFields = (asked.get(productId) ?? []).flatMap((entry) => entry.askable ? [{ ...entry.question, ...entry.askable }] : []);
66
+ return { customFields, ...row.collecting };
67
+ }
68
+ /**
69
+ * Saves the checkout settings a request states, and leaves the rest alone.
70
+ *
71
+ * A request only has to include the sections it wants to change. Say nothing about
72
+ * shipping and the shipping settings stay exactly as they were, so a client that only
73
+ * knows how to edit the questions cannot wipe out the shipping settings by omitting
74
+ * them.
75
+ */
76
+ async edit(context, productId, input) {
77
+ const stated = parseInput(input);
78
+ const now = new Date();
79
+ if (stated.custom_fields) {
80
+ await assertQuestionsAskable(this.knex, stated.custom_fields);
81
+ }
82
+ const plan = await this.planCollection(stated);
83
+ const created = await this.knex.transaction(async (trx) => {
84
+ await assertTierExists(trx, productId);
85
+ await writeOptions(trx, productId, stated, now);
86
+ for (const port of plan.clear) {
87
+ await this.bindings.remove(trx, productId, port);
88
+ }
89
+ const made = [];
90
+ for (const wanted of plan.create) {
91
+ made.push(await this.fields.addOne(wanted, { executor: trx }));
92
+ }
93
+ for (const { port, key } of plan.bind) {
94
+ await this.bindings.bind(trx, productId, port, key, now);
95
+ }
96
+ if (stated.custom_fields) {
97
+ await this.writeQuestions(trx, productId, stated.custom_fields, now);
98
+ }
99
+ return made;
100
+ });
101
+ await this.fields.recordCreated(context, created);
102
+ }
103
+ async questions(productId) {
104
+ const rows = await (0, queries_1.questionRows)(this.knex, productId);
105
+ const byTier = new Map();
106
+ for (const row of rows) {
107
+ const parts = zod_1.z.decode(codec_1.questionRowCodec, row);
108
+ const forTier = byTier.get(parts.tierId) ?? [];
109
+ forTier.push(parts);
110
+ byTier.set(parts.tierId, forTier);
111
+ }
112
+ return byTier;
113
+ }
114
+ async planCollection(stated) {
115
+ const wanted = [];
116
+ if (stated.shipping?.collect) {
117
+ wanted.push({ port: checkout_1.STRIPE_PORT.shippingName, key: stated.shipping.name.custom_field_key }, { port: checkout_1.STRIPE_PORT.shippingAddress, key: stated.shipping.address.custom_field_key });
118
+ }
119
+ if (stated.phone?.collect) {
120
+ wanted.push({ port: checkout_1.STRIPE_PORT.phone, key: stated.phone.custom_field_key });
121
+ }
122
+ const bound = new Set(wanted.map(({ port }) => port));
123
+ const clear = [];
124
+ for (const block of ['shipping', 'phone']) {
125
+ if (stated[block]) {
126
+ clear.push(...BLOCK_PORTS[block].filter((port) => !bound.has(port)));
127
+ }
128
+ }
129
+ const create = new Map();
130
+ for (const { port, key } of wanted) {
131
+ const wants = checkout_1.PORT_FIELD[port];
132
+ const existing = await this.fields.findByKey(key);
133
+ if (existing) {
134
+ assertCollectableInto(port, existing, wants.type);
135
+ continue;
136
+ }
137
+ const alreadyPlanned = create.get(key);
138
+ if (alreadyPlanned && alreadyPlanned.type !== wants.type) {
139
+ throw new errors_1.default.ValidationError({
140
+ message: `This can only be collected into a ${wants.type} field.`,
141
+ property: `checkout.${port}.custom_field_key`,
142
+ });
143
+ }
144
+ if (!alreadyPlanned) {
145
+ create.set(key, { key, name: wants.name, type: wants.type });
146
+ }
147
+ }
148
+ return { clear, create: [...create.values()], bind: wanted };
149
+ }
150
+ /**
151
+ * Saves the questions this tier asks its buyers during checkout.
152
+ *
153
+ * A binding records that one value coming back from Stripe belongs in one custom field,
154
+ * and it identifies the value by the name Stripe uses for it. For a question that name
155
+ * is the custom field's own key: Ghost sends the key to Stripe as the question's
156
+ * identifier, and Stripe returns the buyer's answer labelled with that same key. That is
157
+ * why the key is passed twice below — once as the name Stripe will answer under, and
158
+ * once as the field the answer is stored in.
159
+ */
160
+ async writeQuestions(trx, productId, questions, now) {
161
+ const asked = new Set(questions.map((question) => question.key));
162
+ const alreadyAsked = await trx(queries_1.QUESTIONS_TABLE)
163
+ .join(queries_1.BINDINGS_TABLE, `${queries_1.BINDINGS_TABLE}.id`, `${queries_1.QUESTIONS_TABLE}.binding_id`)
164
+ .where(`${queries_1.BINDINGS_TABLE}.product_id`, productId)
165
+ .select(`${queries_1.BINDINGS_TABLE}.port`);
166
+ for (const { port } of alreadyAsked) {
167
+ if (!asked.has(port)) {
168
+ await this.bindings.remove(trx, productId, port);
169
+ }
170
+ }
171
+ for (const [index, question] of questions.entries()) {
172
+ const bindingId = await this.bindings.bind(trx, productId, question.key, question.key, now);
173
+ await trx(queries_1.QUESTIONS_TABLE).where('binding_id', bindingId).del();
174
+ await trx(queries_1.QUESTIONS_TABLE).insert({
175
+ id: new bson_objectid_1.default().toHexString(),
176
+ binding_id: bindingId,
177
+ sort_order: index,
178
+ label: question.label ?? null,
179
+ optional: question.optional ?? true,
180
+ created_at: now,
181
+ });
182
+ }
183
+ }
184
+ }
185
+ exports.TierCheckoutConfigService = TierCheckoutConfigService;
186
+ function parseInput(input) {
187
+ const parsed = serializers_1.CheckoutConfigInput.safeParse(input);
188
+ if (parsed.success) {
189
+ return parsed.data;
190
+ }
191
+ const issue = parsed.error.issues[0];
192
+ throw new errors_1.default.ValidationError({
193
+ message: issue.message,
194
+ property: issue.path.join('.') || 'checkout',
195
+ });
196
+ }
197
+ function decodeCollection(rows) {
198
+ return rows.map((row) => zod_1.z.decode(codec_1.collectionRowCodec, row));
199
+ }
200
+ function assemble(row, asked) {
201
+ return {
202
+ tierId: row.tierId,
203
+ customFields: asked.map((entry) => entry.question),
204
+ ...row.collection,
205
+ };
206
+ }
207
+ async function assertTierExists(db, productId) {
208
+ const tier = await db('products').where('id', productId).first();
209
+ if (!tier) {
210
+ throw new errors_1.default.NotFoundError({ message: 'Tier not found.' });
211
+ }
212
+ }
213
+ /**
214
+ * Refuses a custom field that cannot hold what Stripe will send back.
215
+ *
216
+ * A request names the custom field each collected value should be saved into. That field
217
+ * has to be active, because an archived field accepts no new values, and it has to store
218
+ * the right kind of data: Stripe returns a structured address for the shipping address,
219
+ * and plain text for a name or a phone number.
220
+ *
221
+ * Both refusals are deliberate rather than defensive. Ghost could save the value into some
222
+ * other field, or accept the request and quietly collect nothing, but a publisher who named
223
+ * a field meant that field. Being told now is better than finding out weeks later that
224
+ * nothing was ever recorded.
225
+ */
226
+ function assertCollectableInto(port, field, valueType) {
227
+ if (field.status !== schema_1.FIELD_STATUS.active) {
228
+ throw new errors_1.default.ValidationError({
229
+ message: 'An archived custom field cannot receive collected data. Restore it first.',
230
+ property: `checkout.${port}.custom_field_key`,
231
+ });
232
+ }
233
+ if (field.type !== valueType) {
234
+ throw new errors_1.default.ValidationError({
235
+ message: `This can only be collected into a ${valueType} field.`,
236
+ property: `checkout.${port}.custom_field_key`,
237
+ });
238
+ }
239
+ }
240
+ async function writeOptions(trx, productId, stated, now) {
241
+ const all = zod_1.z.encode(codec_1.optionsCodec, {
242
+ shippingAllowedCountries: stated.shipping?.collect
243
+ ? (stated.shipping.allowed_countries ?? null)
244
+ : null,
245
+ taxNumber: stated.tax_number?.collect ?? false,
246
+ });
247
+ // Only the columns this request spoke about are written, so two requests changing
248
+ // different parts of the same tier cannot undo each other, and a request that mentions
249
+ // neither still leaves the row behind as the record that this tier has been set up.
250
+ const columns = {
251
+ ...(stated.shipping ? { shipping_allowed_countries: all.shipping_allowed_countries } : {}),
252
+ ...(stated.tax_number ? { tax_number_collect: all.tax_number_collect } : {}),
253
+ };
254
+ await trx(queries_1.CONFIG_TABLE)
255
+ .insert({
256
+ id: new bson_objectid_1.default().toHexString(),
257
+ product_id: productId,
258
+ created_at: now,
259
+ updated_at: now,
260
+ ...columns,
261
+ })
262
+ .onConflict('product_id')
263
+ .merge({ ...columns, updated_at: now });
264
+ }
265
+ async function assertQuestionsAskable(db, questions) {
266
+ const collides = questions.find((question) => (0, checkout_1.isStripePort)(question.key));
267
+ if (collides) {
268
+ throw new errors_1.default.ValidationError({
269
+ message: `A field keyed ${collides.key} cannot be asked at checkout, because that is what this checkout calls something it collects for itself.`,
270
+ property: 'checkout.custom_fields',
271
+ });
272
+ }
273
+ const keys = questions.map((question) => question.key);
274
+ if (keys.length === 0) {
275
+ return;
276
+ }
277
+ const rows = await db(queries_1.FIELDS_TABLE)
278
+ .whereIn(`${queries_1.FIELDS_TABLE}.key`, keys)
279
+ .where(`${queries_1.FIELDS_TABLE}.status`, schema_1.FIELD_STATUS.active)
280
+ .select(`${queries_1.FIELDS_TABLE}.key`, `${queries_1.FIELDS_TABLE}.name`, `${queries_1.FIELDS_TABLE}.type`, `${queries_1.FIELDS_TABLE}.status`);
281
+ const byKey = new Map(rows.map((row) => [row.key, row]));
282
+ for (const question of questions) {
283
+ const field = byKey.get(question.key);
284
+ if (!field) {
285
+ throw new errors_1.default.ValidationError({
286
+ message: `Unknown custom field: ${question.key}`,
287
+ property: 'checkout.custom_fields',
288
+ });
289
+ }
290
+ if (!(0, checkout_1.isCheckoutEligible)(field.type)) {
291
+ throw new errors_1.default.ValidationError({
292
+ message: `A ${field.type} field cannot be asked for at checkout.`,
293
+ property: 'checkout.custom_fields',
294
+ });
295
+ }
296
+ const prompt = question.label ?? field.name;
297
+ if (prompt.length > checkout_1.MAX_CHECKOUT_LABEL_LENGTH) {
298
+ throw new errors_1.default.ValidationError({
299
+ message: `A checkout question can be at most ${checkout_1.MAX_CHECKOUT_LABEL_LENGTH} characters. Give this one a shorter label.`,
300
+ property: 'checkout.custom_fields',
301
+ });
302
+ }
303
+ }
304
+ }
@@ -30,6 +30,27 @@ class TiersServiceWrapper {
30
30
  repository,
31
31
  slugService,
32
32
  });
33
+
34
+ // What a tier's checkout asks, kept beside the tier rather than inside it: these
35
+ // rows are read live on every request, because deleting a custom field cascades a
36
+ // question away without the repository above ever seeing it, and a cached copy
37
+ // would go on naming a field the site no longer has.
38
+ //
39
+ // Boot builds the custom fields services before this one, so both collaborators
40
+ // are ready by the time this runs.
41
+ const { TierCheckoutConfigService } = require('../tier-checkout-config');
42
+ const { bindings, definitions } = require('../members-custom-fields');
43
+ this.checkout = new TierCheckoutConfigService({
44
+ knex: models.Base.knex,
45
+ // A binding is where a collected value lands, and the same rows are what a completed
46
+ // checkout is routed through later. Handed over rather than reached for, so the
47
+ // checkout domain states what it needs of them and nothing more.
48
+ bindings,
49
+ // Turning a collection on has to leave it somewhere to land, and making a field is
50
+ // the definitions' to do. A separate collaborator because it is a separate act, and
51
+ // because deciding whether one is needed belongs to the checkout rather than to them.
52
+ fields: definitions,
53
+ });
33
54
  }
34
55
  }
35
56
 
@@ -2,7 +2,7 @@ const api = require('../../api').endpoints;
2
2
  const config = require('../../../shared/config');
3
3
  const logging = require('@tryghost/logging');
4
4
  const urlUtils = require('../../../shared/url-utils').default;
5
- const jobsService = require('../jobs');
5
+ const UpdateCheckJob = require('./jobs/update-check-job').default;
6
6
 
7
7
  const request = require('@tryghost/request');
8
8
  const ghostVersion = require('@tryghost/version');
@@ -66,7 +66,7 @@ module.exports = async ({
66
66
  await updateChecker.check();
67
67
  };
68
68
 
69
- module.exports.scheduleRecurringJobs = () => {
69
+ const scheduleRecurringJob = async (jobsService) => {
70
70
  // use a random seconds/minutes/hours value to avoid spikes to the update service API
71
71
  const s = Math.floor(Math.random() * 60); // 0-59
72
72
  const m = Math.floor(Math.random() * 60); // 0-59
@@ -74,17 +74,17 @@ module.exports.scheduleRecurringJobs = () => {
74
74
 
75
75
  const at = `${s} ${m} ${h} * * *`;
76
76
  logging.info(`[Background Job] update-check scheduled at ${at}`);
77
- jobsService.addJob({
78
- at, // Every day
79
- job: require('path').resolve(__dirname, 'run-update-check.js'),
80
- name: 'update-check',
81
- });
77
+ await jobsService.scheduleRecurring(new UpdateCheckJob(), { cron: at });
82
78
  };
83
79
 
84
- module.exports.scheduleBootJob = () => {
85
- logging.info('[Background Job] update-check-boot queued');
86
- jobsService.addJob({
87
- job: require('path').resolve(__dirname, 'run-update-check.js'),
88
- name: 'update-check-boot',
89
- });
80
+ const scheduleBootJob = async (jobsService) => {
81
+ logging.info('[Background Job] update-check boot run queued');
82
+ await jobsService.dispatch(new UpdateCheckJob());
83
+ };
84
+
85
+ module.exports.scheduleJobs = async (jobsService) => {
86
+ await scheduleRecurringJob(jobsService);
87
+ if (config.get('updateCheck:forceUpdate')) {
88
+ await scheduleBootJob(jobsService);
89
+ }
90
90
  };
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const job_1 = require("../../jobs-service/job");
4
+ class UpdateCheckJob extends job_1.Job {
5
+ static type = 'update-check';
6
+ }
7
+ exports.default = UpdateCheckJob;
@@ -99,7 +99,7 @@ class UpdateCheckService {
99
99
  );
100
100
 
101
101
  err.context = tpl(messages.checkingForUpdatesFailedError);
102
- err.help = tpl(messages.checkingForUpdatesFailedHelp, { url: 'https://ghost.org/docs/' });
102
+ err.help = tpl(messages.checkingForUpdatesFailedHelp, { url: 'https://docs.ghost.org/' });
103
103
 
104
104
  this.logging.error(
105
105
  {
@@ -120,7 +120,7 @@ class UpdateCheckService {
120
120
  * Only the Ghost version is sent so the service can return the relevant
121
121
  * release and security notifications. No site data is collected.
122
122
  *
123
- * @see https://ghost.org/docs/concepts/config/#privacy
123
+ * @see https://docs.ghost.org/concepts/config/#privacy
124
124
  * @returns {Promise}
125
125
  */
126
126
  async updateCheckRequest() {
@@ -46,7 +46,7 @@ module.exports = function adminController(req, res) {
46
46
  throw new errors.IncorrectUsageError({
47
47
  message: tpl(messages.templateError.message, { templatePath }),
48
48
  context: tpl(messages.templateError.context),
49
- help: tpl(messages.templateError.help, { link: 'https://ghost.org/docs/install/source/' }),
49
+ help: tpl(messages.templateError.help, { link: 'https://docs.ghost.org/install/source/' }),
50
50
  err,
51
51
  });
52
52
  }