ghost 6.61.0 → 6.63.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 (619) 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-i18n-0.0.0.tgz +0 -0
  11. package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
  12. package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
  13. package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
  14. package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
  15. package/components/tryghost-kg-default-nodes-2.2.1.tgz +0 -0
  16. package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
  17. package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
  18. package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
  19. package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
  20. package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
  21. package/components/tryghost-limit-service-0.0.0.tgz +0 -0
  22. package/components/tryghost-metafield-types-0.0.0.tgz +0 -0
  23. package/components/tryghost-nql-string-0.0.0.tgz +0 -0
  24. package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
  25. package/content/settings/README.md +1 -1
  26. package/content/themes/casper/.gitignore +28 -0
  27. package/content/themes/casper/assets/built/casper.js +1 -1
  28. package/content/themes/casper/assets/built/screen.css +1 -1
  29. package/content/themes/casper/assets/css/screen.css +2 -1
  30. package/content/themes/casper/assets/js/lightbox.js +63 -46
  31. package/content/themes/casper/package.json +6 -6
  32. package/content/themes/casper/page.hbs +1 -0
  33. package/content/themes/casper/post.hbs +1 -0
  34. package/content/themes/source/.gitignore +28 -0
  35. package/content/themes/source/assets/built/screen.css +1 -1
  36. package/content/themes/source/assets/built/source.js +1 -1
  37. package/content/themes/source/assets/css/screen.css +2 -1
  38. package/content/themes/source/assets/js/lightbox.js +62 -45
  39. package/content/themes/source/assets/js/main.js +2 -2
  40. package/content/themes/source/package.json +4 -4
  41. package/content/themes/source/partials/feature-image.hbs +1 -0
  42. package/content/themes/source/partials/post-card.hbs +1 -1
  43. package/core/boot.js +61 -27
  44. package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
  45. package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
  46. package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
  47. package/core/built/admin/assets/{about-Bve7IJS2.js → about-C1C3s5-g.js} +1 -1
  48. package/core/built/admin/assets/accordion-CxGsDKwa.js +1 -0
  49. package/core/built/admin/assets/account-migration-CbUhW6gk.js +1 -0
  50. package/core/built/admin/assets/activitypub/{account-migration-C-aej4x_.js → account-migration-KA6Jjf9L.js} +16 -16
  51. package/core/built/admin/assets/activitypub/activitypub.js +9 -9
  52. package/core/built/admin/assets/activitypub/{avatar-DLPL3Abs.js → avatar-DVJF5bxP.js} +2 -2
  53. package/core/built/admin/assets/activitypub/{avatar-flipboard-C9iDY-QV.js → avatar-flipboard-csQaNeYK.js} +2 -2
  54. package/core/built/admin/assets/activitypub/{bluesky-sharing-BXkA5mqV.js → bluesky-sharing-C7nTDMvw.js} +37 -37
  55. package/core/built/admin/assets/activitypub/{content-formatters-CHcyMFk4.js → content-formatters-CLcjaBwV.js} +4 -4
  56. package/core/built/admin/assets/activitypub/{copy-DIAOQoCZ.js → copy-DcOh8kXw.js} +2 -2
  57. package/core/built/admin/assets/activitypub/{domain-B3ihkPde.js → domain-fZ-X8i4-.js} +121 -121
  58. package/core/built/admin/assets/activitypub/{edit-profile-DsZM3rTX.js → edit-profile-BqMLQV88.js} +40 -40
  59. package/core/built/admin/assets/activitypub/{explore-BodNrZgR.js → explore-B4yPoP8E.js} +27 -27
  60. package/core/built/admin/assets/activitypub/{feed-PwH1iJ84.js → feed-BRC_mvAe.js} +51 -51
  61. package/core/built/admin/assets/activitypub/{field-D06-kmOJ.js → field-COacNgOt.js} +3 -3
  62. package/core/built/admin/assets/activitypub/{hash-Bm3WxYN4.js → hash-C7u-irQQ.js} +2 -2
  63. package/core/built/admin/assets/activitypub/inbox-BW-yvUoN.js +1284 -0
  64. package/core/built/admin/assets/activitypub/{inbox-d8oJRTf4.js → inbox-CbTTdDQ3.js} +2 -2
  65. package/core/built/admin/assets/activitypub/label-kPMmYI7l.js +16 -0
  66. package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +1 -0
  67. package/core/built/admin/assets/activitypub/{moderation-D1Yapsod.js → moderation-CuidJzc0.js} +91 -91
  68. package/core/built/admin/assets/activitypub/{note-C5zjqq5C.js → note-CAMRRSqD.js} +24 -24
  69. package/core/built/admin/assets/activitypub/{notifications-B9p8eCHD.js → notifications-BmNlXGgp.js} +51 -51
  70. package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-Bqg7fh_L.js} +2 -2
  71. package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-Dmun9jr_.js} +2 -2
  72. package/core/built/admin/assets/activitypub/{preferences-BCqUdC3v.js → preferences--oH19wf9.js} +1519 -1519
  73. package/core/built/admin/assets/activitypub/{profile-BRCWoFem.js → profile-DH_tQx2D.js} +185 -185
  74. package/core/built/admin/assets/activitypub/{reply-DYK5vVqb.js → reply-Cap9PxSH.js} +2 -2
  75. package/core/built/admin/assets/activitypub/{routes-BSXT992i.js → routes-DdyY9uFF.js} +744 -735
  76. package/core/built/admin/assets/activitypub/{separator-BbQqTm-p.js → separator-CAxxu8VV.js} +3 -3
  77. package/core/built/admin/assets/activitypub/settings-B6EEcVkW.js +315 -0
  78. package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-BYps63oh.js} +2 -2
  79. package/core/built/admin/assets/activitypub/{step-1-B-YfsVeO.js → step-1-CKjR2KAm.js} +6 -6
  80. package/core/built/admin/assets/activitypub/{step-2-DtPQzlTH.js → step-2-B6PKfOUQ.js} +6 -6
  81. package/core/built/admin/assets/activitypub/{step-3-D6BFMXL_.js → step-3-C-qBbcUo.js} +11 -11
  82. package/core/built/admin/assets/activitypub/{tabs-GpNecvfB.js → tabs-DLhZtVde.js} +306 -306
  83. package/core/built/admin/assets/activitypub/{topic-filter-BPDeQQys.js → topic-filter-DsUOuu0k.js} +3 -3
  84. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-BoVOxXkV.js → use-activity-pub-queries-CX9snsLs.js} +3 -3
  85. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-Bmk5h4lW.js → use-navigate-with-base-path-DsJTGDSS.js} +988 -959
  86. package/core/built/admin/assets/activitypub/{use-reply-chain-data-wVBX7swM.js → use-reply-chain-data-AJi9zkzm.js} +30 -30
  87. package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-inweyKL5.js} +192 -164
  88. package/core/built/admin/assets/activitypub/{x-DbQZOLiI.js → x-DR8AT3-4.js} +2 -2
  89. package/core/built/admin/assets/add-integration-modal-BT_onDJy.js +1 -0
  90. package/core/built/admin/assets/add-newsletter-modal-Cx1kQpiY.js +1 -0
  91. package/core/built/admin/assets/add-recommendation-modal-B5nvAhvr.js +1 -0
  92. package/core/built/admin/assets/analytics-DjoJWPI7.js +1 -0
  93. package/core/built/admin/assets/{announcement-bar-modal-DM3cYOpo.js → announcement-bar-modal-Dwd0EGFP.js} +1 -1
  94. package/core/built/admin/assets/{api-keys-3-0vJC3Y.js → api-keys-BGPzLHzA.js} +1 -1
  95. package/core/built/admin/assets/{api-keys-BLm2NDBh.js → api-keys-BoeacM-s.js} +1 -1
  96. package/core/built/admin/assets/{audience-9mwU5TW6.js → audience-ceV3XVHq.js} +1 -1
  97. package/core/built/admin/assets/automated-emails-BLesE41L.js +1 -0
  98. package/core/built/admin/assets/{automations-sAcJ1334.js → automations-BN4u3GR8.js} +1 -1
  99. package/core/built/admin/assets/automations-C-ZTUwW3.js +1 -0
  100. package/core/built/admin/assets/bluesky-sharing-vzh-oPXL.js +1 -0
  101. package/core/built/admin/assets/{breadcrumb-DLiqCyFc.js → breadcrumb-CaLk2b7D.js} +1 -1
  102. package/core/built/admin/assets/{button-B2jcu6bk.js → button-Dvcv1QSR.js} +1 -1
  103. package/core/built/admin/assets/calendar-Bl9RLcZX.js +1 -0
  104. package/core/built/admin/assets/{chart-helpers-Fo3bUL3K.js → chart-helpers-FHywU0pj.js} +1 -1
  105. package/core/built/admin/assets/{checkbox-BfKktMph.js → checkbox-BtayFtjg.js} +1 -1
  106. package/core/built/admin/assets/chunk.254.035b85ec456ebbd7336b.js +440 -0
  107. package/core/built/admin/assets/{chunk.524.60ca4692d91c6022042f.js → chunk.524.e6975095fccdf22d90f8.js} +5 -5
  108. package/core/built/admin/assets/{chunk.582.525c7617c2d9e94c940a.js → chunk.582.98b485f9b2802f540897.js} +6 -6
  109. package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js → chunk.901.4ccc639821020230bcca.js} +9949 -7921
  110. package/core/built/admin/assets/{code-editor-view-DCQJxsfU.js → code-editor-view-C3ZfoF1X.js} +1 -1
  111. package/core/built/admin/assets/{code-editor-view-CYVu4EW6.js → code-editor-view-CAyRWy1s.js} +1 -1
  112. package/core/built/admin/assets/color-picker-G4kGR7rp.js +3 -0
  113. package/core/built/admin/assets/color-picker-field-DQFVTNnT.js +1 -0
  114. package/core/built/admin/assets/combobox-BnYzgEB4.js +1 -0
  115. package/core/built/admin/assets/command-C_vyTikI.js +1 -0
  116. package/core/built/admin/assets/comments-aBrxfnZf.js +1 -0
  117. package/core/built/admin/assets/{config-DOMQDZGZ.js → config-kSHvwdTG.js} +1 -1
  118. package/core/built/admin/assets/confirmation-context-DwXX_Y5h.js +1 -0
  119. package/core/built/admin/assets/{content-api-modal-CyQEC3fr.js → content-api-modal-D2G0nJP6.js} +1 -1
  120. package/core/built/admin/assets/context-menu-DOVB6yaz.js +1 -0
  121. package/core/built/admin/assets/{copy-field-B3M1s78L.js → copy-field-DCJwh9qf.js} +1 -1
  122. package/core/built/admin/assets/create-ghost-browse-value-source-5C53jJaX.js +1 -0
  123. package/core/built/admin/assets/custom-field-type-option-BAuW0F-E.js +1 -0
  124. package/core/built/admin/assets/custom-integration-modal-AbJGVyKc.js +1 -0
  125. package/core/built/admin/assets/{date-range-select-DD2RKkgB.js → date-range-select-BY3K-ukj.js} +1 -1
  126. package/core/built/admin/assets/design-and-theme-modal-BvEy4_pC.js +2 -0
  127. package/core/built/admin/assets/dirty-confirm-dialog-DVPuGz3U.js +1 -0
  128. package/core/built/admin/assets/{disabled-sources-indicator-Cvy47bE2.js → disabled-sources-indicator-Cy7Zp4c8.js} +1 -1
  129. package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
  130. package/core/built/admin/assets/domain-Dl61QGuk.js +1 -0
  131. package/core/built/admin/assets/dropzone-Dtz5Rmjt.js +5 -0
  132. package/core/built/admin/assets/edit-profile-DNJSMDwm.js +1 -0
  133. package/core/built/admin/assets/editor-DbAen9zf.js +8 -0
  134. package/core/built/admin/assets/editor-screen-BFWThIai.js +26 -0
  135. package/core/built/admin/assets/email-sending-status-context-CWDOD1FN.js +1 -0
  136. package/core/built/admin/assets/email-sending-status-provider-DCPkDICt.js +1 -0
  137. package/core/built/admin/assets/emails-vFMk4yYl.js +1 -0
  138. package/core/built/admin/assets/{embed-signup-form-modal-DeBMLxlL.js → embed-signup-form-modal-DGqsORxS.js} +1 -1
  139. package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
  140. package/core/built/admin/assets/explore-nnRr1Fja.js +1 -0
  141. package/core/built/admin/assets/feature-flag-overrides-context-DQjdJFAk.js +1 -0
  142. package/core/built/admin/assets/feature-image-placeholder-BrIHmPs8.js +1 -0
  143. package/core/built/admin/assets/feed-BNUcNu-W.js +1 -0
  144. package/core/built/admin/assets/feedback-DJ4Zb9rZ.js +1 -0
  145. package/core/built/admin/assets/{field-Cj8lmDOo.js → field-C93_Mg6j.js} +1 -1
  146. package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
  147. package/core/built/admin/assets/filter-sources-CGJb4C5r.js +1 -0
  148. package/core/built/admin/assets/filters--kKP-EC8.js +1 -0
  149. package/core/built/admin/assets/filters-DxA_a4FP.js +1 -0
  150. package/core/built/admin/assets/first-promoter-modal-BYDtrz2S.js +1 -0
  151. package/core/built/admin/assets/focus-koenig-editor-on-bottom-click-BbSnGOtK.js +1 -0
  152. package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8fd5bb799356bbefb254210ba7e1236d.css} +1 -1
  153. package/core/built/admin/assets/{ghost-3894eedcc6968f658578c0abfe942aab.js → ghost-b4e8a403baba61905cd7560e30b9732d.js} +63 -44
  154. package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-cac0cb2d5bb78e9bd14d8d1c2768ed45.css} +1 -1
  155. package/core/built/admin/assets/gift-link-modal-B7u5bD4e.js +1 -0
  156. package/core/built/admin/assets/{gift-links-DLDS7ljO.js → gift-links-CfLSnst2.js} +1 -1
  157. package/core/built/admin/assets/{growth-D6mhcx3q.js → growth-Cd1uO9d2.js} +1 -1
  158. package/core/built/admin/assets/growth-aG32xfdE.js +3 -0
  159. package/core/built/admin/assets/history-modal-Df8lW8h-.js +1 -0
  160. package/core/built/admin/assets/hooks-Cy_-oZ4o.js +2 -0
  161. package/core/built/admin/assets/{html-field-C8qVGGC1.js → html-field-DA4eU7eq.js} +1 -1
  162. package/core/built/admin/assets/{images-h8VZEQ9k.js → images-KXmyO3Ss.js} +1 -1
  163. package/core/built/admin/assets/{inbox-BYd1M06Q.js → inbox-gJHG7_xd.js} +14 -14
  164. package/core/built/admin/assets/index-BHHXtR5C.css +2 -0
  165. package/core/built/admin/assets/index-CgMCwsxN.js +4 -0
  166. package/core/built/admin/assets/index.es-CY6-yuw9.js +1 -0
  167. package/core/built/admin/assets/{input-group-D8sCXRF5.js → input-group-BE8rM2y1.js} +1 -1
  168. package/core/built/admin/assets/{integrations-Bjbu86Z0.js → integrations-B4kuRBFY.js} +1 -1
  169. package/core/built/admin/assets/invite-user-modal-B4CuNcx4.js +1 -0
  170. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
  171. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
  172. package/core/built/admin/assets/koenig-lexical/style.css +1 -1
  173. package/core/built/admin/assets/{koenig-lexical-_-IsVI7E.js → koenig-lexical-CGXb0F8o.js} +3 -3
  174. package/core/built/admin/assets/koenig-loader-BeM5rFPX.js +2 -0
  175. package/core/built/admin/assets/{kpi-card-DZQk_0W8.js → kpi-card-B21aToe8.js} +1 -1
  176. package/core/built/admin/assets/{kpi-card-DW_2y3ug.js → kpi-card-pz6Wv4Ae.js} +1 -1
  177. package/core/built/admin/assets/{kpi-helpers-PueTKuA6.js → kpi-helpers-Dq5jelmP.js} +1 -1
  178. package/core/built/admin/assets/{kpi-tabs-CojDs-Qz.js → kpi-tabs-DEZHpKUt.js} +1 -1
  179. package/core/built/admin/assets/{label-DjbT91GM.js → label-OLCzze8N.js} +1 -1
  180. package/core/built/admin/assets/label-picker-BigOvMPV.js +1 -0
  181. package/core/built/admin/assets/{labels-CH3Kn8RV.js → labels-Dr0SgUQD.js} +1 -1
  182. package/core/built/admin/assets/layout-DUTL8SH5.js +1 -0
  183. package/core/built/admin/assets/lib-7QPTI0FS.js +1 -0
  184. package/core/built/admin/assets/link-helpers-BRAg9SCj.js +1 -0
  185. package/core/built/admin/assets/links-BDHPTlyj.js +1 -0
  186. package/core/built/admin/assets/member-custom-fields-TRTHCAz9.js +1 -0
  187. package/core/built/admin/assets/member-detail-b6dRwc3C.js +1 -0
  188. package/core/built/admin/assets/members-Czt5lJSc.js +4 -0
  189. package/core/built/admin/assets/moderation-ZWefD9uH.js +1 -0
  190. package/core/built/admin/assets/{multi-select-combobox-B6LiRzDO.js → multi-select-combobox-CT13MTo-.js} +1 -1
  191. package/core/built/admin/assets/navigation-modal-C8FEhBK5.js +1 -0
  192. package/core/built/admin/assets/newsletter-5VYW4RyJ.js +1 -0
  193. package/core/built/admin/assets/newsletter-detail-modal-ChjC0ZqH.js +1 -0
  194. package/core/built/admin/assets/{newsletter-emails-CFeLlt4z.js → newsletter-emails-DZH6I-PY.js} +1 -1
  195. package/core/built/admin/assets/newsletters-DOC9KBcX.js +1 -0
  196. package/core/built/admin/assets/newsletters-Rq1M3GD-.js +1 -0
  197. package/core/built/admin/assets/note-DIV-Pojr.js +1 -0
  198. package/core/built/admin/assets/notifications-s20-H49V.js +1 -0
  199. package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
  200. package/core/built/admin/assets/{offers-9J1CSiLy.js → offers--e2nGvkW.js} +1 -1
  201. package/core/built/admin/assets/offers-container-modal-BmG5M5Tv.js +1 -0
  202. package/core/built/admin/assets/onboarding-CUuy9unR.js +1 -0
  203. package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-pXyct0Jm.js} +1 -1
  204. package/core/built/admin/assets/{onboarding-route-Dmc4IPm3.js → onboarding-route-CK4pcXoR.js} +1 -1
  205. package/core/built/admin/assets/overview-CaPsciMs.js +1 -0
  206. package/core/built/admin/assets/overview-a8GfcsKX.js +1 -0
  207. package/core/built/admin/assets/{page-header-BR_nbIPp.js → page-header-DM_KyoFI.js} +1 -1
  208. package/core/built/admin/assets/pagemenu-vgZVUVKR.js +1 -0
  209. package/core/built/admin/assets/pages-CjDY2CBI.js +1 -0
  210. package/core/built/admin/assets/pages-route-BzKZYdV5.js +1 -0
  211. package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
  212. package/core/built/admin/assets/{pintura-modal-BR9hEvS8.js → pintura-modal-DExCF1ii.js} +1 -1
  213. package/core/built/admin/assets/portal-frame-Cqanidq4.js +1 -0
  214. package/core/built/admin/assets/portal-modal-oinBgA27.js +1 -0
  215. package/core/built/admin/assets/post-analytics-C5oJ1dWV.js +1 -0
  216. package/core/built/admin/assets/post-analytics-header-OATo3X_r.js +1 -0
  217. package/core/built/admin/assets/post-analytics-provider-tOpIVGT5.js +1 -0
  218. package/core/built/admin/assets/post-helpers-C88JBa4D.js +1 -0
  219. package/core/built/admin/assets/post-share-modal-DvhC1XgV.js +1 -0
  220. package/core/built/admin/assets/post-utils-DKF4mfuo.js +1 -0
  221. package/core/built/admin/assets/posts-list-screen-o4ZjbgS3.js +2 -0
  222. package/core/built/admin/assets/posts-pCPrhQeR.js +1 -0
  223. package/core/built/admin/assets/posts-route-9F--xdIb.js +1 -0
  224. package/core/built/admin/assets/preferences--vNubsqD.js +5 -0
  225. package/core/built/admin/assets/{preview-modal-C-BL-F9k.js → preview-modal-YYbzlYdO.js} +1 -1
  226. package/core/built/admin/assets/profile-BllKV3dR.js +1 -0
  227. package/core/built/admin/assets/{radio-group-Bgz60i81.js → radio-group-DL8GI8rt.js} +1 -1
  228. package/core/built/admin/assets/{referrers-CnhCMWCs.js → referrers-D8oHPmDB.js} +1 -1
  229. package/core/built/admin/assets/router-provider-BSvTXgZY.js +1 -0
  230. package/core/built/admin/assets/schemas-BoYdB6YU.js +64 -0
  231. package/core/built/admin/assets/select-v5YbA3Mw.js +1 -0
  232. package/core/built/admin/assets/settings-PEsZupw1.js +4 -0
  233. package/core/built/admin/assets/{settings-breadcrumbs-CyP_9zTo.js → settings-breadcrumbs-NwPnCTC7.js} +1 -1
  234. package/core/built/admin/assets/settings-u_s1CRkr.js +1 -0
  235. package/core/built/admin/assets/share-modal-SskCmVGL.js +1 -0
  236. package/core/built/admin/assets/slack-modal-XxSJiYgo.js +1 -0
  237. package/core/built/admin/assets/{sort-button-Cc-Z96sA.js → sort-button-Cx1KtINE.js} +1 -1
  238. package/core/built/admin/assets/{source-icon-v6PUnmyM.js → source-icon-Bdp9Im_5.js} +1 -1
  239. package/core/built/admin/assets/stats-D9PDLS3x.js +1 -0
  240. package/core/built/admin/assets/stats-view-W2g6Jsd-.js +1 -0
  241. package/core/built/admin/assets/{step-1-DSRp2vBj.js → step-1-BQYzvBzr.js} +1 -1
  242. package/core/built/admin/assets/{step-2-COqv2g0u.js → step-2-xES32xJV.js} +1 -1
  243. package/core/built/admin/assets/{step-3-6W1NMVQc.js → step-3-DA_XK5kW.js} +2 -2
  244. package/core/built/admin/assets/{stripe-button-D2K42Uty.js → stripe-button-CVsLHelt.js} +1 -1
  245. package/core/built/admin/assets/stripe-connect-modal-ClmLOMGS.js +1 -0
  246. package/core/built/admin/assets/style-Wd9nQubI.css +1 -0
  247. package/core/built/admin/assets/{table-B2pIKjVh.js → table-BenqVupI.js} +1 -1
  248. package/core/built/admin/assets/{tabs-SaW_UT-Q.js → tabs-eBBlxPp8.js} +1 -1
  249. package/core/built/admin/assets/tag-detail-Ce8Se2qd.js +2 -0
  250. package/core/built/admin/assets/tag-picker-C3UR-uJ-.js +1 -0
  251. package/core/built/admin/assets/tags-B-kJxCs7.js +1 -0
  252. package/core/built/admin/assets/{tags-zTFiKRpb.js → tags-ChfdwJyS.js} +1 -1
  253. package/core/built/admin/assets/testimonials-modal-9Q6fT06V.js +1 -0
  254. package/core/built/admin/assets/tier-detail-modal-CDyYdqK7.js +1 -0
  255. package/core/built/admin/assets/tiers-BCpIOGQr.js +1 -0
  256. package/core/built/admin/assets/{toggle-group-EM9WpESu.js → toggle-group-CviIkyok.js} +1 -1
  257. package/core/built/admin/assets/{topic-filter-DY4gNHn2.js → topic-filter-I2jCBGxm.js} +1 -1
  258. package/core/built/admin/assets/{transistor-modal-CpyPG-MH.js → transistor-modal-p53u3W8y.js} +1 -1
  259. package/core/built/admin/assets/{trend-badge-ByMkuAuH.js → trend-badge-CideVzQd.js} +1 -1
  260. package/core/built/admin/assets/{unsplash-modal-Cv2Q6GtY.js → unsplash-modal-DY8UcKcE.js} +1 -1
  261. package/core/built/admin/assets/{use-analytics-data-CHvjMYuT.js → use-analytics-data-DAIGcr2f.js} +1 -1
  262. package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
  263. package/core/built/admin/assets/{use-check-theme-limit-error-Qv4SVXqw.js → use-check-theme-limit-error-DRUjK4YM.js} +1 -1
  264. package/core/built/admin/assets/use-filter-params-JkZhIS8L.js +1 -0
  265. package/core/built/admin/assets/{use-filterable-api-CzXcpUh8.js → use-filterable-api-BVEVMrhZ.js} +1 -1
  266. package/core/built/admin/assets/use-growth-stats-937CUj6j.js +1 -0
  267. package/core/built/admin/assets/use-host-limits-D0Dv3tKA.js +1 -0
  268. package/core/built/admin/assets/{use-limiter-cwJvsNBo.js → use-limiter-CohWfzvK.js} +1 -1
  269. package/core/built/admin/assets/{use-navigate-with-base-path-BHPA228f.js → use-navigate-with-base-path-BkxcPj5l.js} +1 -1
  270. package/core/built/admin/assets/use-pintura-config-xgytjU35.js +1 -0
  271. package/core/built/admin/assets/{use-pintura-editor-BDBXWUYq.js → use-pintura-editor-CaV1mbfO.js} +1 -1
  272. package/core/built/admin/assets/{use-post-referrers-DbY-mzci.js → use-post-referrers-BCQI7fDg.js} +1 -1
  273. package/core/built/admin/assets/use-reply-chain-data-CYRhoaxY.js +1 -0
  274. package/core/built/admin/assets/{use-router-history-entry-Bg6BhBQQ.js → use-router-history-entry-NOKcd--l.js} +1 -1
  275. package/core/built/admin/assets/{use-setting-group-VM7FQwMY.js → use-setting-group-bLKMwoBT.js} +1 -1
  276. package/core/built/admin/assets/use-settings-navigation-B-qrriKD.js +1 -0
  277. package/core/built/admin/assets/{use-simple-pagination-Dum6Irbs.js → use-simple-pagination-CNPd6Rf6.js} +1 -1
  278. package/core/built/admin/assets/use-staff-users-pRIMIyiW.js +1 -0
  279. package/core/built/admin/assets/use-tinybird-query-CmdbCS9X.js +1 -0
  280. package/core/built/admin/assets/use-tinybird-token-KLne235s.js +1 -0
  281. package/core/built/admin/assets/{use-unsaved-changes-guard-BAy0RANR.js → use-unsaved-changes-guard-7-ug0JdH.js} +1 -1
  282. package/core/built/admin/assets/use-upgrade-route-Dm_qqKr0.js +1 -0
  283. package/core/built/admin/assets/{use-url-input-Df5QzNa7.js → use-url-input-fvp-TpPx.js} +1 -1
  284. package/core/built/admin/assets/user-detail-modal-D3KlGYv-.js +1 -0
  285. package/core/built/admin/assets/users-DVNWrqg9.js +1 -0
  286. package/core/built/admin/assets/{vendor-5753232c7d6ffb3c44e1eb0a02133953.js → vendor-2dd263291938d407802568d3357ec69e.js} +19 -17
  287. package/core/built/admin/assets/{virtual-list-DEx8WgiO.js → virtual-list-CZ3RK2Yf.js} +1 -1
  288. package/core/built/admin/assets/web-ClEJsCAj.js +2 -0
  289. package/core/built/admin/assets/web-X9rdnrtq.js +1 -0
  290. package/core/built/admin/assets/{zapier-modal-Cw3BTBUu.js → zapier-modal-DfVhV17j.js} +1 -1
  291. package/core/built/admin/index.html +23 -27
  292. package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
  293. package/core/frontend/helpers/cancel_link.js +2 -2
  294. package/core/frontend/helpers/pagination.js +2 -2
  295. package/core/frontend/helpers/social_accounts.js +2 -2
  296. package/core/frontend/public/ghost.css +1 -1
  297. package/core/frontend/public/ghost.min.css +1 -1
  298. package/core/frontend/services/sitemap/user-map-generator.js +9 -2
  299. package/core/server/adapters/jobs/InMemoryJobsBackend.js +74 -29
  300. package/core/server/api/endpoints/authentication.js +1 -2
  301. package/core/server/api/endpoints/authors-public.js +15 -5
  302. package/core/server/api/endpoints/emails.js +13 -0
  303. package/core/server/api/endpoints/index.js +10 -2
  304. package/core/server/api/endpoints/mail.js +1 -1
  305. package/core/server/api/endpoints/member-metafields-members.js +23 -0
  306. package/core/server/api/endpoints/member-metafields.js +102 -0
  307. package/core/server/api/endpoints/members-account.js +42 -0
  308. package/core/server/api/endpoints/members.js +4 -4
  309. package/core/server/api/endpoints/newsletters-public.js +10 -1
  310. package/core/server/api/endpoints/pages-public.js +15 -5
  311. package/core/server/api/endpoints/posts-public.js +15 -5
  312. package/core/server/api/endpoints/posts.js +3 -3
  313. package/core/server/api/endpoints/tags-public.js +22 -3
  314. package/core/server/api/endpoints/tiers-checkout-config.js +2 -2
  315. package/core/server/api/endpoints/tiers-public.js +38 -1
  316. package/core/server/api/endpoints/users.js +2 -2
  317. package/core/server/api/endpoints/utils/api-filter-utils.js +170 -3
  318. package/core/server/api/endpoints/utils/public-cache-keys.js +3 -0
  319. package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -0
  320. package/core/server/api/endpoints/utils/serializers/input/members.js +20 -9
  321. package/core/server/api/endpoints/utils/serializers/input/pages.js +2 -0
  322. package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -0
  323. package/core/server/api/endpoints/utils/serializers/input/tags.js +2 -0
  324. package/core/server/api/endpoints/utils/serializers/output/emails.js +7 -0
  325. package/core/server/api/endpoints/utils/serializers/output/index.js +6 -2
  326. package/core/server/api/endpoints/utils/serializers/output/member-metafields.js +18 -0
  327. package/core/server/api/endpoints/utils/serializers/output/members-account.js +19 -0
  328. package/core/server/api/endpoints/utils/serializers/output/members-csv-transform.js +1 -1
  329. package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
  330. package/core/server/api/endpoints/utils/serializers/output/tiers.js +10 -2
  331. package/core/server/api/endpoints/utils/serializers/output/users.js +0 -10
  332. package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
  333. package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
  334. package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
  335. package/core/server/data/exporter/table-lists.js +3 -3
  336. package/core/server/data/importer/importers/data/data-importer.js +3 -3
  337. package/core/server/data/migrations/utils/migrations.js +1 -1
  338. package/core/server/data/migrations/versions/6.53/2026-07-15-20-22-53-add-members-custom-field-values.js +1 -1
  339. package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +10 -1
  340. package/core/server/data/migrations/versions/6.63/2026-09-03-10-07-03-rename-custom-field-tables-to-metafields.js +169 -0
  341. package/core/server/data/migrations/versions/6.63/2026-09-03-11-03-31-remove-member-custom-field-read-permissions.js +30 -0
  342. package/core/server/data/schema/fixtures/fixtures.json +0 -11
  343. package/core/server/data/schema/lib/default-index-name.js +8 -11
  344. package/core/server/data/schema/schema.js +18 -18
  345. package/core/server/data/seeders/README.md +48 -0
  346. package/core/server/data/tinybird/pipes/mv_hits.pipe +86 -50
  347. package/core/server/lib/db-types/count.js +16 -0
  348. package/core/server/lib/image/image-size.js +24 -0
  349. package/core/server/lib/package-json/parse.js +2 -2
  350. package/core/server/lib/validate-password.js +81 -120
  351. package/core/server/models/action.js +4 -4
  352. package/core/server/models/base/plugins/filtered-collection.js +16 -0
  353. package/core/server/models/member.js +10 -21
  354. package/core/server/models/post.js +2 -2
  355. package/core/server/models/relations/authors.js +2 -16
  356. package/core/server/models/user.js +10 -3
  357. package/core/server/services/auth/passwordreset.js +13 -0
  358. package/core/server/services/automations/automations-api.js +1 -1
  359. package/core/server/services/automations/database-automations-repository.js +11 -4
  360. package/core/server/services/comments/comments-controller.js +30 -9
  361. package/core/server/services/comments/comments-service.js +55 -5
  362. package/core/server/services/content-import/csv/formula.js +12 -0
  363. package/core/server/services/content-import/csv/index.js +6 -1
  364. package/core/server/services/content-import/csv/parse.js +27 -6
  365. package/core/server/services/content-import/csv/serialize.js +13 -0
  366. package/core/server/services/content-import/import/completion-email.js +164 -0
  367. package/core/server/services/content-import/import/errors-file.js +36 -0
  368. package/core/server/services/content-import/import/importer.js +160 -53
  369. package/core/server/services/content-import/import/local-media-url.js +63 -0
  370. package/core/server/services/content-import/import/media.js +254 -0
  371. package/core/server/services/content-import/import/post-data.js +0 -1
  372. package/core/server/services/content-import/import/post-link.js +11 -0
  373. package/core/server/services/content-import/import/post-repository.js +25 -3
  374. package/core/server/services/content-import/import/reader.js +9 -2
  375. package/core/server/services/content-import/import/report.js +34 -0
  376. package/core/server/services/content-import/import/row.js +0 -2
  377. package/core/server/services/content-import/import/schema.js +1 -0
  378. package/core/server/services/content-import/import/staged-file.js +29 -0
  379. package/core/server/services/content-import/import/store.js +25 -2
  380. package/core/server/services/content-import/index.js +52 -6
  381. package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
  382. package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
  383. package/core/server/services/custom-redirects/redirects-service.js +1 -1
  384. package/core/server/services/custom-redirects/validation.js +1 -1
  385. package/core/server/services/email-address/email-address-parser.js +30 -38
  386. package/core/server/services/email-address/email-address-service.js +37 -4
  387. package/core/server/services/email-analytics/index.js +2 -7
  388. package/core/server/services/email-service/CONTEXT.md +37 -0
  389. package/core/server/services/email-service/README.md +42 -0
  390. package/core/server/services/email-service/batch-sending-service.js +2 -4
  391. package/core/server/services/email-service/email-controller.js +18 -2
  392. package/core/server/services/email-service/email-renderer.js +14 -4
  393. package/core/server/services/email-service/email-service-wrapper.js +3 -9
  394. package/core/server/services/email-service/mailgun-email-provider.js +20 -9
  395. package/core/server/services/email-service/sending-status-schema.js +21 -0
  396. package/core/server/services/email-service/sending-status-serializers.js +33 -0
  397. package/core/server/services/email-service/sending-status-service.js +47 -0
  398. package/core/server/services/email-service/sending-status.js +111 -0
  399. package/core/server/services/email-suppression-list/email-suppression-list.js +0 -33
  400. package/core/server/services/email-suppression-list/mailgun-email-suppression-list.js +6 -7
  401. package/core/server/services/gifts/README.md +2 -2
  402. package/core/server/services/gifts/constants.js +1 -1
  403. package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
  404. package/core/server/services/gifts/gift-email-service.js +0 -1
  405. package/core/server/services/gifts/gift-service.js +19 -57
  406. package/core/server/services/gifts/index.js +3 -5
  407. package/core/server/services/gifts/jobs/index.js +4 -4
  408. package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
  409. package/core/server/services/invitations/accept.js +4 -2
  410. package/core/server/services/jobs/job-service.js +0 -9
  411. package/core/server/services/jobs-service/index.js +8 -0
  412. package/core/server/services/jobs-service/jobs-service.js +52 -3
  413. package/core/server/services/jobs-service/register-job-handlers.js +32 -11
  414. package/core/server/services/mail/ghost-mailer.js +4 -4
  415. package/core/server/services/member-welcome-emails/service.js +7 -7
  416. package/core/server/services/members/account-service.js +98 -0
  417. package/core/server/services/members/api.js +4 -3
  418. package/core/server/services/members/import-export/csv/parse.js +0 -7
  419. package/core/server/services/members/import-export/export/exporter.js +20 -20
  420. package/core/server/services/members/import-export/import/completion-email.js +8 -11
  421. package/core/server/services/members/import-export/import/importer.js +13 -13
  422. package/core/server/services/members/import-export/import/row.js +0 -3
  423. package/core/server/services/members/import-export/index.js +10 -17
  424. package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
  425. package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
  426. package/core/server/services/members/jobs/index.js +48 -24
  427. package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
  428. package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
  429. package/core/server/services/members/members-api/members-api.js +26 -8
  430. package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
  431. package/core/server/services/members/members-api/repositories/member-repository.js +4 -4
  432. package/core/server/services/members/members-api/services/member-bread-service.js +62 -84
  433. package/core/server/services/members/members-api/services/payments-service.js +1 -1
  434. package/core/server/services/members/members-ssr.js +18 -0
  435. package/core/server/services/members/middleware.js +95 -81
  436. package/core/server/services/members/service.js +71 -47
  437. package/core/server/services/members/utils.js +9 -0
  438. package/core/server/services/members-metafields/access.js +28 -0
  439. package/core/server/services/{members-custom-fields → members-metafields}/actions.js +5 -5
  440. package/core/server/services/members-metafields/bindings-service.js +147 -0
  441. package/core/server/services/members-metafields/codec.js +14 -0
  442. package/core/server/services/{members-custom-fields → members-metafields}/config.js +1 -1
  443. package/core/server/services/{members-custom-fields → members-metafields}/definitions-service.js +68 -22
  444. package/core/server/services/members-metafields/filter.js +181 -0
  445. package/core/server/services/{members-custom-fields → members-metafields}/index.js +16 -7
  446. package/core/server/services/{members-custom-fields → members-metafields}/key.js +2 -1
  447. package/core/server/services/{members-custom-fields → members-metafields}/models.js +5 -5
  448. package/core/server/services/members-metafields/namespaces.js +34 -0
  449. package/core/server/services/{members-custom-fields → members-metafields}/queries.js +1 -1
  450. package/core/server/services/{members-custom-fields → members-metafields}/schema.js +16 -12
  451. package/core/server/services/members-metafields/serializers.js +22 -0
  452. package/core/server/services/{members-custom-fields → members-metafields}/values-service.js +80 -48
  453. package/core/server/services/mentions/mention-controller.js +23 -21
  454. package/core/server/services/mentions/mention-sending-service.js +26 -9
  455. package/core/server/services/mentions/process-webmention-job.js +16 -0
  456. package/core/server/services/mentions/send-webmentions-job.js +18 -0
  457. package/core/server/services/mentions/service.js +7 -33
  458. package/core/server/services/route-settings/validation-errors.js +1 -1
  459. package/core/server/services/route-settings/yaml-parser.js +1 -1
  460. package/core/server/services/settings/settings-service.js +1 -8
  461. package/core/server/services/settings-helpers/settings-helpers.js +4 -6
  462. package/core/server/services/slack-ping/slack-ping-service.js +1 -1
  463. package/core/server/services/staff/staff-service-emails.js +2 -2
  464. package/core/server/services/stripe/config.js +14 -8
  465. package/core/server/services/stripe/service.js +10 -1
  466. package/core/server/services/stripe/services/checkout/completed-session.js +2 -2
  467. package/core/server/services/stripe/services/checkout/session-options.js +18 -15
  468. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +28 -7
  469. package/core/server/services/stripe/stripe-api.js +2 -0
  470. package/core/server/services/stripe/stripe-service.js +24 -14
  471. package/core/server/services/stripe/webhook-controller.js +10 -0
  472. package/core/server/services/tier-checkout-config/codec.js +11 -7
  473. package/core/server/services/tier-checkout-config/index.js +2 -1
  474. package/core/server/services/tier-checkout-config/models.js +11 -5
  475. package/core/server/services/tier-checkout-config/queries.js +12 -12
  476. package/core/server/services/tier-checkout-config/serializers.js +73 -10
  477. package/core/server/services/tier-checkout-config/service.js +14 -13
  478. package/core/server/services/tiers/service.js +3 -3
  479. package/core/server/services/update-check/index.js +13 -13
  480. package/core/server/services/update-check/jobs/update-check-job.js +7 -0
  481. package/core/server/services/update-check/update-check-service.js +2 -2
  482. package/core/server/services/url/router-filter.js +8 -10
  483. package/core/server/services/users.js +1 -19
  484. package/core/server/web/admin/controller.js +1 -1
  485. package/core/server/web/api/endpoints/admin/routes.js +20 -16
  486. package/core/server/web/members/account/index.js +2 -0
  487. package/core/server/web/members/account/routes.js +46 -0
  488. package/core/server/web/members/app.js +4 -18
  489. package/core/shared/config/defaults.json +1 -1
  490. package/core/shared/config/env/config.testing-mysql.json +1 -1
  491. package/core/shared/labs.js +7 -2
  492. package/package.json +42 -41
  493. package/pnpm-lock.yaml +1159 -582
  494. package/pnpm-workspace.yaml +18 -15
  495. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  496. package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
  497. package/core/built/admin/assets/Combination-5fYEzgbs.js +0 -41
  498. package/core/built/admin/assets/account-migration-DRhAehfw.js +0 -1
  499. package/core/built/admin/assets/activitypub/inbox-BluD2ucT.js +0 -1284
  500. package/core/built/admin/assets/activitypub/label-FHSh6ZAw.js +0 -16
  501. package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +0 -1
  502. package/core/built/admin/assets/activitypub/settings-DQDkr06X.js +0 -315
  503. package/core/built/admin/assets/add-integration-modal-BC0jExDZ.js +0 -1
  504. package/core/built/admin/assets/add-newsletter-modal-DchTkA7l.js +0 -1
  505. package/core/built/admin/assets/add-recommendation-modal-DMvaJZKp.js +0 -1
  506. package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +0 -1
  507. package/core/built/admin/assets/analytics-No1DDY3A.js +0 -1
  508. package/core/built/admin/assets/automated-emails-te1D5kq-.js +0 -1
  509. package/core/built/admin/assets/automations-DpUNUdcZ.js +0 -1
  510. package/core/built/admin/assets/bluesky-sharing-Cjrv_f62.js +0 -1
  511. package/core/built/admin/assets/chunk.896.435fe0637944d4a69810.js +0 -366
  512. package/core/built/admin/assets/color-picker-BTY_NdEm.js +0 -3
  513. package/core/built/admin/assets/color-picker-field-CPlkzOH0.js +0 -1
  514. package/core/built/admin/assets/combobox-DrMtv_4P.js +0 -1
  515. package/core/built/admin/assets/command-ZdgYJpoI.js +0 -1
  516. package/core/built/admin/assets/comments-kidj60-h.js +0 -1
  517. package/core/built/admin/assets/confirmation-context-BhbmQQEl.js +0 -1
  518. package/core/built/admin/assets/custom-field-type-option-CsGIZk1J.js +0 -10
  519. package/core/built/admin/assets/custom-integration-modal-BY37O_9X.js +0 -1
  520. package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +0 -22
  521. package/core/built/admin/assets/dialog-B44iRwlc.js +0 -1
  522. package/core/built/admin/assets/dirty-confirm-dialog-BxgQgQ6H.js +0 -1
  523. package/core/built/admin/assets/dist-Cy8tfrp_.js +0 -1
  524. package/core/built/admin/assets/domain-Ca6KhoQt.js +0 -1
  525. package/core/built/admin/assets/dropzone-Bb-5zQTk.js +0 -5
  526. package/core/built/admin/assets/edit-profile-BdupLlus.js +0 -1
  527. package/core/built/admin/assets/editor-BP883Lpn.js +0 -8
  528. package/core/built/admin/assets/explore-FB-CimXw.js +0 -1
  529. package/core/built/admin/assets/feed-C1GxsmI2.js +0 -1
  530. package/core/built/admin/assets/filter-sources-D5n5bFA2.js +0 -1
  531. package/core/built/admin/assets/filters-DJEzkMUK.js +0 -1
  532. package/core/built/admin/assets/filters-MZXvdH5D.js +0 -1
  533. package/core/built/admin/assets/first-promoter-modal-CGTGLCr5.js +0 -1
  534. package/core/built/admin/assets/gift-link-modal-B6v0tnRN.js +0 -1
  535. package/core/built/admin/assets/growth-BGAJD1rs.js +0 -3
  536. package/core/built/admin/assets/history-modal-iKwUY3XR.js +0 -1
  537. package/core/built/admin/assets/hooks-CuEZV2-q.js +0 -2
  538. package/core/built/admin/assets/index-DjQe-e5s.js +0 -4
  539. package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
  540. package/core/built/admin/assets/index.es-VihPwfeH.js +0 -1
  541. package/core/built/admin/assets/input--W-rCTrX.js +0 -1
  542. package/core/built/admin/assets/invite-user-modal-MdImLCaJ.js +0 -1
  543. package/core/built/admin/assets/koenig-loader-DYZpm6El.js +0 -2
  544. package/core/built/admin/assets/label-picker-cB51xl1r.js +0 -1
  545. package/core/built/admin/assets/layout-c_B_1hax.js +0 -1
  546. package/core/built/admin/assets/link-helpers-MVDdnFCY.js +0 -1
  547. package/core/built/admin/assets/loading-indicator-BpFWTVgh.js +0 -1
  548. package/core/built/admin/assets/member-custom-fields-qs0dIkxM.js +0 -1
  549. package/core/built/admin/assets/member-detail-B6uec2a6.js +0 -1
  550. package/core/built/admin/assets/members-BQF3CqhI.js +0 -2
  551. package/core/built/admin/assets/moderation-DY4wF1Q3.js +0 -1
  552. package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +0 -1
  553. package/core/built/admin/assets/newsletter-CyQA7m3m.js +0 -1
  554. package/core/built/admin/assets/newsletter-detail-modal-BDrptuxn.js +0 -1
  555. package/core/built/admin/assets/newsletters-DXuNmC42.js +0 -1
  556. package/core/built/admin/assets/newsletters-DhNigJUh.js +0 -1
  557. package/core/built/admin/assets/note-Cgo2LCJ6.js +0 -1
  558. package/core/built/admin/assets/notifications-BC50AMku.js +0 -1
  559. package/core/built/admin/assets/offers-container-modal-DWycIMzT.js +0 -1
  560. package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
  561. package/core/built/admin/assets/overview-CVbFtn-k.js +0 -1
  562. package/core/built/admin/assets/overview-YKj7SZaK.js +0 -1
  563. package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
  564. package/core/built/admin/assets/popover-CCy0h5rq.js +0 -1
  565. package/core/built/admin/assets/portal-frame-dc4XKr0e.js +0 -1
  566. package/core/built/admin/assets/portal-modal-BvvdF_B9.js +0 -1
  567. package/core/built/admin/assets/post-analytics-DX470WRG.js +0 -1
  568. package/core/built/admin/assets/post-analytics-header-CXpJmdDv.js +0 -1
  569. package/core/built/admin/assets/post-analytics-provider-CLfKGWZy.js +0 -1
  570. package/core/built/admin/assets/post-helpers-C5OQMBoi.js +0 -1
  571. package/core/built/admin/assets/post-share-modal-Cl0NXRJr.js +0 -1
  572. package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
  573. package/core/built/admin/assets/preferences-DO0UUWFp.js +0 -5
  574. package/core/built/admin/assets/profile-CxT-0l4d.js +0 -1
  575. package/core/built/admin/assets/router-provider-B0gZyiGJ.js +0 -1
  576. package/core/built/admin/assets/schemas-4UPic-oD.js +0 -64
  577. package/core/built/admin/assets/select-DuDUFdPq.js +0 -1
  578. package/core/built/admin/assets/settings-BjCii7BM.js +0 -1
  579. package/core/built/admin/assets/settings-b8kNzZSQ.js +0 -4
  580. package/core/built/admin/assets/share-modal-BV70mr9k.js +0 -1
  581. package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
  582. package/core/built/admin/assets/slack-modal-Be2_KK-z.js +0 -1
  583. package/core/built/admin/assets/stats-C4reGh9O.js +0 -1
  584. package/core/built/admin/assets/stats-view-DP9pQ62k.js +0 -1
  585. package/core/built/admin/assets/stripe-connect-modal-Z-UT_BGc.js +0 -1
  586. package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +0 -2
  587. package/core/built/admin/assets/tags-BLaXBwnb.js +0 -1
  588. package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +0 -1
  589. package/core/built/admin/assets/tier-detail-modal-BCFpWwUS.js +0 -1
  590. package/core/built/admin/assets/tiers-Nd_z4BZY.js +0 -1
  591. package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +0 -1
  592. package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +0 -1
  593. package/core/built/admin/assets/use-host-limits-DZtR08oa.js +0 -1
  594. package/core/built/admin/assets/use-pintura-config-CzTfnufz.js +0 -1
  595. package/core/built/admin/assets/use-reply-chain-data-BeQwz8oF.js +0 -1
  596. package/core/built/admin/assets/use-settings-navigation-BbrSUVcf.js +0 -1
  597. package/core/built/admin/assets/use-staff-users-cZg4ugOF.js +0 -1
  598. package/core/built/admin/assets/use-tinybird-query-B1IMyAcv.js +0 -39
  599. package/core/built/admin/assets/use-tinybird-token-Bow0Zwn8.js +0 -1
  600. package/core/built/admin/assets/use-upgrade-route-DhRSo7at.js +0 -1
  601. package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +0 -1
  602. package/core/built/admin/assets/users-5msE1WMs.js +0 -1
  603. package/core/built/admin/assets/web-CKcXAu86.js +0 -1
  604. package/core/built/admin/assets/web-DR4TqM3E.js +0 -2
  605. package/core/server/api/endpoints/member-custom-fields.js +0 -90
  606. package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +0 -20
  607. package/core/server/services/email-suppression-list/in-memory-email-suppression-list.js +0 -39
  608. package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
  609. package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
  610. package/core/server/services/members-custom-fields/bindings-service.js +0 -119
  611. package/core/server/services/members-custom-fields/codec.js +0 -15
  612. package/core/server/services/members-custom-fields/filter.js +0 -199
  613. package/core/server/services/members-custom-fields/serializers.js +0 -24
  614. package/core/server/services/stripe/services/checkout/allowed-countries.js +0 -267
  615. package/core/server/services/stripe/services/checkout/field-ports.js +0 -34
  616. package/core/server/services/tier-checkout-config/destinations.js +0 -17
  617. package/core/server/services/update-check/run-update-check.js +0 -63
  618. /package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js.LICENSE.txt → chunk.901.4ccc639821020230bcca.js.LICENSE.txt} +0 -0
  619. /package/core/server/services/{members-custom-fields → members-metafields}/storage.js +0 -0
@@ -6,7 +6,7 @@ const limitService = require('../services/limits');
6
6
  const tpl = require('@tryghost/tpl');
7
7
  const errors = require('@tryghost/errors');
8
8
  const security = require('@tryghost/security');
9
- const validatePassword = require('../lib/validate-password');
9
+ const { validatePassword } = require('../lib/validate-password');
10
10
  const permissions = require('../services/permissions');
11
11
  const urlUtils = require('../../shared/url-utils').default;
12
12
  const { setIsRoles } = require('./role-utils');
@@ -43,8 +43,13 @@ const messages = {
43
43
  };
44
44
 
45
45
  /**
46
- * inactive: owner user before blog setup, suspended users
47
- * locked user: imported users, they get a random password
46
+ * `inactive`: suspended staff, who cannot sign in or reset their password.
47
+ * Also used for the initial owner before site setup is complete.
48
+ * `locked`: users whose password was randomized by an import or authentication
49
+ * reset. They must reset their password to regain access.
50
+ *
51
+ * Both statuses prevent normal sign-in, so they are grouped here. This is not
52
+ * a suspension check: isInactive() checks only the literal `inactive` status.
48
53
  */
49
54
  const inactiveStates = ['inactive', 'locked'];
50
55
 
@@ -212,6 +217,8 @@ User = ghostBookshelf.Model.extend(
212
217
  return this.save(update, { ...options, patch: true });
213
218
  },
214
219
 
220
+ // Checks the literal status, not membership in inactiveStates: locked users
221
+ // must remain eligible for password resets.
215
222
  isInactive: function isInactive() {
216
223
  return this.get('status') === 'inactive';
217
224
  },
@@ -11,6 +11,7 @@ const mail = require('../mail');
11
11
 
12
12
  const messages = {
13
13
  userNotFound: 'User not found.',
14
+ userSuspended: 'This account has been suspended.',
14
15
  resetPassword: 'Reset Password',
15
16
  expired: {
16
17
  message: 'Cannot reset password.',
@@ -46,6 +47,14 @@ function generateToken(email, settingsAPI, transaction) {
46
47
  throw new errors.NotFoundError({ message: tpl(messages.userNotFound) });
47
48
  }
48
49
 
50
+ // Prevent suspended users from generating a reset token. This same check must appear
51
+ // below in doReset() to account for password reset tokens that were generated prior
52
+ // to suspension since they have a 24-hour lifetime.
53
+ // Locked users must still be able to recover access by resetting their password.
54
+ if (user.isInactive()) {
55
+ throw new errors.NoPermissionError({ message: tpl(messages.userSuspended) });
56
+ }
57
+
49
58
  token = security.tokens.resetToken.generateHash({
50
59
  expires: moment().add(1, 'days').valueOf(),
51
60
  email: email,
@@ -128,6 +137,10 @@ function doReset(options, tokenParts, settingsAPI) {
128
137
  }
129
138
  }
130
139
 
140
+ if (user.isInactive()) {
141
+ throw new errors.NoPermissionError({ message: tpl(messages.userSuspended) });
142
+ }
143
+
131
144
  const updatedUser = await models.User.changePassword(
132
145
  {
133
146
  oldPassword: oldPassword,
@@ -78,7 +78,7 @@ const repository = (0, database_automations_repository_1.createDatabaseAutomatio
78
78
  fakeWaitHoursMultiplier: (0, fake_wait_hours_multiplier_1.parseFakeWaitHoursMultiplier)(config.get('automations:fakeWaitHoursMultiplier')),
79
79
  });
80
80
  async function browse() {
81
- return await repository.browse();
81
+ return await repository.browse({ includeStats: true });
82
82
  }
83
83
  async function read(automationId) {
84
84
  const automation = await repository.getById(automationId);
@@ -35,14 +35,16 @@ const messages = {
35
35
  const DEFAULT_EMAIL_DESIGN_SETTING_REFERENCE = constants_1.DEFAULT_EMAIL_DESIGN_SETTING_SLUG;
36
36
  function createDatabaseAutomationsRepository({ knex, fakeWaitHoursMultiplier, }) {
37
37
  return {
38
- async browse() {
38
+ async browse({ includeStats }) {
39
39
  return await knex.transaction(async (trx) => {
40
40
  await ensureDefaultAutomations(trx);
41
- const rows = await loadAutomations(trx);
41
+ const data = includeStats
42
+ ? (await loadAutomationsWithStats(trx)).map((row) => buildAutomationBrowseResult(row))
43
+ : (await loadAutomations(trx)).map((row) => buildAutomationSummary(row));
42
44
  return {
43
- data: rows.map((row) => buildAutomationBrowseResult(row)),
45
+ data,
44
46
  meta: {
45
- pagination: buildPagination(rows.length),
47
+ pagination: buildPagination(data.length),
46
48
  },
47
49
  };
48
50
  });
@@ -671,6 +673,11 @@ async function loadAutomationBySlug(trx, slug) {
671
673
  return row ?? null;
672
674
  }
673
675
  async function loadAutomations(trx) {
676
+ return await trx('automations')
677
+ .select('id', 'slug', 'name', 'status', 'created_at', 'updated_at')
678
+ .orderBy('name');
679
+ }
680
+ async function loadAutomationsWithStats(trx) {
674
681
  const inProgressRuns = trx('automation_run_steps')
675
682
  .distinct('automation_run_id')
676
683
  .where('status', 'pending')
@@ -14,6 +14,7 @@ const messages = {
14
14
  cannotDestroyComments: 'You cannot destroy comments.',
15
15
  memberNotFound: 'Unable to find member',
16
16
  unsupportedReportCountFilter: 'Unsupported count.reports filter',
17
+ errorParsingFilter: 'Error parsing filter',
17
18
  };
18
19
 
19
20
  module.exports = class CommentsController {
@@ -26,6 +27,16 @@ module.exports = class CommentsController {
26
27
  this.stats = stats;
27
28
  }
28
29
 
30
+ #getPostIdFromFilter(query) {
31
+ if (typeof query.post_id === 'string') {
32
+ return query.post_id;
33
+ }
34
+
35
+ // Only equality conditions required by an AND can scope a legacy browse.
36
+ // Choosing an ID from an OR or an exclusion would authorize the wrong posts.
37
+ return query.$and?.map((part) => this.#getPostIdFromFilter(part)).find(Boolean);
38
+ }
39
+
29
40
  #withPostFilter(options) {
30
41
  const nextOptions = { ...options };
31
42
 
@@ -138,7 +149,17 @@ module.exports = class CommentsController {
138
149
  * @param {Frame} frame
139
150
  */
140
151
  async browse(frame) {
141
- return await this.service.getComments(this.#withPostFilter(frame.options));
152
+ const options = { ...frame.options };
153
+ if (!options.post_id && options.filter) {
154
+ let query;
155
+ try {
156
+ query = nql(options.filter).parse();
157
+ } catch (err) {
158
+ throw new errors.BadRequestError({ message: tpl(messages.errorParsingFilter), err });
159
+ }
160
+ options.post_id = this.#getPostIdFromFilter(query);
161
+ }
162
+ return await this.service.getComments(this.#withPostFilter(options));
142
163
  }
143
164
 
144
165
  async adminBrowse(frame) {
@@ -345,14 +366,14 @@ module.exports = class CommentsController {
345
366
  async like(frame) {
346
367
  this.#checkMember(frame);
347
368
 
369
+ const comment = await this.service.getCommentByID(frame.options.id, frame.options);
370
+
348
371
  const result = await this.service.likeComment(
349
372
  frame.options.id,
350
373
  frame.options?.context?.member,
351
374
  frame.options,
352
375
  );
353
376
 
354
- const comment = await this.service.getCommentByID(frame.options.id, frame.options);
355
-
356
377
  this.setCacheInvalidationHeaders(comment, frame);
357
378
 
358
379
  return result;
@@ -364,14 +385,14 @@ module.exports = class CommentsController {
364
385
  async unlike(frame) {
365
386
  this.#checkMember(frame);
366
387
 
388
+ const comment = await this.service.getCommentByID(frame.options.id, frame.options);
389
+
367
390
  const result = await this.service.unlikeComment(
368
391
  frame.options.id,
369
392
  frame.options?.context?.member,
370
393
  frame.options,
371
394
  );
372
395
 
373
- const comment = await this.service.getCommentByID(frame.options.id, frame.options);
374
-
375
396
  this.setCacheInvalidationHeaders(comment, frame);
376
397
 
377
398
  return result;
@@ -420,14 +441,14 @@ module.exports = class CommentsController {
420
441
  async dislike(frame) {
421
442
  this.#checkMember(frame);
422
443
 
444
+ const comment = await this.service.getCommentByID(frame.options.id, frame.options);
445
+
423
446
  const result = await this.service.dislikeComment(
424
447
  frame.options.id,
425
448
  frame.options?.context?.member,
426
449
  frame.options,
427
450
  );
428
451
 
429
- const comment = await this.service.getCommentByID(frame.options.id, frame.options);
430
-
431
452
  this.setCacheInvalidationHeaders(comment, frame);
432
453
 
433
454
  return result;
@@ -439,14 +460,14 @@ module.exports = class CommentsController {
439
460
  async undislike(frame) {
440
461
  this.#checkMember(frame);
441
462
 
463
+ const comment = await this.service.getCommentByID(frame.options.id, frame.options);
464
+
442
465
  const result = await this.service.undislikeComment(
443
466
  frame.options.id,
444
467
  frame.options?.context?.member,
445
468
  frame.options,
446
469
  );
447
470
 
448
- const comment = await this.service.getCommentByID(frame.options.id, frame.options);
449
-
450
471
  this.setCacheInvalidationHeaders(comment, frame);
451
472
 
452
473
  return result;
@@ -13,7 +13,9 @@ const messages = {
13
13
  alreadyDisliked: 'This comment was disliked already',
14
14
  replyToReply: 'Can not reply to a reply',
15
15
  commentsNotEnabled: 'Comments are not enabled for this site.',
16
+ postIdRequired: 'A post ID is required to read comments.',
16
17
  cannotCommentOnPost: 'You do not have permission to comment on this post.',
18
+ cannotReadComment: 'You do not have permission to read comments on this post',
17
19
  cannotEditComment: 'You do not have permission to edit comments',
18
20
  cannotPinReply: 'Replies cannot be pinned',
19
21
  cannotPinDeletedComment: 'Deleted comments cannot be pinned',
@@ -39,7 +41,7 @@ const COMMENT_STATUSES_IN_REPLY_TO = [COMMENT_STATUS_PUBLISHED, COMMENT_STATUS_H
39
41
  // new `comment.get(...)` call has one obvious place to register its column.
40
42
  const REPLY_PARENT_REQUIRED_COLUMNS = ['parent_id', 'post_id'];
41
43
  const IN_REPLY_TO_REQUIRED_COLUMNS = ['parent_id'];
42
- const OWNERSHIP_REQUIRED_COLUMNS = ['member_id'];
44
+ const OWNERSHIP_REQUIRED_COLUMNS = ['member_id', 'post_id'];
43
45
  const REPORT_REQUIRED_COLUMNS = ['post_id', 'member_id', 'html', 'created_at'];
44
46
 
45
47
  function getColumnList(columns) {
@@ -167,11 +169,14 @@ class CommentsService {
167
169
  }
168
170
 
169
171
  /** @private */
170
- checkPostAccess(postModel, memberModel) {
171
- const access = this.contentGating.checkPostAccess(postModel.toJSON(), memberModel.toJSON());
172
+ checkPostAccess(postModel, memberModel, { action = 'comment' } = {}) {
173
+ const access = this.contentGating.checkPostAccess(
174
+ postModel.toJSON(),
175
+ memberModel ? memberModel.toJSON() : null,
176
+ );
172
177
  if (access === this.contentGating.BLOCK_ACCESS) {
173
178
  throw new errors.NoPermissionError({
174
- message: tpl(messages.cannotCommentOnPost),
179
+ message: tpl(action === 'read' ? messages.cannotReadComment : messages.cannotCommentOnPost),
175
180
  });
176
181
  }
177
182
  }
@@ -189,6 +194,32 @@ class CommentsService {
189
194
  });
190
195
  }
191
196
 
197
+ async #checkPostAccessForRead(postId, options) {
198
+ if (options.isAdmin) {
199
+ return;
200
+ }
201
+
202
+ if (!postId) {
203
+ throw new errors.ValidationError({ message: tpl(messages.postIdRequired) });
204
+ }
205
+
206
+ const postModel = await this.models.Post.findOne(
207
+ { id: postId },
208
+ { require: true, transacting: options.transacting, withRelated: ['tiers'] },
209
+ );
210
+
211
+ let memberModel = null;
212
+ const memberId = options.context?.member?.id;
213
+ if (memberId) {
214
+ memberModel = await this.models.Member.findOne(
215
+ { id: memberId },
216
+ { require: true, transacting: options.transacting, withRelated: ['products'] },
217
+ );
218
+ }
219
+
220
+ this.checkPostAccess(postModel, memberModel, { action: 'read' });
221
+ }
222
+
192
223
  /**
193
224
  * Primary comment lookup: a single keyed fetch with the allowed statuses
194
225
  * applied in the query (`WHERE id = ? AND status IN (...)`), optionally locked
@@ -520,6 +551,7 @@ class CommentsService {
520
551
  */
521
552
  async getComments(options) {
522
553
  this.checkEnabled();
554
+ await this.#checkPostAccessForRead(options.post_id, options);
523
555
  const page = await this.models.Comment.findPage(
524
556
  withPinnedSelect({ ...options, parentId: null, pinnedFirst: true }),
525
557
  );
@@ -659,6 +691,17 @@ class CommentsService {
659
691
  */
660
692
  async getReplies(id, options, { includeHidden = false } = {}) {
661
693
  this.checkEnabled();
694
+
695
+ const parentComment = await this.#fetchCommentByID(id, options, {
696
+ requiredColumns: ['post_id'],
697
+ });
698
+
699
+ if (!parentComment) {
700
+ throw new errors.NotFoundError({ message: tpl(messages.commentNotFound) });
701
+ }
702
+
703
+ await this.#checkPostAccessForRead(parentComment.get('post_id'), options);
704
+
662
705
  const page = await this.models.Comment.findPage(
663
706
  withPinnedSelect({ ...options, parentId: id }, { includeHidden }),
664
707
  );
@@ -740,7 +783,9 @@ class CommentsService {
740
783
  async getCommentByID(id, options = {}) {
741
784
  this.checkEnabled();
742
785
 
743
- return await this.#getReadableCommentByID(id, options);
786
+ const model = await this.#getReadableCommentByID(id, options, ['post_id']);
787
+ await this.#checkPostAccessForRead(model.get('post_id'), options);
788
+ return model;
744
789
  }
745
790
 
746
791
  /**
@@ -908,6 +953,9 @@ class CommentsService {
908
953
  });
909
954
  }
910
955
 
956
+ // Mutation responses include replies, so they require the same post access as reads.
957
+ await this.#checkPostAccessForRead(existingComment.get('post_id'), options);
958
+
911
959
  const model = await this.models.Comment.edit(
912
960
  {
913
961
  status: COMMENT_STATUS_DELETED,
@@ -943,6 +991,8 @@ class CommentsService {
943
991
  });
944
992
  }
945
993
 
994
+ await this.#checkPostAccessForRead(existingComment.get('post_id'), options);
995
+
946
996
  if (!comment) {
947
997
  return existingComment;
948
998
  }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stripFormulaGuard = stripFormulaGuard;
4
+ const FORMULA_TRIGGERS = ['=', '+', '-', '@', '\t', '\r'];
5
+ // Report files prefix formula-shaped cells with an apostrophe. Remove exactly
6
+ // that guard during re-import so repeated fixes do not change publisher data.
7
+ function stripFormulaGuard(cell) {
8
+ if (cell.charAt(0) === "'" && FORMULA_TRIGGERS.includes(cell.charAt(1))) {
9
+ return cell.slice(1);
10
+ }
11
+ return cell;
12
+ }
@@ -3,6 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parse = void 0;
6
+ exports.stripFormulaGuard = exports.serialize = exports.parseWithSource = exports.parse = void 0;
7
7
  var parse_1 = require("./parse");
8
8
  Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
9
+ Object.defineProperty(exports, "parseWithSource", { enumerable: true, get: function () { return parse_1.parseWithSource; } });
10
+ var serialize_1 = require("./serialize");
11
+ Object.defineProperty(exports, "serialize", { enumerable: true, get: function () { return __importDefault(serialize_1).default; } });
12
+ var formula_1 = require("./formula");
13
+ Object.defineProperty(exports, "stripFormulaGuard", { enumerable: true, get: function () { return formula_1.stripFormulaGuard; } });
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = parse;
7
+ exports.parseWithSource = parseWithSource;
7
8
  const papaparse_1 = __importDefault(require("papaparse"));
8
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const formula_1 = require("./formula");
9
11
  const errors = require('@tryghost/errors');
10
12
  // A column named after an Object.prototype member is unsafe as a key.
11
13
  function isSafeColumnName(name) {
@@ -17,44 +19,63 @@ function isSafeColumnName(name) {
17
19
  function isFatal(error) {
18
20
  return error.type === 'Quotes';
19
21
  }
22
+ function isEmptyRow(row) {
23
+ return !Object.values(row).some((value) => {
24
+ if (Array.isArray(value)) {
25
+ return value.some((cell) => typeof cell === 'string' && cell.trim().length > 0);
26
+ }
27
+ return value.trim().length > 0;
28
+ });
29
+ }
20
30
  // headerMapping renames headers to the columns they emit under; unmapped columns
21
31
  // carry through, and one mapped to an empty target is dropped.
22
32
  async function parse(path, headerMapping) {
33
+ const parsed = await parseWithSource(path, headerMapping);
34
+ return parsed.rows.map(({ data }) => data);
35
+ }
36
+ async function parseWithSource(path, headerMapping) {
23
37
  // Buffered rather than streamed: files are bounded by the interim row cap, and
24
38
  // papaparse's stream mode drops results.errors, which is what catches a
25
39
  // malformed quoted field before it imports as garbage.
26
40
  const content = (await fs_extra_1.default.readFile(path, 'utf8')).replace(/^\ufeff/, '');
27
41
  const parsed = papaparse_1.default.parse(content, {
28
42
  header: true,
29
- skipEmptyLines: true,
43
+ // Empty records stay in the parser output long enough to contribute to the
44
+ // publisher-visible spreadsheet line number, then are dropped below.
45
+ skipEmptyLines: false,
30
46
  });
31
47
  const fatal = parsed.errors.find(isFatal);
32
48
  if (fatal) {
33
49
  throw new errors.ValidationError({ message: `${fatal.code}: ${fatal.message}` });
34
50
  }
35
51
  const rows = [];
36
- for (const parsedRow of parsed.data) {
52
+ for (const [index, parsedRow] of parsed.data.entries()) {
53
+ if (isEmptyRow(parsedRow)) {
54
+ continue;
55
+ }
37
56
  const row = {};
57
+ const source = Object.create(null);
38
58
  for (const [header, value] of Object.entries(parsedRow)) {
39
59
  // non-string values are papaparse's __parsed_extra overflow from ragged rows
40
60
  if (typeof value !== 'string') {
41
61
  continue;
42
62
  }
63
+ source[header] = value;
43
64
  // hasOwn: a prototype-named header must not match an inherited method on the mapping
44
65
  if (headerMapping && Object.hasOwn(headerMapping, header)) {
45
66
  if (!headerMapping[header]) {
46
67
  continue;
47
68
  }
48
- row[headerMapping[header]] = value;
69
+ row[headerMapping[header]] = (0, formula_1.stripFormulaGuard)(value);
49
70
  }
50
71
  else if (isSafeColumnName(header)) {
51
- row[header] = value;
72
+ row[header] = (0, formula_1.stripFormulaGuard)(value);
52
73
  }
53
74
  }
54
75
  if (!Object.keys(row).length) {
55
76
  continue;
56
77
  }
57
- rows.push(row);
78
+ rows.push({ data: row, source, line: index + 2 });
58
79
  }
59
- return rows;
80
+ return { columns: parsed.meta.fields ?? [], rows };
60
81
  }
@@ -0,0 +1,13 @@
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.default = serialize;
7
+ const papaparse_1 = __importDefault(require("papaparse"));
8
+ // Import reports are opened in spreadsheets, so formula-shaped cells must never
9
+ // be emitted verbatim. Papa Parse prefixes them with an apostrophe when this
10
+ // option is enabled.
11
+ function serialize(rows, { columns, header = true } = {}) {
12
+ return papaparse_1.default.unparse(rows, { columns, header, escapeFormulae: true });
13
+ }
@@ -0,0 +1,164 @@
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.default = buildCompletionEmail;
7
+ const report_1 = __importDefault(require("./report"));
8
+ const errors_file_1 = __importDefault(require("./errors-file"));
9
+ const { slugify } = require('@tryghost/string');
10
+ const IMPORTED_POST_PREVIEW_LIMIT = 10;
11
+ function escapeHTML(value) {
12
+ return value
13
+ .replace(/&/g, '&')
14
+ .replace(/</g, '&lt;')
15
+ .replace(/>/g, '&gt;')
16
+ .replace(/"/g, '&quot;')
17
+ .replace(/'/g, '&#39;');
18
+ }
19
+ function countsFor(run) {
20
+ const counts = {
21
+ created: 0,
22
+ updated: 0,
23
+ skipped: 0,
24
+ failed: 0,
25
+ warningRows: 0,
26
+ };
27
+ for (const row of run.rows) {
28
+ counts[row.status] += 1;
29
+ if (row.warnings?.length) {
30
+ counts.warningRows += 1;
31
+ }
32
+ }
33
+ return counts;
34
+ }
35
+ function headingFor(run, counts) {
36
+ if (run.status === 'failed') {
37
+ return 'Your content import could not be completed';
38
+ }
39
+ if (counts.failed > 0 && counts.created + counts.updated === 0) {
40
+ return 'Your content import was unsuccessful';
41
+ }
42
+ return 'Your content import is complete';
43
+ }
44
+ function formatNumber(value) {
45
+ return value.toLocaleString();
46
+ }
47
+ function summaryItems(counts, hasErrorsFile) {
48
+ const items = [
49
+ ['Created', counts.created],
50
+ ['Updated', counts.updated],
51
+ ['Skipped', counts.skipped],
52
+ ['Failed', counts.failed],
53
+ ];
54
+ return items
55
+ .map(([label, count]) => {
56
+ const attachmentCopy = label === 'Failed' && hasErrorsFile ? ' (see attached errors.csv)' : '';
57
+ return `<li style="margin-bottom: 6px;"><strong>${label}:</strong> ${formatNumber(count)}${attachmentCopy}</li>`;
58
+ })
59
+ .join('');
60
+ }
61
+ function importTagSlug(runId) {
62
+ // Internal tag slugs replace the leading # with "hash-" in the model layer.
63
+ return slugify(`#Import Run ${runId}`.replace(/^#/, 'hash-'));
64
+ }
65
+ function importedPostLinks(run, adminUrl) {
66
+ const importedRows = run.rows.filter((row) => row.status === 'created' || row.status === 'updated');
67
+ if (!importedRows.length) {
68
+ return '';
69
+ }
70
+ const rows = importedRows.filter((row) => row.url);
71
+ const previewRows = rows.slice(0, IMPORTED_POST_PREVIEW_LIMIT);
72
+ const hasPages = importedRows.some((row) => row.postType === 'page');
73
+ const hasPosts = importedRows.some((row) => row.postType !== 'page');
74
+ const links = previewRows
75
+ .map((row) => {
76
+ const title = escapeHTML(row.title || `Row ${row.line}`);
77
+ return `<li style="margin-bottom: 6px;"><a href="${escapeHTML(row.url)}" style="color: #3A464C;">${title}</a></li>`;
78
+ })
79
+ .join('');
80
+ const remaining = importedRows.length - previewRows.length;
81
+ const remainingCopy = remaining
82
+ ? `<p style="font-size: 16px; line-height: 25px; color: #3A464C;">Including ${formatNumber(remaining)} more.</p>`
83
+ : '';
84
+ const tag = encodeURIComponent(importTagSlug(run.id));
85
+ const postsUrl = escapeHTML(new URL(`#/posts?tag=${tag}`, adminUrl).href);
86
+ const pagesUrl = escapeHTML(new URL(`#/pages?tag=${tag}`, adminUrl).href);
87
+ let heading = 'Imported posts';
88
+ if (hasPages) {
89
+ heading = hasPosts ? 'Imported posts and pages' : 'Imported pages';
90
+ }
91
+ const filteredLinks = [
92
+ hasPosts ? `<a href="${postsUrl}" style="color: #3A464C;">View imported posts</a>` : '',
93
+ hasPages ? `<a href="${pagesUrl}" style="color: #3A464C;">View imported pages</a>` : '',
94
+ ]
95
+ .filter(Boolean)
96
+ .join(' · ');
97
+ const preview = links
98
+ ? `<ul style="font-size: 16px; line-height: 25px; color: #3A464C; padding-left: 24px;">${links}</ul>`
99
+ : '';
100
+ return `<h2 style="color: #15212A; font-size: 18px; line-height: 24px; margin: 32px 0 12px;">${heading}</h2>
101
+ ${preview}
102
+ ${remainingCopy}
103
+ <p style="font-size: 16px; line-height: 25px; color: #3A464C;">${filteredLinks}</p>`;
104
+ }
105
+ function renderCompletionEmail(run, recipient, adminUrl, hasErrorsFile) {
106
+ const counts = countsFor(run);
107
+ const heading = headingFor(run, counts);
108
+ const warningCopy = counts.warningRows
109
+ ? `<p style="font-size: 16px; line-height: 25px; color: #3A464C;"><strong>${formatNumber(counts.warningRows)}</strong> ${counts.warningRows === 1 ? 'post has' : 'posts have'} warnings.</p>`
110
+ : '';
111
+ const failureCopy = run.status === 'failed'
112
+ ? '<p style="font-size: 16px; line-height: 25px; color: #3A464C;">Something went wrong on our end before the import could finish. There is nothing wrong with your file, and it is safe to try again.</p>'
113
+ : '';
114
+ const escapedRecipient = escapeHTML(recipient);
115
+ return `<!doctype html>
116
+ <html>
117
+ <head>
118
+ <meta name="viewport" content="width=device-width">
119
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
120
+ <title>${heading}</title>
121
+ </head>
122
+ <body style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; color: #3A464C; margin: 0; padding: 0;">
123
+ <div style="box-sizing: border-box; margin: 0 auto; max-width: 540px; padding: 40px 20px;">
124
+ <img src="https://static.ghost.org/v4.0.0/images/ghost-orb-4.png" width="60" height="60" alt="Ghost" style="display: block; margin: 0 auto 40px;" />
125
+ <h1 style="color: #15212A; font-size: 21px; line-height: 25px; margin: 0 0 24px;">${heading}</h1>
126
+ ${failureCopy}
127
+ <p style="font-size: 16px; line-height: 25px; color: #3A464C;">The import processed ${formatNumber(run.total)} ${run.total === 1 ? 'row' : 'rows'}:</p>
128
+ <ul style="font-size: 16px; line-height: 25px; color: #3A464C; padding-left: 24px;">${summaryItems(counts, hasErrorsFile)}</ul>
129
+ ${warningCopy}
130
+ ${importedPostLinks(run, adminUrl)}
131
+ <p style="color: #738A94; font-size: 11px; line-height: 18px; margin-top: 64px;">This email was sent to <a href="mailto:${escapedRecipient}" style="color: #738A94;">${escapedRecipient}</a>.</p>
132
+ </div>
133
+ </body>
134
+ </html>`;
135
+ }
136
+ function buildCompletionEmail(run, recipient, adminUrl) {
137
+ const counts = countsFor(run);
138
+ const report = (0, report_1.default)(run);
139
+ const errorsFile = (0, errors_file_1.default)(run);
140
+ const attachments = [];
141
+ if (report) {
142
+ attachments.push({
143
+ filename: 'report.csv',
144
+ content: report,
145
+ contentType: 'text/csv',
146
+ contentDisposition: 'attachment',
147
+ });
148
+ }
149
+ if (errorsFile) {
150
+ attachments.push({
151
+ filename: 'errors.csv',
152
+ content: errorsFile,
153
+ contentType: 'text/csv',
154
+ contentDisposition: 'attachment',
155
+ });
156
+ }
157
+ return {
158
+ to: recipient,
159
+ subject: headingFor(run, counts),
160
+ html: renderCompletionEmail(run, recipient, adminUrl, Boolean(errorsFile)),
161
+ forceTextContent: true,
162
+ attachments,
163
+ };
164
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = buildErrorsFile;
4
+ const csv_1 = require("../csv");
5
+ const ANNOTATION_NAMES = ['import_status', 'import_reason', 'import_media_failures'];
6
+ function isActionable(row) {
7
+ return Boolean(row.source) && row.status === 'failed';
8
+ }
9
+ function uniqueColumnName(preferred, used) {
10
+ let candidate = preferred;
11
+ let suffix = 2;
12
+ while (used.has(candidate)) {
13
+ candidate = `${preferred}_${suffix}`;
14
+ suffix += 1;
15
+ }
16
+ used.add(candidate);
17
+ return candidate;
18
+ }
19
+ function buildErrorsFile(run) {
20
+ const rows = run.rows.filter(isActionable);
21
+ if (!rows.length) {
22
+ return undefined;
23
+ }
24
+ const usedColumns = new Set(run.sourceColumns);
25
+ const [outcomeColumn, reasonColumn, mediaFailuresColumn] = ANNOTATION_NAMES.map((name) => uniqueColumnName(name, usedColumns));
26
+ const columns = [outcomeColumn, reasonColumn, mediaFailuresColumn, ...run.sourceColumns];
27
+ return (0, csv_1.serialize)(rows.map((row) => {
28
+ const sourceCells = Object.fromEntries(run.sourceColumns.map((column) => [column, row.source?.[column] ?? '']));
29
+ return {
30
+ ...sourceCells,
31
+ [outcomeColumn]: row.status,
32
+ [reasonColumn]: row.reason ?? '',
33
+ [mediaFailuresColumn]: row.mediaFailures?.length ? JSON.stringify(row.mediaFailures) : '',
34
+ };
35
+ }), { columns });
36
+ }