ghost 6.64.0 → 6.65.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 (672) hide show
  1. package/bin/create-migration.js +15 -0
  2. package/components/tryghost-adapter-base-scheduling-0.2.4.tgz +0 -0
  3. package/components/tryghost-api-framework-0.0.0.tgz +0 -0
  4. package/components/tryghost-i18n-0.0.0.tgz +0 -0
  5. package/components/tryghost-kg-default-cards-10.3.6.tgz +0 -0
  6. package/components/tryghost-kg-lexical-html-renderer-1.5.1.tgz +0 -0
  7. package/components/tryghost-metafield-types-0.0.0.tgz +0 -0
  8. package/content/themes/casper/assets/built/casper.js +1 -1
  9. package/content/themes/casper/assets/built/screen.css +1 -1
  10. package/content/themes/casper/assets/css/screen.css +15 -2
  11. package/content/themes/casper/assets/js/init.js +37 -9
  12. package/content/themes/casper/package.json +8 -8
  13. package/content/themes/source/assets/built/screen.css +1 -1
  14. package/content/themes/source/assets/built/source.js +1 -1
  15. package/content/themes/source/assets/css/screen.css +14 -1
  16. package/content/themes/source/assets/js/main.js +24 -4
  17. package/content/themes/source/package.json +7 -7
  18. package/core/boot.js +22 -16
  19. package/core/bridge.js +1 -1
  20. package/core/built/admin/assets/Combination-DQ6f9A5P.js +41 -0
  21. package/core/built/admin/assets/{PolarAngleAxis-a81ieQUO.js → PolarAngleAxis-BBaU7PRV.js} +1 -1
  22. package/core/built/admin/assets/RadialBarChart-Bp20JPnA.js +1 -0
  23. package/core/built/admin/assets/{about-39CAfroa.js → about-Dp43j18M.js} +1 -1
  24. package/core/built/admin/assets/accordion-h62UzFhj.js +1 -0
  25. package/core/built/admin/assets/account-migration-CBTbtX6Q.js +1 -0
  26. package/core/built/admin/assets/action-list-DC9QpX-l.js +1 -0
  27. package/core/built/admin/assets/activitypub/{account-migration-KA6Jjf9L.js → account-migration-B1DUZS4Q.js} +8 -8
  28. package/core/built/admin/assets/activitypub/activitypub.js +24 -11
  29. package/core/built/admin/assets/activitypub/avatar-CRGud8yf.js +153 -0
  30. package/core/built/admin/assets/activitypub/{avatar-flipboard-csQaNeYK.js → avatar-flipboard-BZNFAXfm.js} +3 -3
  31. package/core/built/admin/assets/activitypub/{bluesky-sharing-C7nTDMvw.js → bluesky-sharing-Cg_v-LwS.js} +30 -31
  32. package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-Ck3O4oo2.js → chunk-KS7C4IRE-CTUL98Lc.js} +2498 -1885
  33. package/core/built/admin/assets/activitypub/{content-formatters-CLcjaBwV.js → content-formatters-D7GWP7nH.js} +23 -24
  34. package/core/built/admin/assets/activitypub/{copy-DcOh8kXw.js → copy-CgzTvoeR.js} +2 -2
  35. package/core/built/admin/assets/activitypub/{domain-fZ-X8i4-.js → domain-C3G_nkHU.js} +46 -46
  36. package/core/built/admin/assets/activitypub/{edit-profile-BqMLQV88.js → edit-profile-C8cCY-AK.js} +927 -928
  37. package/core/built/admin/assets/activitypub/{explore-B4yPoP8E.js → explore-NlDT4inq.js} +11 -11
  38. package/core/built/admin/assets/activitypub/{feed-BRC_mvAe.js → feed-DF22jCTV.js} +10 -10
  39. package/core/built/admin/assets/activitypub/{field-COacNgOt.js → field-95-iuTte.js} +4 -4
  40. package/core/built/admin/assets/activitypub/{hash-C7u-irQQ.js → hash-R7LMLnhY.js} +2 -2
  41. package/core/built/admin/assets/activitypub/{inbox-CbTTdDQ3.js → inbox-DUJUiV6x.js} +2 -2
  42. package/core/built/admin/assets/activitypub/{inbox-BW-yvUoN.js → inbox-DerExBzA.js} +416 -414
  43. package/core/built/admin/assets/activitypub/{label-kPMmYI7l.js → label-EwCn1QYc.js} +4 -4
  44. package/core/built/admin/assets/activitypub/layout-Dy6NnW1G.js +1 -0
  45. package/core/built/admin/assets/activitypub/{moderation-CuidJzc0.js → moderation-BSkfiVYo.js} +73 -75
  46. package/core/built/admin/assets/activitypub/{note-CAMRRSqD.js → note-DOnAJTXK.js} +9 -9
  47. package/core/built/admin/assets/activitypub/{notifications-BmNlXGgp.js → notifications-DZc6XTjG.js} +27 -27
  48. package/core/built/admin/assets/activitypub/{onboarding-Dmun9jr_.js → onboarding-8vEIwYTH.js} +3 -3
  49. package/core/built/admin/assets/activitypub/{onboarding-Bqg7fh_L.js → onboarding-DYVqbNmp.js} +2 -2
  50. package/core/built/admin/assets/activitypub/{preferences--oH19wf9.js → preferences-DDok6F2N.js} +1418 -1390
  51. package/core/built/admin/assets/activitypub/{profile-DH_tQx2D.js → profile-DjVa2hUk.js} +254 -255
  52. package/core/built/admin/assets/activitypub/{reply-Cap9PxSH.js → reply-DtXBpHxu.js} +2 -2
  53. package/core/built/admin/assets/activitypub/routes-Cw302e1c.js +4946 -0
  54. package/core/built/admin/assets/activitypub/{separator-CAxxu8VV.js → separator-FpRpVDd3.js} +8 -9
  55. package/core/built/admin/assets/activitypub/settings-yK-puALn.js +314 -0
  56. package/core/built/admin/assets/activitypub/{site-BYps63oh.js → site-CHw0R2PA.js} +2 -2
  57. package/core/built/admin/assets/activitypub/{step-1-CKjR2KAm.js → step-1-737bnvxk.js} +7 -7
  58. package/core/built/admin/assets/activitypub/{step-2-B6PKfOUQ.js → step-2-DBaIsAsM.js} +7 -7
  59. package/core/built/admin/assets/activitypub/{step-3-C-qBbcUo.js → step-3-BBmy9m9V.js} +23 -23
  60. package/core/built/admin/assets/activitypub/tabs-LQ2xirdm.js +1101 -0
  61. package/core/built/admin/assets/activitypub/{topic-filter-DsUOuu0k.js → topic-filter-75Nu0CKj.js} +4 -4
  62. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CX9snsLs.js → use-activity-pub-queries-CYI2QJMs.js} +58 -58
  63. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-DsJTGDSS.js → use-navigate-with-base-path-B4jZ8hyg.js} +3924 -1403
  64. package/core/built/admin/assets/activitypub/{use-reply-chain-data-AJi9zkzm.js → use-reply-chain-data-BHmFxc3U.js} +6 -6
  65. package/core/built/admin/assets/activitypub/users-CSjOwpT9.js +886 -0
  66. package/core/built/admin/assets/activitypub/{x-DR8AT3-4.js → x-B9ptWCds.js} +2 -2
  67. package/core/built/admin/assets/{add-integration-modal-B12oRifD.js → add-integration-modal-BwePhq95.js} +1 -1
  68. package/core/built/admin/assets/add-newsletter-modal-Zv0I7Msm.js +1 -0
  69. package/core/built/admin/assets/{add-recommendation-modal-CJf92edY.js → add-recommendation-modal-cdLv0wHy.js} +1 -1
  70. package/core/built/admin/assets/analytics-DY8_SaP7.js +1 -0
  71. package/core/built/admin/assets/announcement-bar-modal-C-2yWwag.js +1 -0
  72. package/core/built/admin/assets/api-keys-BHKATUqQ.js +1 -0
  73. package/core/built/admin/assets/{audience-6GXevic-.js → audience-wRcSN0jV.js} +1 -1
  74. package/core/built/admin/assets/{automated-emails-DJQ4uNZo.js → automated-emails-CzUa3v4d.js} +1 -1
  75. package/core/built/admin/assets/automations-DupSsSOr.js +1 -0
  76. package/core/built/admin/assets/badge-CI1Z0xyG.js +1 -0
  77. package/core/built/admin/assets/bluesky-sharing-B8PC1SBj.js +1 -0
  78. package/core/built/admin/assets/box-LUuYRT-1.js +1 -0
  79. package/core/built/admin/assets/brand-icon-CrmbX86T.js +1 -0
  80. package/core/built/admin/assets/breadcrumb-DDDpzLyo.js +1 -0
  81. package/core/built/admin/assets/button-D414TI3B.js +1 -0
  82. package/core/built/admin/assets/calendar-Cp_opo3i.js +1 -0
  83. package/core/built/admin/assets/card-C2dfd5u0.js +1 -0
  84. package/core/built/admin/assets/chart-B_UNLXse.js +63 -0
  85. package/core/built/admin/assets/chart-helpers-Czu_9QpJ.js +1 -0
  86. package/core/built/admin/assets/checkbox-CFFqf0Nh.js +1 -0
  87. package/core/built/admin/assets/{chunk.368.22ce1b732c0b40abd3f1.js → chunk.201.6961b57ed7a281ac193e.js} +23 -19
  88. package/core/built/admin/assets/chunk.524.0b850e72ec49a0fa3882.js +25 -0
  89. package/core/built/admin/assets/chunk.582.db2ae9e968256e8d58ac.js +27 -0
  90. package/core/built/admin/assets/code-editor-view-Bd_nnHKk.js +1 -0
  91. package/core/built/admin/assets/code-editor-view-BeU3Yf9M.js +1 -0
  92. package/core/built/admin/assets/color-picker-Bzm1V7qg.js +3 -0
  93. package/core/built/admin/assets/color-picker-field-DP0moHz9.js +1 -0
  94. package/core/built/admin/assets/combobox-6_ruuTuj.js +1 -0
  95. package/core/built/admin/assets/command-B5wfm6pd.js +1 -0
  96. package/core/built/admin/assets/comments-BB26dUSl.js +1 -0
  97. package/core/built/admin/assets/confirmation-context-znVJD4pX.js +1 -0
  98. package/core/built/admin/assets/container-CHHif8km.js +1 -0
  99. package/core/built/admin/assets/{content-api-modal-0ZjuLcQ8.js → content-api-modal-DOHwl0yZ.js} +1 -1
  100. package/core/built/admin/assets/context-menu-BpY46Dlb.js +1 -0
  101. package/core/built/admin/assets/copy-field-DRBx6Ady.js +1 -0
  102. package/core/built/admin/assets/create-ghost-browse-value-source-eIXo5Qws.js +1 -0
  103. package/core/built/admin/assets/custom-field-type-option-DrSlETOI.js +1 -0
  104. package/core/built/admin/assets/custom-integration-modal-Dgo425Ec.js +1 -0
  105. package/core/built/admin/assets/data-list-DM8yqpXi.js +1 -0
  106. package/core/built/admin/assets/date-range-select-DdC23c13.js +1 -0
  107. package/core/built/admin/assets/design-and-theme-modal-CbSu9IT4.js +2 -0
  108. package/core/built/admin/assets/detail-page-CN4NdhpV.js +1 -0
  109. package/core/built/admin/assets/dirty-confirm-dialog-BT_Vq9TD.js +1 -0
  110. package/core/built/admin/assets/{disabled-sources-indicator-CLZhMv8J.js → disabled-sources-indicator-C5xtW0Q2.js} +1 -1
  111. package/core/built/admin/assets/dist-B40T-RCQ.js +1 -0
  112. package/core/built/admin/assets/dist-CrlaEoaK.js +1 -0
  113. package/core/built/admin/assets/domain-BPReCZa8.js +1 -0
  114. package/core/built/admin/assets/dropzone-B2auMkf2.js +5 -0
  115. package/core/built/admin/assets/edit-profile-B7XTBzKe.js +1 -0
  116. package/core/built/admin/assets/editor-BcaC9dqm.js +8 -0
  117. package/core/built/admin/assets/editor-screen-FuMpItC8.js +27 -0
  118. package/core/built/admin/assets/email-sending-status-provider-9d81o1C2.js +1 -0
  119. package/core/built/admin/assets/embed-signup-form-modal-CbVbMsxl.js +8 -0
  120. package/core/built/admin/assets/empty-indicator-CSQts7GV.js +1 -0
  121. package/core/built/admin/assets/explore-DbSXFTJN.js +1 -0
  122. package/core/built/admin/assets/feature-image-placeholder-DFXA4vfJ.js +1 -0
  123. package/core/built/admin/assets/feed-W7RlF-Gp.js +1 -0
  124. package/core/built/admin/assets/field-BXEh3ptU.js +1 -0
  125. package/core/built/admin/assets/filter-sources-DHK2IoCk.js +1 -0
  126. package/core/built/admin/assets/filters-0SYNTqyQ.js +1 -0
  127. package/core/built/admin/assets/filters-DTtZKQyq.js +1 -0
  128. package/core/built/admin/assets/{first-promoter-modal-wr73Dldc.js → first-promoter-modal-DcoN_OXu.js} +1 -1
  129. package/core/built/admin/assets/{ghost-a1e6e172c6917e8ef8fa6b0007756d9b.js → ghost-25495154132af7305cb7b646fd83f48f.js} +562 -514
  130. package/core/built/admin/assets/gift-link-modal-BX-Q1TOk.js +1 -0
  131. package/core/built/admin/assets/grid-BEUIDQX7.js +1 -0
  132. package/core/built/admin/assets/growth-G6PLO4aF.js +1 -0
  133. package/core/built/admin/assets/growth-tkD-fBOY.js +3 -0
  134. package/core/built/admin/assets/heading-ih-fOVqF.js +1 -0
  135. package/core/built/admin/assets/history-modal-Ca-gStoq.js +1 -0
  136. package/core/built/admin/assets/html-field-B3CrKV9Y.js +1 -0
  137. package/core/built/admin/assets/images-H-BIm8OQ.js +1 -0
  138. package/core/built/admin/assets/{inbox-DgSZ6w2p.js → inbox-DBbDVMPz.js} +14 -14
  139. package/core/built/admin/assets/index-DOF2jDq0.js +4 -0
  140. package/core/built/admin/assets/index-JrrNchW1.css +2 -0
  141. package/core/built/admin/assets/input-group-CvH-3d3F.js +1 -0
  142. package/core/built/admin/assets/input-surface-CL23Mlh8.js +1 -0
  143. package/core/built/admin/assets/integration-header-C7GXGis_.js +1 -0
  144. package/core/built/admin/assets/invite-user-modal-EJ2_6RpV.js +1 -0
  145. package/core/built/admin/assets/isPlainObject-BzzgUf1E.js +1 -0
  146. package/core/built/admin/assets/{koenig-lexical-CXVgQFUk.js → koenig-lexical-Dkwee5FG.js} +1 -1
  147. package/core/built/admin/assets/koenig-loader-BzXCXHoA.js +2 -0
  148. package/core/built/admin/assets/kpi-card-iCDUTgOR.js +1 -0
  149. package/core/built/admin/assets/kpi-card-jTnpLKGr.js +1 -0
  150. package/core/built/admin/assets/kpi-helpers-Criu0YAf.js +1 -0
  151. package/core/built/admin/assets/kpi-tabs-DbOmmyOo.js +1 -0
  152. package/core/built/admin/assets/label-Du0yqMqL.js +1 -0
  153. package/core/built/admin/assets/label-picker-Bq1XXr1D.js +1 -0
  154. package/core/built/admin/assets/{labels-KMkFoRG6.js → labels-0x2NfLtC.js} +1 -1
  155. package/core/built/admin/assets/layout-Ckwzsnsu.js +1 -0
  156. package/core/built/admin/assets/lib-CCz2mjm8.js +2 -0
  157. package/core/built/admin/assets/link-helpers-o9GgBTni.js +1 -0
  158. package/core/built/admin/assets/{links-FwNCVyVe.js → links-Ctujreuh.js} +1 -1
  159. package/core/built/admin/assets/list-page-CH_wnDC3.js +1 -0
  160. package/core/built/admin/assets/member-activity-CsAh6MxK.js +1 -0
  161. package/core/built/admin/assets/member-activity-feed-0ytsQbtY.js +1 -0
  162. package/core/built/admin/assets/member-custom-fields-CTFGnnYv.js +1 -0
  163. package/core/built/admin/assets/member-detail-QU3vB1ML.js +1 -0
  164. package/core/built/admin/assets/members-CtMVO_AB.js +4 -0
  165. package/core/built/admin/assets/modal-page-8Ipfl51B.js +1 -0
  166. package/core/built/admin/assets/moderation-B8VtA-W0.js +1 -0
  167. package/core/built/admin/assets/multi-select-combobox-_9wEYmGv.js +1 -0
  168. package/core/built/admin/assets/navigation-modal-CGcjJvkc.js +1 -0
  169. package/core/built/admin/assets/newsletter-BpHjjqmq.js +1 -0
  170. package/core/built/admin/assets/newsletter-detail-modal-DzTbGOyo.js +1 -0
  171. package/core/built/admin/assets/newsletter-emails-BMKH4Osp.js +1 -0
  172. package/core/built/admin/assets/newsletters-BxC84_cL.js +1 -0
  173. package/core/built/admin/assets/note-BPtEzzZS.js +1 -0
  174. package/core/built/admin/assets/notifications-ng3ulEdp.js +1 -0
  175. package/core/built/admin/assets/offers-container-modal-CwqnRa3T.js +1 -0
  176. package/core/built/admin/assets/onboarding-route-CplZzd7F.js +1 -0
  177. package/core/built/admin/assets/overview-B0yD9OoX.js +1 -0
  178. package/core/built/admin/assets/overview-C4eJzQqi.js +1 -0
  179. package/core/built/admin/assets/page-header-CdCpekG9.js +1 -0
  180. package/core/built/admin/assets/pagemenu-CKQD9hoP.js +1 -0
  181. package/core/built/admin/assets/pages-route-CKsnxIvw.js +1 -0
  182. package/core/built/admin/assets/{pintura-modal-BljM7hf8.js → pintura-modal-C5jzeyGk.js} +1 -1
  183. package/core/built/admin/assets/portal-frame-DYo0bh0E.js +1 -0
  184. package/core/built/admin/assets/portal-modal-C0d8xjKa.js +1 -0
  185. package/core/built/admin/assets/post-analytics-C5C7IyAm.js +1 -0
  186. package/core/built/admin/assets/post-analytics-header-DKWHwoK2.js +1 -0
  187. package/core/built/admin/assets/{post-analytics-provider-CgreZDjS.js → post-analytics-provider-BqCPPxmW.js} +1 -1
  188. package/core/built/admin/assets/post-share-modal-wxmqbAv_.js +1 -0
  189. package/core/built/admin/assets/posts-list-screen-B978ZO0R.js +2 -0
  190. package/core/built/admin/assets/posts-route-CDqxKazl.js +1 -0
  191. package/core/built/admin/assets/preferences-CnejTRc4.js +5 -0
  192. package/core/built/admin/assets/preview-chrome-C7yZWVLZ.js +1 -0
  193. package/core/built/admin/assets/preview-modal-CFUxUGHp.js +1 -0
  194. package/core/built/admin/assets/profile-ClRGiOqA.js +1 -0
  195. package/core/built/admin/assets/radio-group-CTmLo0h9.js +1 -0
  196. package/core/built/admin/assets/router-provider-DDZvgS3V.js +1 -0
  197. package/core/built/admin/assets/select-D_4xWFYv.js +1 -0
  198. package/core/built/admin/assets/settings-BRl7-7a9.js +1 -0
  199. package/core/built/admin/assets/settings-BoQcPa-y.js +4 -0
  200. package/core/built/admin/assets/settings-breadcrumbs-C9Yxl81q.js +1 -0
  201. package/core/built/admin/assets/{dist-BXHrDV19.js → shade-scope-Cccj-c7I-D0XwkdKQ.js} +5 -3
  202. package/core/built/admin/assets/share-modal-DBqZobuR.js +1 -0
  203. package/core/built/admin/assets/simple-pagination-D5t_ox6x.js +1 -0
  204. package/core/built/admin/assets/skeleton-CPcTiTSX.js +1 -0
  205. package/core/built/admin/assets/{slack-modal-D7W_fNON.js → slack-modal-BF9ex8cd.js} +1 -1
  206. package/core/built/admin/assets/sort-button-C0EXGjnS.js +1 -0
  207. package/core/built/admin/assets/{source-icon-CQboPZQ3.js → source-icon-BzzftCQ2.js} +1 -1
  208. package/core/built/admin/assets/stats-view-CzpGoHzF.js +1 -0
  209. package/core/built/admin/assets/{step-1-BKhjy0PV.js → step-1-CBUYUPPF.js} +1 -1
  210. package/core/built/admin/assets/{step-2-PxA1OM7R.js → step-2-CEIXUwQa.js} +1 -1
  211. package/core/built/admin/assets/{step-3-BJbAiq16.js → step-3-C1HA5i1N.js} +2 -2
  212. package/core/built/admin/assets/stripe-button-DMFsLs80.js +1 -0
  213. package/core/built/admin/assets/stripe-connect-modal-CEvxCUTv.js +1 -0
  214. package/core/built/admin/assets/switch-DoOJF2yg.js +1 -0
  215. package/core/built/admin/assets/table-BDYbUHJz.js +1 -0
  216. package/core/built/admin/assets/tabs-BZyz4ZCH.js +1 -0
  217. package/core/built/admin/assets/tag-detail-CFzaz_9E.js +2 -0
  218. package/core/built/admin/assets/tag-picker-BKUCZuRc.js +1 -0
  219. package/core/built/admin/assets/tags-Bs1FiD7m.js +1 -0
  220. package/core/built/admin/assets/{testimonials-modal-Dkt6ODLW.js → testimonials-modal-CVm0_7E-.js} +1 -1
  221. package/core/built/admin/assets/text-BLI0C8Mc.js +1 -0
  222. package/core/built/admin/assets/textarea-C5qolByg.js +1 -0
  223. package/core/built/admin/assets/tier-detail-modal-Cmwzi-e7.js +1 -0
  224. package/core/built/admin/assets/toggle-group-BTL1QsWY.js +1 -0
  225. package/core/built/admin/assets/{topic-filter-DVOe3D-b.js → topic-filter-BMjhquv7.js} +1 -1
  226. package/core/built/admin/assets/transistor-modal-hMv53jF8.js +1 -0
  227. package/core/built/admin/assets/trend-badge-1w_HEzkG.js +1 -0
  228. package/core/built/admin/assets/{unsplash-modal-DXt7Brum.js → unsplash-modal-jzC8lEB2.js} +1 -1
  229. package/core/built/admin/assets/use-analytics-data-B3lGOxsC.js +1 -0
  230. package/core/built/admin/assets/use-availability-DnKqOyqd.js +1 -0
  231. package/core/built/admin/assets/{use-check-theme-limit-error-JD-J5aaZ.js → use-check-theme-limit-error-C1tc2P4N.js} +2 -2
  232. package/core/built/admin/assets/use-filter-params-DU-aiaTb.js +1 -0
  233. package/core/built/admin/assets/{use-filterable-api-CEMSAYPg.js → use-filterable-api-CxCrnPxG.js} +1 -1
  234. package/core/built/admin/assets/use-growth-stats-E13rMQUE.js +1 -0
  235. package/core/built/admin/assets/use-limiter-DlL7au90.js +19 -0
  236. package/core/built/admin/assets/{use-navigate-with-base-path-88EQpw2k.js → use-navigate-with-base-path-BzgNwMAx.js} +1 -1
  237. package/core/built/admin/assets/{use-pintura-config-BmU45Ruy.js → use-pintura-config-CtEV1JQY.js} +1 -1
  238. package/core/built/admin/assets/{use-pintura-editor-Bz2bZ4iT.js → use-pintura-editor-CaxE64kD.js} +1 -1
  239. package/core/built/admin/assets/use-reply-chain-data-BST2OgCM.js +1 -0
  240. package/core/built/admin/assets/use-sending-eta-DR9ueMH8.js +1 -0
  241. package/core/built/admin/assets/use-setting-group-ldtX1Vsk.js +1 -0
  242. package/core/built/admin/assets/use-settings-navigation-CzGU4sus.js +1 -0
  243. package/core/built/admin/assets/use-staff-users-DoeXaPsb.js +1 -0
  244. package/core/built/admin/assets/use-tinybird-query-BaBNCaj0.js +1 -0
  245. package/core/built/admin/assets/{use-tinybird-token-Lk1RSn0t.js → use-tinybird-token-CnZ19NDf.js} +1 -1
  246. package/core/built/admin/assets/{use-url-input-DpmbDppC.js → use-url-input-C5KHJw79.js} +1 -1
  247. package/core/built/admin/assets/user-detail-modal-Dm0KfPeR.js +1 -0
  248. package/core/built/admin/assets/{vendor-2dd263291938d407802568d3357ec69e.js → vendor-97e91ff8dd1457c668f9ef103ee1a77b.js} +2748 -2977
  249. package/core/built/admin/assets/virtual-list-tAuPUICw.js +1 -0
  250. package/core/built/admin/assets/web-BQlKcW2x.js +2 -0
  251. package/core/built/admin/assets/web-NTT5DkEz.js +1 -0
  252. package/core/built/admin/assets/{zapier-modal-BafLUO4R.js → zapier-modal-DsHG9Oev.js} +1 -1
  253. package/core/built/admin/index.html +22 -24
  254. package/core/frontend/apps/private-blogging/index.js +2 -2
  255. package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
  256. package/core/frontend/apps/private-blogging/lib/router.js +1 -1
  257. package/core/frontend/helpers/cancel_link.js +3 -3
  258. package/core/frontend/helpers/content.js +2 -2
  259. package/core/frontend/helpers/content_api_url.js +1 -1
  260. package/core/frontend/helpers/date.js +1 -1
  261. package/core/frontend/helpers/foreach.js +5 -5
  262. package/core/frontend/helpers/get.js +7 -4
  263. package/core/frontend/helpers/ghost_head.js +2 -2
  264. package/core/frontend/helpers/has.js +1 -3
  265. package/core/frontend/helpers/link.js +7 -7
  266. package/core/frontend/helpers/link_class.js +2 -2
  267. package/core/frontend/helpers/navigation.js +1 -2
  268. package/core/frontend/helpers/recommendations.js +5 -6
  269. package/core/frontend/helpers/tiers.js +1 -1
  270. package/core/frontend/helpers/total_members.js +1 -1
  271. package/core/frontend/helpers/total_paid_members.js +1 -1
  272. package/core/frontend/meta/author-url.js +1 -1
  273. package/core/frontend/meta/modified-date.js +1 -1
  274. package/core/frontend/meta/published-date.js +1 -1
  275. package/core/frontend/meta/schema.js +3 -8
  276. package/core/frontend/meta/structured-data.js +1 -2
  277. package/core/frontend/services/assets-minification/minifier.js +1 -1
  278. package/core/frontend/services/data/entry-lookup.js +1 -1
  279. package/core/frontend/services/data/fetch-data.js +2 -2
  280. package/core/frontend/services/rendering/format-response.js +1 -1
  281. package/core/frontend/services/rendering/renderer.js +3 -0
  282. package/core/frontend/services/rendering/templates.js +1 -1
  283. package/core/frontend/services/routing/controllers/static.js +1 -1
  284. package/core/frontend/services/routing/parent-router.js +1 -1
  285. package/core/frontend/services/routing/registry.js +1 -1
  286. package/core/frontend/services/sitemap/base-site-map-generator.js +68 -97
  287. package/core/frontend/services/sitemap/site-map-index-generator.js +16 -21
  288. package/core/frontend/services/sitemap/site-map-manager.js +82 -43
  289. package/core/frontend/services/sitemap/sitemap-xml.js +72 -0
  290. package/core/frontend/services/theme-engine/handlebars/utils.js +8 -8
  291. package/core/frontend/services/theme-engine/i18n/i18n.js +4 -5
  292. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +1 -1
  293. package/core/frontend/services/theme-engine/preview.js +1 -1
  294. package/core/frontend/utils/member-count.js +2 -2
  295. package/core/frontend/web/routers/serve-public-file.js +1 -1
  296. package/core/server/adapters/scheduling/README.md +4 -0
  297. package/core/server/adapters/scheduling/get-scheduler-idempotency-key.js +21 -0
  298. package/core/server/adapters/storage/LocalStorageBase.js +2 -1
  299. package/core/server/api/endpoints/automated-emails.js +8 -2
  300. package/core/server/api/endpoints/db.js +1 -1
  301. package/core/server/api/endpoints/emails.js +3 -3
  302. package/core/server/api/endpoints/mail.js +1 -1
  303. package/core/server/api/endpoints/member-signin-urls.js +1 -1
  304. package/core/server/api/endpoints/members.js +3 -3
  305. package/core/server/api/endpoints/oembed.js +1 -1
  306. package/core/server/api/endpoints/posts.js +1 -1
  307. package/core/server/api/endpoints/settings.js +2 -2
  308. package/core/server/api/endpoints/themes.js +5 -5
  309. package/core/server/api/endpoints/utils/serializers/input/pages.js +2 -2
  310. package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -2
  311. package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
  312. package/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js +4 -4
  313. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +8 -5
  314. package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +1 -1
  315. package/core/server/api/endpoints/utils/serializers/output/pages.js +4 -4
  316. package/core/server/api/endpoints/utils/serializers/output/posts.js +4 -4
  317. package/core/server/api/endpoints/utils/serializers/output/search-index.js +11 -11
  318. package/core/server/api/endpoints/utils/serializers/output/settings.js +1 -1
  319. package/core/server/api/endpoints/utils/serializers/output/utils/extra-attrs.js +3 -3
  320. package/core/server/api/endpoints/utils/validators/input/automated_emails.js +23 -33
  321. package/core/server/api/endpoints/utils/validators/input/settings.js +3 -3
  322. package/core/server/data/exporter/exporter.js +1 -2
  323. package/core/server/data/exporter/table-lists.js +1 -0
  324. package/core/server/data/importer/handlers/image.js +1 -2
  325. package/core/server/data/importer/handlers/json.js +1 -3
  326. package/core/server/data/importer/handlers/markdown.js +4 -8
  327. package/core/server/data/importer/import-manager.js +1 -1
  328. package/core/server/data/importer/importers/content-file-importer.js +4 -8
  329. package/core/server/data/importer/importers/data/base.js +10 -10
  330. package/core/server/data/importer/importers/data/custom-theme-settings-importer.js +1 -1
  331. package/core/server/data/importer/importers/data/data-importer.js +3 -4
  332. package/core/server/data/importer/importers/data/posts-importer.js +10 -10
  333. package/core/server/data/importer/importers/data/products-importer.js +3 -3
  334. package/core/server/data/importer/importers/data/settings-importer.js +2 -2
  335. package/core/server/data/importer/importers/data/stripe-prices-importer.js +1 -1
  336. package/core/server/data/importer/importers/data/stripe-products-importer.js +1 -1
  337. package/core/server/data/importer/importers/data/tags-importer.js +1 -1
  338. package/core/server/data/importer/importers/data/users-importer.js +2 -2
  339. package/core/server/data/importer/importers/json-to-html.js +12 -9
  340. package/core/server/data/migrations/versions/5.0/2022-05-11-13-12-rename-settings.js +1 -1
  341. package/core/server/data/migrations/versions/6.65/2026-09-16-15-35-26-add-members-metafield-change-events.js +17 -0
  342. package/core/server/data/schema/fixtures/fixture-manager.js +3 -3
  343. package/core/server/data/schema/index.js +1 -1
  344. package/core/server/data/schema/schema.js +26 -0
  345. package/core/server/data/schema/validator.js +96 -123
  346. package/core/server/data/seeders/importers/email-recipients-importer.js +2 -2
  347. package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
  348. package/core/server/data/seeders/importers/members-newsletters-importer.js +1 -1
  349. package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +1 -1
  350. package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
  351. package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
  352. package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
  353. package/core/server/data/seeders/importers/members-subscribe-events-importer.js +2 -2
  354. package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
  355. package/core/server/data/tinybird/pipes/mv_hits.pipe +25 -20
  356. package/core/server/data/tinybird/scripts/docker-analytics-manager.js +2 -2
  357. package/core/server/ghost-server.js +1 -19
  358. package/core/server/lib/image/image-size.js +1 -3
  359. package/core/server/lib/request-external.js +53 -117
  360. package/core/server/models/base/plugins/actions.js +2 -3
  361. package/core/server/models/base/plugins/crud.js +1 -2
  362. package/core/server/models/base/plugins/events.js +4 -4
  363. package/core/server/models/base/plugins/raw-knex.js +3 -3
  364. package/core/server/models/base/plugins/sanitize.js +1 -1
  365. package/core/server/models/benefit.js +1 -1
  366. package/core/server/models/collection.js +1 -1
  367. package/core/server/models/comment.js +3 -3
  368. package/core/server/models/invite.js +2 -5
  369. package/core/server/models/label.js +3 -6
  370. package/core/server/models/member.js +7 -10
  371. package/core/server/models/newsletter.js +1 -1
  372. package/core/server/models/permission.js +3 -6
  373. package/core/server/models/post.js +20 -23
  374. package/core/server/models/product.js +4 -1
  375. package/core/server/models/relations/authors.js +9 -12
  376. package/core/server/models/role.js +2 -5
  377. package/core/server/models/settings.js +3 -3
  378. package/core/server/models/tag.js +2 -5
  379. package/core/server/models/user.js +8 -10
  380. package/core/server/models/webhook.js +2 -4
  381. package/core/server/notify.js +2 -2
  382. package/core/server/services/adapter-manager/utils.js +6 -1
  383. package/core/server/services/api-version-compatibility/legacy-api-path-match.js +2 -2
  384. package/core/server/services/auth/api-key/admin.js +2 -2
  385. package/core/server/services/auth/api-key/content.js +1 -1
  386. package/core/server/services/auth/passwordreset.js +1 -1
  387. package/core/server/services/auth/setup.js +1 -3
  388. package/core/server/services/automations/README.md +19 -0
  389. package/core/server/services/automations/poll.js +3 -1
  390. package/core/server/services/automations/service.js +2 -2
  391. package/core/server/services/automations/welcome-email-automation-poll.js +199 -293
  392. package/core/server/services/comments/comments-controller.js +9 -30
  393. package/core/server/services/comments/comments-service-email-renderer.js +1 -1
  394. package/core/server/services/comments/comments-service-emails.js +1 -1
  395. package/core/server/services/comments/comments-service.js +27 -58
  396. package/core/server/services/comments/comments-stats-service.js +1 -1
  397. package/core/server/services/content-import/import/staged-file.js +9 -9
  398. package/core/server/services/content-import/index.js +1 -1
  399. package/core/server/services/custom-redirects/validation.js +1 -1
  400. package/core/server/services/email-analytics/email-analytics-service-wrapper.js +23 -55
  401. package/core/server/services/email-analytics/index.js +35 -19
  402. package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.js +31 -60
  403. package/core/server/services/email-service/README.md +35 -0
  404. package/core/server/services/email-service/batch-sending-service.js +13 -14
  405. package/core/server/services/email-service/email-renderer.js +50 -14
  406. package/core/server/services/email-service/email-service-wrapper.js +3 -2
  407. package/core/server/services/email-service/email-service.js +33 -8
  408. package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +2 -1
  409. package/core/server/services/email-service/email-templates/partials/styles.hbs +0 -6
  410. package/core/server/services/email-service/email-templates/template.hbs +1 -1
  411. package/core/server/services/email-service/jobs/send-email-job.js +12 -0
  412. package/core/server/services/email-service/mailgun-email-provider.js +7 -2
  413. package/core/server/services/email-service/sending-service.js +1 -1
  414. package/core/server/services/gifts/gift-service.js +12 -3
  415. package/core/server/services/gifts/index.js +0 -2
  416. package/core/server/services/gifts/jobs/index.js +12 -24
  417. package/core/server/services/gifts/jobs/send-gift-reminders-job.js +6 -46
  418. package/core/server/services/invitations/accept.js +2 -2
  419. package/core/server/services/jobs/job-service.js +0 -28
  420. package/core/server/services/jobs-service/jobs-service.js +5 -0
  421. package/core/server/services/jobs-service/register-job-handlers.js +17 -1
  422. package/core/server/services/lib/mailgun-client.js +16 -1
  423. package/core/server/services/lib/mailgun-recipient-message-id.js +49 -0
  424. package/core/server/services/limits.js +1 -1
  425. package/core/server/services/mail/ghost-mailer.js +1 -1
  426. package/core/server/services/member-attribution/member-attribution-service.js +1 -1
  427. package/core/server/services/member-attribution/referrer-translator.js +1 -1
  428. package/core/server/services/members/account-service.js +3 -3
  429. package/core/server/services/members/api.js +1 -1
  430. package/core/server/services/members/import-export/import/importer.js +25 -16
  431. package/core/server/services/members/import-export/import/spool.js +11 -15
  432. package/core/server/services/members/import-export/index.js +6 -4
  433. package/core/server/services/members/jobs/index.js +1 -1
  434. package/core/server/services/members/jobs/members-import-job.js +18 -0
  435. package/core/server/services/members/member-avatar.js +18 -0
  436. package/core/server/services/members/members-api/controllers/member-controller.js +2 -2
  437. package/core/server/services/members/members-api/members-api.js +4 -4
  438. package/core/server/services/members/members-api/repositories/event-repository.js +36 -1
  439. package/core/server/services/members/members-api/repositories/member-repository.js +55 -17
  440. package/core/server/services/members/members-api/repositories/product-repository.js +1 -1
  441. package/core/server/services/members/members-api/services/member-bread-service.js +4 -7
  442. package/core/server/services/members/service.js +7 -2
  443. package/core/server/services/members/stats/members-stats.js +2 -2
  444. package/core/server/services/members-events/event-storage.js +2 -2
  445. package/core/server/services/members-metafields/actions.js +23 -2
  446. package/core/server/services/members-metafields/bindings-service.js +5 -5
  447. package/core/server/services/members-metafields/definitions-service.js +2 -7
  448. package/core/server/services/members-metafields/index.js +2 -1
  449. package/core/server/services/members-metafields/queries.js +7 -1
  450. package/core/server/services/members-metafields/schema.js +49 -1
  451. package/core/server/services/members-metafields/values-service.js +112 -10
  452. package/core/server/services/mentions/mention-sending-service.js +2 -2
  453. package/core/server/services/mentions/mention.js +3 -4
  454. package/core/server/services/mentions/mentions-api.js +4 -1
  455. package/core/server/services/mentions-jobs/job-service.js +0 -28
  456. package/core/server/services/milestones/milestones-service.js +1 -1
  457. package/core/server/services/newsletters/newsletters-service.js +1 -1
  458. package/core/server/services/notifications/notifications.js +3 -3
  459. package/core/server/services/oembed/oembed-service.js +79 -3
  460. package/core/server/services/permissions/can-this.js +1 -2
  461. package/core/server/services/post-scheduling/post-scheduling.js +8 -1
  462. package/core/server/services/posts/post-scheduling.js +7 -0
  463. package/core/server/services/settings/settings-bread-service.js +3 -3
  464. package/core/server/services/settings/settings-utils.js +1 -1
  465. package/core/server/services/settings-helpers/settings-helpers.js +1 -1
  466. package/core/server/services/staff/staff-service-emails.js +9 -9
  467. package/core/server/services/stats/posts-stats-service.js +10 -10
  468. package/core/server/services/stats/referrers-stats-service.js +1 -1
  469. package/core/server/services/stats/subscription-stats-service.js +3 -3
  470. package/core/server/services/stats/utils/tinybird.js +8 -1
  471. package/core/server/services/stripe/stripe-api.js +3 -3
  472. package/core/server/services/stripe/stripe-migrations.js +6 -6
  473. package/core/server/services/themes/storage.js +21 -26
  474. package/core/server/services/themes/theme-storage.js +2 -2
  475. package/core/server/services/tiers/tier.js +15 -15
  476. package/core/server/services/tinybird-sync/index.js +3 -0
  477. package/core/server/services/tinybird-sync/tinybird-sync-service.js +7 -2
  478. package/core/server/services/update-check/update-check-service.js +3 -3
  479. package/core/server/services/webhooks/serialize.js +1 -1
  480. package/core/server/web/api/app.js +0 -5
  481. package/core/server/web/api/middleware/upload.js +29 -2
  482. package/core/server/web/parent/middleware/index.js +1 -1
  483. package/core/server/web/parent/middleware/log-request.js +5 -0
  484. package/core/server/web/parent/middleware/queue-request.js +104 -59
  485. package/core/server/web/shared/middleware/api/spam-prevention.js +2 -2
  486. package/core/shared/config/defaults.json +6 -1
  487. package/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js +1 -1
  488. package/core/shared/labs.js +3 -2
  489. package/core/shared/settings-cache/cache-manager.js +1 -1
  490. package/nodemon.json +1 -1
  491. package/package.json +6 -7
  492. package/pnpm-lock.yaml +126 -728
  493. package/pnpm-workspace.yaml +4 -7
  494. package/tsconfig.json +1 -1
  495. package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
  496. package/core/built/admin/assets/Combination-DWfJPcz9.js +0 -41
  497. package/core/built/admin/assets/RadialBarChart-CHRBhiBN.js +0 -1
  498. package/core/built/admin/assets/_baseAssignValue-DcPokYW8.js +0 -1
  499. package/core/built/admin/assets/accordion-CJmRaqK7.js +0 -1
  500. package/core/built/admin/assets/account-migration-DYf7skbb.js +0 -1
  501. package/core/built/admin/assets/action-list-Crnh-_UI.js +0 -1
  502. package/core/built/admin/assets/activitypub/avatar-DVJF5bxP.js +0 -261
  503. package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +0 -1
  504. package/core/built/admin/assets/activitypub/routes-DdyY9uFF.js +0 -7181
  505. package/core/built/admin/assets/activitypub/settings-B6EEcVkW.js +0 -315
  506. package/core/built/admin/assets/activitypub/tabs-DLhZtVde.js +0 -1083
  507. package/core/built/admin/assets/activitypub/users-inweyKL5.js +0 -1499
  508. package/core/built/admin/assets/add-newsletter-modal-CTvXiIqY.js +0 -1
  509. package/core/built/admin/assets/analytics-PHR1Fggi.js +0 -1
  510. package/core/built/admin/assets/announcement-bar-modal-Bql0CXdp.js +0 -1
  511. package/core/built/admin/assets/api-keys-CubH-7_H.js +0 -1
  512. package/core/built/admin/assets/automations-DcZSLFDU.js +0 -1
  513. package/core/built/admin/assets/badge-qP06OT7C.js +0 -1
  514. package/core/built/admin/assets/bluesky-sharing-CJw4d4Mf.js +0 -1
  515. package/core/built/admin/assets/box-CZ_LSVSy.js +0 -1
  516. package/core/built/admin/assets/brand-icon-D3ffDYaC.js +0 -1
  517. package/core/built/admin/assets/breadcrumb-Chn1Ip_q.js +0 -1
  518. package/core/built/admin/assets/button-DvIh2Bq-.js +0 -1
  519. package/core/built/admin/assets/calendar-1QGoStFM.js +0 -1
  520. package/core/built/admin/assets/card-FHQ4D10L.js +0 -1
  521. package/core/built/admin/assets/chart-helpers-CNpA7re_.js +0 -1
  522. package/core/built/admin/assets/chart-jVz9Nf9t.js +0 -63
  523. package/core/built/admin/assets/checkbox-DKht9xMl.js +0 -1
  524. package/core/built/admin/assets/chunk.524.cc9be98e44890cedc8c8.js +0 -25
  525. package/core/built/admin/assets/chunk.582.e6fe742b3217207b11a6.js +0 -27
  526. package/core/built/admin/assets/code-editor-view-BLQIV85-.js +0 -1
  527. package/core/built/admin/assets/code-editor-view-Cg5eJxcr.js +0 -1
  528. package/core/built/admin/assets/color-picker-DQoghc0t.js +0 -3
  529. package/core/built/admin/assets/color-picker-field-BbeK5RRs.js +0 -1
  530. package/core/built/admin/assets/combobox-weO4FBbv.js +0 -1
  531. package/core/built/admin/assets/command-DlLrCm9V.js +0 -1
  532. package/core/built/admin/assets/comments-BjTitLmP.js +0 -1
  533. package/core/built/admin/assets/confirmation-context-IyqoXVk-.js +0 -1
  534. package/core/built/admin/assets/container-DAimjOF_.js +0 -1
  535. package/core/built/admin/assets/context-menu-BtioYFrg.js +0 -1
  536. package/core/built/admin/assets/copy-field-C6ujqJlP.js +0 -1
  537. package/core/built/admin/assets/create-ghost-browse-value-source-DAZMWZNq.js +0 -1
  538. package/core/built/admin/assets/custom-integration-modal-Dm8QI_J4.js +0 -1
  539. package/core/built/admin/assets/data-list-C0CKkeWW.js +0 -1
  540. package/core/built/admin/assets/date-range-select-DrjHQdgz.js +0 -1
  541. package/core/built/admin/assets/design-and-theme-modal-CXVOzBWu.js +0 -2
  542. package/core/built/admin/assets/detail-page-QpLIL-EO.js +0 -1
  543. package/core/built/admin/assets/dirty-confirm-dialog-DWwxREGh.js +0 -1
  544. package/core/built/admin/assets/dist-Bf-HQtVL.js +0 -1
  545. package/core/built/admin/assets/dist-CUwte9v_.js +0 -1
  546. package/core/built/admin/assets/dist-FEQQAwTe.js +0 -1
  547. package/core/built/admin/assets/domain-DBrBic6q.js +0 -1
  548. package/core/built/admin/assets/dropzone-D61QVcEY.js +0 -5
  549. package/core/built/admin/assets/edit-profile-jCqJBxqw.js +0 -1
  550. package/core/built/admin/assets/editor-8Fm4UBEd.js +0 -8
  551. package/core/built/admin/assets/editor-screen-BSX_uC2B.js +0 -27
  552. package/core/built/admin/assets/email-sending-status-provider-DnNhlIvt.js +0 -1
  553. package/core/built/admin/assets/embed-signup-form-modal-DqtBs1WE.js +0 -8
  554. package/core/built/admin/assets/empty-indicator-L4Ql5Db7.js +0 -1
  555. package/core/built/admin/assets/en-Cx75dJv5.js +0 -1
  556. package/core/built/admin/assets/explore-DXhDCtKw.js +0 -1
  557. package/core/built/admin/assets/feature-image-placeholder-e2q6TcH6.js +0 -1
  558. package/core/built/admin/assets/feed-mmt4noNL.js +0 -1
  559. package/core/built/admin/assets/field-p7KnP98B.js +0 -1
  560. package/core/built/admin/assets/filter-bar-CiIKBg3D.js +0 -1
  561. package/core/built/admin/assets/filter-sources-I-dRaQxl.js +0 -1
  562. package/core/built/admin/assets/filters-1injvyxJ.js +0 -1
  563. package/core/built/admin/assets/filters-DnpO6GWX.js +0 -1
  564. package/core/built/admin/assets/gift-link-modal-C3H7YFXO.js +0 -1
  565. package/core/built/admin/assets/grid-5vplBYri.js +0 -1
  566. package/core/built/admin/assets/growth-B1og1kQp.js +0 -1
  567. package/core/built/admin/assets/growth-Bf6OHZlK.js +0 -3
  568. package/core/built/admin/assets/heading-BB21QAbb.js +0 -1
  569. package/core/built/admin/assets/history-modal-t_qEnxPx.js +0 -1
  570. package/core/built/admin/assets/html-field-COLQbV4r.js +0 -1
  571. package/core/built/admin/assets/images-4mvoc3lB.js +0 -1
  572. package/core/built/admin/assets/index-B3HZU2i8.css +0 -2
  573. package/core/built/admin/assets/index-DDd-zhNL.js +0 -4
  574. package/core/built/admin/assets/inline-B8H6NzIw.js +0 -1
  575. package/core/built/admin/assets/input-group-CLIN2ykI.js +0 -1
  576. package/core/built/admin/assets/input-surface-wSO5CRy5.js +0 -1
  577. package/core/built/admin/assets/integration-header-CKGrXNVP.js +0 -1
  578. package/core/built/admin/assets/invite-user-modal-BaN2mdhI.js +0 -1
  579. package/core/built/admin/assets/koenig-loader-B1XPPY5C.js +0 -2
  580. package/core/built/admin/assets/kpi-card-CRLPVlFW.js +0 -1
  581. package/core/built/admin/assets/kpi-card-CRPbW8NZ.js +0 -1
  582. package/core/built/admin/assets/kpi-helpers-D7zT96bp.js +0 -1
  583. package/core/built/admin/assets/kpi-tabs-DE_yh9Go.js +0 -1
  584. package/core/built/admin/assets/label-CgeBxy4o.js +0 -1
  585. package/core/built/admin/assets/label-picker-DGDp4Vwr.js +0 -1
  586. package/core/built/admin/assets/layout-DWnOMly4.js +0 -1
  587. package/core/built/admin/assets/lib-_QYaUU89.js +0 -2
  588. package/core/built/admin/assets/link-helpers-8J7ixADV.js +0 -1
  589. package/core/built/admin/assets/list-page-DLMP0PxC.js +0 -1
  590. package/core/built/admin/assets/member-activity-ClMWFyvN.js +0 -1
  591. package/core/built/admin/assets/member-activity-feed-BQY5Rwaj.js +0 -1
  592. package/core/built/admin/assets/member-custom-fields-Dd_ie1zG.js +0 -1
  593. package/core/built/admin/assets/member-detail-DjwkGVne.js +0 -1
  594. package/core/built/admin/assets/members--N9lhk1N.js +0 -4
  595. package/core/built/admin/assets/modal-page-Dz-9TP4E.js +0 -1
  596. package/core/built/admin/assets/moderation-DfYBKq9s.js +0 -1
  597. package/core/built/admin/assets/multi-select-combobox-BcBEzUW_.js +0 -1
  598. package/core/built/admin/assets/navigation-modal-Cd-scywg.js +0 -1
  599. package/core/built/admin/assets/newsletter-detail-modal-CFRY6vg_.js +0 -1
  600. package/core/built/admin/assets/newsletter-emails-4ntck1AC.js +0 -1
  601. package/core/built/admin/assets/newsletter-o6eCJtab.js +0 -1
  602. package/core/built/admin/assets/newsletters-Bmlv0edG.js +0 -1
  603. package/core/built/admin/assets/note-CWs3LF-0.js +0 -1
  604. package/core/built/admin/assets/notifications-BjSvTJqh.js +0 -1
  605. package/core/built/admin/assets/offers-container-modal-Bc9A196w.js +0 -1
  606. package/core/built/admin/assets/onboarding-route-Di1LJYEE.js +0 -1
  607. package/core/built/admin/assets/overview-DE7NHZQN.js +0 -1
  608. package/core/built/admin/assets/overview-DEF-ZY6x.js +0 -1
  609. package/core/built/admin/assets/page-header-B-G_g42C.js +0 -1
  610. package/core/built/admin/assets/pagemenu-CnkTDO71.js +0 -1
  611. package/core/built/admin/assets/pages-route-VT7r7YhG.js +0 -1
  612. package/core/built/admin/assets/portal-frame-D9V8iq5x.js +0 -1
  613. package/core/built/admin/assets/portal-modal-B5OOR9ZD.js +0 -1
  614. package/core/built/admin/assets/post-analytics-header-J0WciLx6.js +0 -1
  615. package/core/built/admin/assets/post-analytics-rGb-PgD-.js +0 -1
  616. package/core/built/admin/assets/post-share-modal-D0BbTo4W.js +0 -1
  617. package/core/built/admin/assets/posts-list-screen-CO5jAWoU.js +0 -2
  618. package/core/built/admin/assets/posts-route-DV-CXWK9.js +0 -1
  619. package/core/built/admin/assets/preferences-Tac29oG6.js +0 -5
  620. package/core/built/admin/assets/preview-chrome-C2lQgZBZ.js +0 -1
  621. package/core/built/admin/assets/preview-modal-CjRbBr8U.js +0 -1
  622. package/core/built/admin/assets/profile-yiM1f_Og.js +0 -1
  623. package/core/built/admin/assets/radio-group-CKROWsw4.js +0 -1
  624. package/core/built/admin/assets/router-provider-CU87qxXw.js +0 -1
  625. package/core/built/admin/assets/select-CKaC4ggu.js +0 -1
  626. package/core/built/admin/assets/settings-81kxUa28.js +0 -1
  627. package/core/built/admin/assets/settings-BVXFqBVK.js +0 -4
  628. package/core/built/admin/assets/settings-breadcrumbs-czGRx-iu.js +0 -1
  629. package/core/built/admin/assets/shade-app-CcS3f8MZ-D5BnfHck.js +0 -3
  630. package/core/built/admin/assets/share-modal-BL-16PiB.js +0 -1
  631. package/core/built/admin/assets/skeleton-DwCva0-b.js +0 -1
  632. package/core/built/admin/assets/sort-button-Dquc83jY.js +0 -1
  633. package/core/built/admin/assets/stack-BURz77VS.js +0 -1
  634. package/core/built/admin/assets/stats-view-NDja_g96.js +0 -1
  635. package/core/built/admin/assets/stripe-button-C3cfCAou.js +0 -1
  636. package/core/built/admin/assets/stripe-connect-modal-FyE3Ey2c.js +0 -1
  637. package/core/built/admin/assets/switch-zfUUy9Jh.js +0 -1
  638. package/core/built/admin/assets/table-dE4GTIRT.js +0 -1
  639. package/core/built/admin/assets/tabs-B3XK93sR.js +0 -1
  640. package/core/built/admin/assets/tag-detail-D4P-tZdM.js +0 -2
  641. package/core/built/admin/assets/tag-picker-BNSZ6Qxw.js +0 -1
  642. package/core/built/admin/assets/tags-BsZcaJ6h.js +0 -1
  643. package/core/built/admin/assets/text-DC-ot6u2.js +0 -1
  644. package/core/built/admin/assets/textarea-B4FffFmE.js +0 -1
  645. package/core/built/admin/assets/tier-detail-modal-a77Z2VMH.js +0 -1
  646. package/core/built/admin/assets/toggle-group-D2p4okmJ.js +0 -1
  647. package/core/built/admin/assets/transistor-modal-C8RMkhOD.js +0 -1
  648. package/core/built/admin/assets/trend-badge-B9v7kMF8.js +0 -1
  649. package/core/built/admin/assets/types-uYQ1HgqL.js +0 -1
  650. package/core/built/admin/assets/use-analytics-data-ChHqiZXv.js +0 -1
  651. package/core/built/admin/assets/use-availability-hPdeG76y.js +0 -1
  652. package/core/built/admin/assets/use-filter-params-BSFigDIn.js +0 -1
  653. package/core/built/admin/assets/use-growth-stats-CqqdnESN.js +0 -1
  654. package/core/built/admin/assets/use-limiter-DBGjYqu7.js +0 -19
  655. package/core/built/admin/assets/use-reply-chain-data-DmD2yX5D.js +0 -1
  656. package/core/built/admin/assets/use-sending-eta-CQeG6OP_.js +0 -1
  657. package/core/built/admin/assets/use-setting-group-Dza6bQ7W.js +0 -1
  658. package/core/built/admin/assets/use-settings-navigation-BIjI5mL3.js +0 -1
  659. package/core/built/admin/assets/use-simple-pagination-DwVxe-Lo.js +0 -1
  660. package/core/built/admin/assets/use-staff-users-i_Mq9OGR.js +0 -1
  661. package/core/built/admin/assets/use-tinybird-query-DXoF74NM.js +0 -1
  662. package/core/built/admin/assets/user-detail-modal-BU4QCHE7.js +0 -1
  663. package/core/built/admin/assets/virtual-list-DScLNuU8.js +0 -1
  664. package/core/built/admin/assets/web-BZS-t4H0.js +0 -1
  665. package/core/built/admin/assets/web-DxAUxnK3.js +0 -2
  666. package/core/frontend/services/sitemap/utils.js +0 -17
  667. package/core/server/services/automations/get-scheduler-idempotency-key.js +0 -13
  668. package/core/server/web/api/testmode/index.js +0 -1
  669. package/core/server/web/api/testmode/jobs/cpu-hog.js +0 -15
  670. package/core/server/web/api/testmode/jobs/graceful-job.js +0 -50
  671. package/core/server/web/api/testmode/jobs/say-hello.js +0 -17
  672. package/core/server/web/api/testmode/routes.js +0 -120
@@ -717,7 +717,7 @@ module.exports = class MemberRepository {
717
717
  }
718
718
 
719
719
  // Fetch the member
720
- let initialMember = await this._Member.findOne(
720
+ const initialMember = await this._Member.findOne(
721
721
  {
722
722
  id: options.id,
723
723
  },
@@ -1335,7 +1335,7 @@ module.exports = class MemberRepository {
1335
1335
  logging.error(e);
1336
1336
  }
1337
1337
 
1338
- let stripeCouponId = stripeSubscriptionData.discount?.coupon?.id;
1338
+ const stripeCouponId = stripeSubscriptionData.discount?.coupon?.id;
1339
1339
 
1340
1340
  // For trial offers, offer id is passed from metadata as there is no stripe coupon
1341
1341
  let offerId = data.offerId || null;
@@ -1432,7 +1432,11 @@ module.exports = class MemberRepository {
1432
1432
 
1433
1433
  return 'inactive';
1434
1434
  };
1435
- let eventData = {};
1435
+ const eventData = {};
1436
+ const isIncomplete = ['incomplete', 'incomplete_expired'].includes(
1437
+ stripeSubscriptionData.status,
1438
+ );
1439
+ let subscriptionToRecord = null;
1436
1440
 
1437
1441
  // A cancellation (or reactivation) changes no `members` column, so the member
1438
1442
  // model event would be suppressed by `wasChanged()`. Remember the pre-update
@@ -1482,10 +1486,34 @@ module.exports = class MemberRepository {
1482
1486
  },
1483
1487
  );
1484
1488
 
1489
+ if (
1490
+ stripeCustomerSubscriptionModel.get('status') === 'incomplete' &&
1491
+ this.isActiveSubscriptionStatus(stripeSubscriptionData.status)
1492
+ ) {
1493
+ // Older Ghost versions recorded incomplete subscriptions as conversions. Avoid
1494
+ // recording them twice when a checkout already in progress completes.
1495
+ const createdEvent = await this._MemberPaidSubscriptionEvent.findOne(
1496
+ {
1497
+ member_id: memberModel.id,
1498
+ subscription_id: stripeCustomerSubscriptionModel.id,
1499
+ type: 'created',
1500
+ },
1501
+ options,
1502
+ );
1503
+ if (!createdEvent) {
1504
+ subscriptionToRecord = updatedStripeCustomerSubscriptionModel;
1505
+ }
1506
+ }
1507
+
1485
1508
  // CASE: Record offer redemption when offer_id changes to a new non-null value
1486
1509
  // This covers: null→new (free member upgrade), old→new (retention offer replacing expired signup offer)
1487
1510
  // The OfferRedemptionEvent handler has a dedup check for repeated webhook deliveries
1488
- if (previousOfferId !== subscriptionData.offer_id && subscriptionData.offer_id) {
1511
+ if (
1512
+ !isIncomplete &&
1513
+ !subscriptionToRecord &&
1514
+ previousOfferId !== subscriptionData.offer_id &&
1515
+ subscriptionData.offer_id
1516
+ ) {
1489
1517
  const redemptionTimestamp =
1490
1518
  subscriptionData.discount_start ||
1491
1519
  updatedStripeCustomerSubscriptionModel.get('created_at');
@@ -1508,14 +1536,16 @@ module.exports = class MemberRepository {
1508
1536
  }
1509
1537
 
1510
1538
  if (
1511
- stripeCustomerSubscriptionModel.get('mrr') !==
1539
+ !isIncomplete &&
1540
+ !subscriptionToRecord &&
1541
+ (stripeCustomerSubscriptionModel.get('mrr') !==
1512
1542
  updatedStripeCustomerSubscriptionModel.get('mrr') ||
1513
- stripeCustomerSubscriptionModel.get('plan_id') !==
1514
- updatedStripeCustomerSubscriptionModel.get('plan_id') ||
1515
- stripeCustomerSubscriptionModel.get('status') !==
1516
- updatedStripeCustomerSubscriptionModel.get('status') ||
1517
- stripeCustomerSubscriptionModel.get('cancel_at_period_end') !==
1518
- updatedStripeCustomerSubscriptionModel.get('cancel_at_period_end')
1543
+ stripeCustomerSubscriptionModel.get('plan_id') !==
1544
+ updatedStripeCustomerSubscriptionModel.get('plan_id') ||
1545
+ stripeCustomerSubscriptionModel.get('status') !==
1546
+ updatedStripeCustomerSubscriptionModel.get('status') ||
1547
+ stripeCustomerSubscriptionModel.get('cancel_at_period_end') !==
1548
+ updatedStripeCustomerSubscriptionModel.get('cancel_at_period_end'))
1519
1549
  ) {
1520
1550
  const originalMrrDelta = stripeCustomerSubscriptionModel.get('mrr');
1521
1551
  const updatedMrrDelta = updatedStripeCustomerSubscriptionModel.get('mrr');
@@ -1607,16 +1637,24 @@ module.exports = class MemberRepository {
1607
1637
  subscriptionData,
1608
1638
  options,
1609
1639
  );
1640
+ if (!isIncomplete) {
1641
+ subscriptionToRecord = newStripeCustomerSubscriptionModel;
1642
+ }
1643
+ }
1644
+
1645
+ // A checkout attempt is not a paid conversion. Record the start only once
1646
+ // the subscription leaves incomplete, retaining attribution from Stripe metadata.
1647
+ if (subscriptionToRecord) {
1610
1648
  await this._MemberPaidSubscriptionEvent.add(
1611
1649
  {
1612
1650
  member_id: memberModel.id,
1613
- subscription_id: newStripeCustomerSubscriptionModel.id,
1651
+ subscription_id: subscriptionToRecord.id,
1614
1652
  type: 'created',
1615
1653
  source: 'stripe',
1616
1654
  from_plan: null,
1617
1655
  to_plan: subscriptionPriceData.id,
1618
1656
  currency: subscriptionPriceData.currency,
1619
- mrr_delta: newStripeCustomerSubscriptionModel.get('mrr'),
1657
+ mrr_delta: subscriptionToRecord.get('mrr'),
1620
1658
  ...eventData,
1621
1659
  },
1622
1660
  options,
@@ -1653,7 +1691,7 @@ module.exports = class MemberRepository {
1653
1691
  source,
1654
1692
  tierId: ghostProduct?.get('id'),
1655
1693
  memberId: memberModel.id,
1656
- subscriptionId: newStripeCustomerSubscriptionModel.get('id'),
1694
+ subscriptionId: subscriptionToRecord.get('id'),
1657
1695
  offerId: offerId,
1658
1696
  attribution: attribution,
1659
1697
  batchId: options.batch_id,
@@ -1665,17 +1703,17 @@ module.exports = class MemberRepository {
1665
1703
  const offerRedemptionEvent = OfferRedemptionEvent.create({
1666
1704
  memberId: memberModel.id,
1667
1705
  offerId: offerId,
1668
- subscriptionId: newStripeCustomerSubscriptionModel.get('id'),
1706
+ subscriptionId: subscriptionToRecord.get('id'),
1669
1707
  });
1670
1708
  this.dispatchEvent(offerRedemptionEvent, options);
1671
1709
  }
1672
1710
 
1673
- if (getStatus(newStripeCustomerSubscriptionModel) === 'active') {
1711
+ if (getStatus(subscriptionToRecord) === 'active') {
1674
1712
  const subscriptionActivatedEvent = SubscriptionActivatedEvent.create({
1675
1713
  source,
1676
1714
  tierId: ghostProduct?.get('id'),
1677
1715
  memberId: memberModel.id,
1678
- subscriptionId: newStripeCustomerSubscriptionModel.get('id'),
1716
+ subscriptionId: subscriptionToRecord.get('id'),
1679
1717
  offerId: offerId,
1680
1718
  attribution: attribution,
1681
1719
  batchId: options.batch_id,
@@ -418,7 +418,7 @@ class ProductRepository {
418
418
 
419
419
  const existingProduct = await this._Product.findOne({ id: productId }, options);
420
420
 
421
- let productData = {
421
+ const productData = {
422
422
  name: data.name,
423
423
  visibility: data.visibility,
424
424
  description: data.description,
@@ -1,5 +1,5 @@
1
1
  const errors = require('@tryghost/errors');
2
- const { ADMIN } = require('../../../members-metafields');
2
+ const { ADMIN, adminWriteOrigin } = require('../../../members-metafields');
3
3
  const logging = require('@tryghost/logging');
4
4
  const tpl = require('@tryghost/tpl');
5
5
  const moment = require('moment');
@@ -641,16 +641,13 @@ module.exports = class MemberBREADService {
641
641
  // The only route to this branch is the authenticated Admin API, so an anonymous
642
642
  // request is a mistake somewhere upstream rather than a writer to invent a name
643
643
  // for. Refusing keeps every stored writer resolvable.
644
- const context = options.context || {};
645
- if (!context.integration && !context.user) {
644
+ const origin = adminWriteOrigin(options.context);
645
+ if (!origin) {
646
646
  throw new errors.IncorrectUsageError({
647
647
  message: tpl(messages.metafieldsWithoutWriter),
648
648
  });
649
649
  }
650
- const writtenBy = context.integration
651
- ? { type: 'integration', id: context.integration.id }
652
- : { type: 'user', id: context.user };
653
- await this.metafieldValues.applyWrite(model.id, plannedMetafields, { writtenBy });
650
+ await this.metafieldValues.applyWrite(model.id, plannedMetafields, origin);
654
651
 
655
652
  // Metafields aren't a member column or relation, so an edit touching
656
653
  // only them leaves `model._changed` empty and the save fires nothing.
@@ -13,7 +13,7 @@ const settingsCache = require('../../../shared/settings-cache');
13
13
  const config = require('../../../shared/config');
14
14
  const models = require('../../models');
15
15
  const { GhostMailer } = require('../mail');
16
- const jobsService = require('../jobs');
16
+ const jobsService = require('../jobs-service');
17
17
  const tiersService = require('../tiers');
18
18
  const giftService = require('../gifts');
19
19
  const VerificationTrigger = require('../verification-trigger');
@@ -83,7 +83,9 @@ const buildImporterDeps = ({ stripeAPIService }) => {
83
83
  },
84
84
  getGiftService: () => giftService.service,
85
85
  sendEmail: ghostMailer.send.bind(ghostMailer),
86
- addJob: jobsService.addJob.bind(jobsService),
86
+ // Resolved per dispatch rather than here: init() also runs where nothing has
87
+ // initialised the jobs service.
88
+ dispatchJob: (job) => jobsService.getInstance().dispatch(job),
87
89
  knex: db.knex,
88
90
  urlFor: urlUtils.urlFor.bind(urlUtils),
89
91
  stripeAPIService,
@@ -233,6 +235,8 @@ module.exports = {
233
235
  membersCSVImporter.importCSV(request, verificationTrigger);
234
236
  module.exports.importInline = (request) =>
235
237
  membersCSVImporter.importInline(request, verificationTrigger);
238
+ // The members-import job handler, registered by boot with the jobs service.
239
+ module.exports.handleImportJob = (job) => membersCSVImporter.handle(job, verificationTrigger);
236
240
 
237
241
  // Constructed here rather than required statically: the exporter needs the
238
242
  // metafields services, which boot builds before this one.
@@ -265,6 +269,7 @@ module.exports = {
265
269
 
266
270
  importCSV: null,
267
271
  importInline: null,
272
+ handleImportJob: null,
268
273
 
269
274
  stats: membersStats,
270
275
  export: null,
@@ -135,11 +135,11 @@ class MembersStats {
135
135
  currentRangeDate = moment.tz(siteTimezone).subtract(days - 1, 'days');
136
136
  }
137
137
 
138
- let endDate = moment.tz(siteTimezone).add(1, 'hour');
138
+ const endDate = moment.tz(siteTimezone).add(1, 'hour');
139
139
  const output = {};
140
140
 
141
141
  while (currentRangeDate.isBefore(endDate)) {
142
- let dateStr = currentRangeDate.format('YYYY-MM-DD');
142
+ const dateStr = currentRangeDate.format('YYYY-MM-DD');
143
143
  runningTotal += resultObject[dateStr] || 0;
144
144
  output[dateStr] = runningTotal;
145
145
 
@@ -23,7 +23,7 @@ class EventStorage {
23
23
  */
24
24
  subscribe(domainEvents) {
25
25
  domainEvents.subscribe(MemberCreatedEvent, async (event) => {
26
- let attribution = event.data.attribution;
26
+ const attribution = event.data.attribution;
27
27
 
28
28
  await this.models.MemberCreatedEvent.add({
29
29
  member_id: event.data.memberId,
@@ -45,7 +45,7 @@ class EventStorage {
45
45
  });
46
46
 
47
47
  domainEvents.subscribe(SubscriptionCreatedEvent, async (event) => {
48
- let attribution = event.data.attribution;
48
+ const attribution = event.data.attribution;
49
49
 
50
50
  await this.models.SubscriptionCreatedEvent.add({
51
51
  member_id: event.data.memberId,
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.actingContext = actingContext;
7
+ exports.adminWriteOrigin = adminWriteOrigin;
7
8
  exports.recordMetafieldAction = recordMetafieldAction;
8
9
  const logging_1 = __importDefault(require("@tryghost/logging"));
9
10
  /**
@@ -16,14 +17,34 @@ const logging_1 = __importDefault(require("@tryghost/logging"));
16
17
  */
17
18
  function actingContext(context) {
18
19
  const frame = (context ?? {});
20
+ const viaApiKey = Boolean(frame.api_key);
19
21
  if (frame.integration) {
20
- return { actor: { id: frame.integration.id, type: 'integration' } };
22
+ return { actor: { id: frame.integration.id, type: 'integration', viaApiKey } };
21
23
  }
22
24
  if (frame.user) {
23
- return { actor: { id: frame.user, type: 'user' } };
25
+ return { actor: { id: frame.user, type: 'user', viaApiKey } };
24
26
  }
25
27
  return { actor: null };
26
28
  }
29
+ /**
30
+ * Who made a write through the Admin API and where, read off the same context as
31
+ * `actingContext`, so the values a request writes and the history it records name the
32
+ * same writer. Null when nobody is acting, which no Admin API write should be.
33
+ */
34
+ function adminWriteOrigin(context) {
35
+ const { actor } = actingContext(context);
36
+ if (!actor) {
37
+ return null;
38
+ }
39
+ if (actor.type === 'integration') {
40
+ return { writtenBy: { type: 'integration', id: actor.id }, source: 'admin_api' };
41
+ }
42
+ // A staff token authenticates as its user but is a call to the API, not a visit to Admin.
43
+ return {
44
+ writtenBy: { type: 'user', id: actor.id },
45
+ source: actor.viaApiKey ? 'admin_api' : 'admin',
46
+ };
47
+ }
27
48
  // Field-definition changes map to activity-feed events. A field's timeline reads
28
49
  // added -> edited -> archived -> restored, and a permanent delete (only from the
29
50
  // archived state) ends it with deleted.
@@ -57,8 +57,8 @@ class MetafieldBindingsService {
57
57
  */
58
58
  async writeCollected(memberId, productId, collected) {
59
59
  return this.writeThrough(memberId, productId, collected, (binding) => ({
60
- type: 'binding',
61
- id: binding.bindingId,
60
+ writtenBy: { type: 'binding', id: binding.bindingId },
61
+ source: 'checkout',
62
62
  }));
63
63
  }
64
64
  /**
@@ -70,8 +70,8 @@ class MetafieldBindingsService {
70
70
  */
71
71
  async writeSuppliedByMember(memberId, productId, supplied) {
72
72
  return this.writeThrough(memberId, productId, supplied, () => ({
73
- type: 'member',
74
- id: memberId,
73
+ writtenBy: { type: 'member', id: memberId },
74
+ source: 'checkout',
75
75
  }));
76
76
  }
77
77
  /**
@@ -112,7 +112,7 @@ class MetafieldBindingsService {
112
112
  // looking. A port exists because a publisher pointed it at a field, and that
113
113
  // decision is what admits the value, whoever supplied it.
114
114
  const planned = await this.values.planWrite({ [`${CUSTOM_NAMESPACE}.${into.key}`]: value }, access_1.INTERNAL);
115
- await this.values.applyWrite(memberId, planned, { writtenBy: attribute(into) });
115
+ await this.values.applyWrite(memberId, planned, attribute(into));
116
116
  }
117
117
  async resolve(productId, port) {
118
118
  const row = await this.knex(BINDINGS_TABLE)
@@ -15,11 +15,6 @@ const schema_1 = require("./schema");
15
15
  const access_1 = require("./access");
16
16
  const queries_1 = require("./queries");
17
17
  const key_1 = require("./key");
18
- // The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to
19
- // our raw-knex query: nql parses the `filter` string to a Mongo query, mongo-knex
20
- // turns that into parametrised WHERE clauses. Neither needs a Bookshelf model.
21
- const nql = require('@tryghost/nql');
22
- const knexify = require('@tryghost/mongo-knex');
23
18
  const TABLE = 'members_metafields';
24
19
  // Column limits come from the canonical schema — the same source the migration and
25
20
  // Bookshelf models read — so the service can never drift from the database. The
@@ -125,7 +120,7 @@ class MetafieldDefinitionsService {
125
120
  return this.list((0, queries_1.definitions)(this.knex, {
126
121
  audience,
127
122
  status: filterReferencesStatus(parsed) ? queries_1.ANY_STATUS : queries_1.ACTIVE_ONLY,
128
- filter: (query) => knexify(query, parsed, { tableName: TABLE }),
123
+ filter: (query) => (0, queries_1.knexify)(query, parsed, { tableName: TABLE }),
129
124
  }));
130
125
  }
131
126
  return this.list((0, queries_1.definitions)(this.knex, { audience, status: queries_1.ACTIVE_ONLY }));
@@ -628,7 +623,7 @@ function isUniqueConstraintViolation(error) {
628
623
  function parseFilter(filter) {
629
624
  let mongoQuery;
630
625
  try {
631
- mongoQuery = nql(filter).toJSON();
626
+ mongoQuery = (0, queries_1.nql)(filter).toJSON();
632
627
  }
633
628
  catch (err) {
634
629
  throw new errors_1.default.BadRequestError({
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.bindings = exports.values = exports.definitions = exports.canWrite = exports.MEMBER_ACCESS = exports.MEMBERS = exports.INTERNAL = exports.ADMIN = exports.actingContext = void 0;
3
+ exports.bindings = exports.values = exports.definitions = exports.canWrite = exports.MEMBER_ACCESS = exports.MEMBERS = exports.INTERNAL = exports.ADMIN = exports.adminWriteOrigin = exports.actingContext = void 0;
4
4
  exports.init = init;
5
5
  const definitions_service_1 = require("./definitions-service");
6
6
  const values_service_1 = require("./values-service");
@@ -9,6 +9,7 @@ const actions_1 = require("./actions");
9
9
  const config_1 = require("./config");
10
10
  var actions_2 = require("./actions");
11
11
  Object.defineProperty(exports, "actingContext", { enumerable: true, get: function () { return actions_2.actingContext; } });
12
+ Object.defineProperty(exports, "adminWriteOrigin", { enumerable: true, get: function () { return actions_2.adminWriteOrigin; } });
12
13
  // Which door a request came through, which is what decides how much of a member's
13
14
  // answers it may see or change. Required wherever that is asked, so a new caller
14
15
  // has to name itself rather than inherit an answer by default.
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ANY_STATUS = exports.ACTIVE_ONLY = void 0;
3
+ exports.ANY_STATUS = exports.ACTIVE_ONLY = exports.knexify = exports.nql = void 0;
4
4
  exports.readableBy = readableBy;
5
5
  exports.definitions = definitions;
6
6
  exports.inFieldOrder = inFieldOrder;
7
7
  const access_1 = require("./access");
8
8
  const schema_1 = require("./schema");
9
9
  const FIELDS_TABLE = 'members_metafields';
10
+ // The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to our
11
+ // raw-knex queries: nql parses a `filter` string to a Mongo query, mongo-knex turns that
12
+ // into parametrised WHERE clauses. Neither needs a Bookshelf model. Typed once here, since
13
+ // neither package ships types.
14
+ exports.nql = require('@tryghost/nql');
15
+ exports.knexify = require('@tryghost/mongo-knex');
10
16
  // Nothing in the database keeps an archived or hidden field out of a read: no
11
17
  // constraint stops a value row referencing one. Both filters are therefore applied in
12
18
  // code, and a query that forgets either is a silent bug.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DbBoundField = exports.DbMetafieldBinding = exports.DbMetafieldLeaf = exports.DbMetafieldValue = exports.WrittenBy = exports.DbMetafield = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
3
+ exports.DbMetafieldChangeEventWithMember = exports.DbChangeEventMember = exports.DbMetafieldChangeEvent = exports.StoredFieldList = exports.MetafieldChangeEventFields = exports.DbBoundField = exports.DbMetafieldBinding = exports.DbMetafieldLeaf = exports.DbMetafieldValue = exports.WrittenBy = exports.DbMetafield = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const metafield_types_1 = require("@tryghost/metafield-types");
6
6
  const date_1 = require("../../lib/db-types/date");
@@ -82,3 +82,51 @@ exports.DbBoundField = zod_1.z.object({
82
82
  key: zod_1.z.string(),
83
83
  type: metafield_types_1.FieldTypeSchema,
84
84
  });
85
+ /** The fields an entry names. */
86
+ exports.MetafieldChangeEventFields = zod_1.z.array(metafield_types_1.MetafieldChangeEventFieldSchema);
87
+ /**
88
+ * The field list as the table holds it, JSON text, against the list itself. A codec rather
89
+ * than a parse on the way out, so the write stores what the read accepts. Zod validates
90
+ * JSON-compatible values (`z.json()`) but has no built-in for parsing JSON text, so decoding
91
+ * the text is this codec's job.
92
+ */
93
+ exports.StoredFieldList = zod_1.z.codec(zod_1.z.string(), exports.MetafieldChangeEventFields, {
94
+ decode: (text, ctx) => {
95
+ try {
96
+ return JSON.parse(text);
97
+ }
98
+ catch {
99
+ ctx.issues.push({
100
+ code: 'custom',
101
+ message: 'The stored field list is not JSON.',
102
+ input: text,
103
+ });
104
+ return zod_1.z.NEVER;
105
+ }
106
+ },
107
+ encode: (fields) => JSON.stringify(fields),
108
+ });
109
+ /** An entry as the table holds it. Writer and source are plain strings, as on the values table. */
110
+ exports.DbMetafieldChangeEvent = zod_1.z.object({
111
+ id: zod_1.z.string(),
112
+ member_id: zod_1.z.string(),
113
+ written_by_type: zod_1.z.string(),
114
+ written_by_id: zod_1.z.string().nullable(),
115
+ source: zod_1.z.string(),
116
+ metafields: exports.StoredFieldList,
117
+ created_at: date_1.DbDate,
118
+ });
119
+ /** The member columns an entry is shown with: only what a feed row needs. */
120
+ exports.DbChangeEventMember = zod_1.z.object({
121
+ id: zod_1.z.string(),
122
+ uuid: zod_1.z.string(),
123
+ name: zod_1.z.string().nullable(),
124
+ email: zod_1.z.string(),
125
+ });
126
+ /**
127
+ * An entry and its member read back together. Parses and nothing else: what to do with an
128
+ * entry that doesn't parse is the reading service's decision.
129
+ */
130
+ exports.DbMetafieldChangeEventWithMember = zod_1.z
131
+ .object({ event: exports.DbMetafieldChangeEvent, member: exports.DbChangeEventMember })
132
+ .transform(({ event, member }) => ({ ...event, member }));
@@ -11,11 +11,14 @@ const zod_1 = require("zod");
11
11
  const metafield_types_1 = require("@tryghost/metafield-types");
12
12
  const identity_1 = require("@tryghost/metafield-types/identity");
13
13
  const schema_1 = require("./schema");
14
+ const date_1 = require("../../lib/db-types/date");
14
15
  const queries_1 = require("./queries");
15
16
  const access_1 = require("./access");
16
17
  const storage_1 = require("./storage");
17
18
  const FIELDS_TABLE = 'members_metafields';
18
19
  const VALUES_TABLE = 'members_metafield_values';
20
+ const CHANGE_EVENTS_TABLE = 'members_metafield_change_events';
21
+ const MEMBERS_TABLE = 'members';
19
22
  /**
20
23
  * From the canonical schema, the same source definitions-service reads, so no key a site
21
24
  * could hold is refused and this cannot drift from the `members_metafields.key` column.
@@ -34,6 +37,24 @@ const UPSERT_CHUNK = 400;
34
37
  const MAX_IDENTITY_LENGTH = MAX_KEY_LENGTH * 2 + 1;
35
38
  const ValuesInput = zod_1.z.record(zod_1.z.string().max(MAX_IDENTITY_LENGTH), zod_1.z.unknown());
36
39
  const wireProperty = (identity) => [identity_1.QUALIFIER, identity].join('.');
40
+ /**
41
+ * Every refusal in one error.
42
+ *
43
+ * The API renders a single error, so the first still fills `message` and `property` and
44
+ * a client that reads only those sees exactly what it saw before. The whole set rides in
45
+ * `errorDetails`, which the renderer already passes through as `details`, so a client
46
+ * that wants to mark every refused input can have them all from one attempt.
47
+ */
48
+ function refusalError(refusals) {
49
+ const [first, ...rest] = refusals;
50
+ return new errors_1.default.ValidationError({
51
+ message: first.message,
52
+ property: first.property,
53
+ // Omitted when there is nothing more to say, so the common single refusal keeps the
54
+ // shape it has always had rather than growing a one-element list.
55
+ ...(rest.length > 0 ? { errorDetails: refusals } : {}),
56
+ });
57
+ }
37
58
  /**
38
59
  * What a member holds for each defined field. Separate from the definitions service
39
60
  * because a value belongs to the member and a definition belongs to the site's settings,
@@ -166,21 +187,27 @@ class MetafieldValuesService {
166
187
  }
167
188
  const byIdentity = await this.allowedFieldsByIdentity(identities, audience);
168
189
  const writes = [];
190
+ // Gathered rather than thrown as they are found. A write names several values, and
191
+ // a composite is several again, so refusing at the first leaves someone correcting
192
+ // one part per round trip to learn what was wrong with the rest.
193
+ const refusals = [];
169
194
  for (const [identity, raw] of Object.entries(values)) {
170
195
  const field = byIdentity.get(identity);
171
196
  if (!field) {
172
- throw new errors_1.default.ValidationError({
197
+ refusals.push({
173
198
  message: `Unknown custom field: ${identity}`,
174
199
  property: wireProperty(identity),
175
200
  });
201
+ continue;
176
202
  }
177
203
  // A different refusal from the unknown-field one above: this audience can
178
204
  // already see the field, so naming it discloses nothing.
179
205
  if (!(0, access_1.canWrite)(audience, field)) {
180
- throw new errors_1.default.ValidationError({
206
+ refusals.push({
181
207
  message: `Cannot set custom field: ${identity}`,
182
208
  property: wireProperty(identity),
183
209
  });
210
+ continue;
184
211
  }
185
212
  // `null` clears any field, and `''` clears one with no parts. For a value
186
213
  // with parts `''` names nothing, so it is left to fail validation rather than
@@ -195,14 +222,21 @@ class MetafieldValuesService {
195
222
  // Which field failed rides in `property`.
196
223
  const value = metafield_types_1.FIELD_TYPES[field.type].value.safeParse(raw);
197
224
  if (!value.success) {
198
- const issue = value.error.issues[0];
199
- throw new errors_1.default.ValidationError({
200
- message: issue.message,
201
- property: [wireProperty(identity), ...issue.path].join('.'),
202
- });
225
+ // Every issue, not the first: a value with parts fails once per part, and each
226
+ // names the part it belongs to, which is what lets a client mark them all.
227
+ for (const issue of value.error.issues) {
228
+ refusals.push({
229
+ message: issue.message,
230
+ property: [wireProperty(identity), ...issue.path].join('.'),
231
+ });
232
+ }
233
+ continue;
203
234
  }
204
235
  writes.push({ field, value: value.data });
205
236
  }
237
+ if (refusals.length > 0) {
238
+ throw refusalError(refusals);
239
+ }
206
240
  return writes;
207
241
  }
208
242
  /**
@@ -213,13 +247,19 @@ class MetafieldValuesService {
213
247
  * them, and saying nothing about a path leaves it alone. There is no whole-value
214
248
  * replace, so a caller that does not know about a field cannot erase it.
215
249
  *
216
- * `writtenBy` is required and has no default: every writer has to name itself, so a new
217
- * one cannot quietly inherit the identity of whichever was written first.
250
+ * `writtenBy` and `source` are required and have no default: every writer has to name
251
+ * itself and where it wrote, so a new one cannot quietly inherit the identity of
252
+ * whichever was written first.
218
253
  *
219
254
  * Always transactional. Given an executor it joins that transaction, so the importer's
220
255
  * failed value write takes its member with it; given none it opens its own.
256
+ *
257
+ * Every write also puts an entry on the member's activity feed, naming the fields it
258
+ * touched and who wrote them where, in the same transaction, so the feed cannot name a
259
+ * change that was not stored or miss one that was. A value has more than one author,
260
+ * and the feed is where a publisher finds out which of them changed it.
221
261
  */
222
- async applyWrite(memberId, writes, { writtenBy, executor = this.knex }) {
262
+ async applyWrite(memberId, writes, { writtenBy, source, executor = this.knex }) {
223
263
  if (writes.length === 0) {
224
264
  return;
225
265
  }
@@ -282,6 +322,22 @@ class MetafieldValuesService {
282
322
  // it currently holds rather than who wrote its first value.
283
323
  .merge(['value_text', 'written_by_type', 'written_by_id', 'updated_at']);
284
324
  }
325
+ const fields = writes.map(({ field }) => ({
326
+ namespace: field.namespace,
327
+ key: field.key,
328
+ name: field.name,
329
+ }));
330
+ await trx(CHANGE_EVENTS_TABLE).insert({
331
+ id: new bson_objectid_1.default().toHexString(),
332
+ member_id: memberId,
333
+ written_by_type: writtenBy.type,
334
+ written_by_id: writtenBy.id,
335
+ source,
336
+ metafields: schema_1.StoredFieldList.encode(fields),
337
+ // A string rather than the Date the value rows take: SQLite stores a bound Date
338
+ // as a number, which sorts before every string the feed pages through time with.
339
+ created_at: (0, date_1.toDatabaseDate)(now),
340
+ });
285
341
  };
286
342
  // knex's marker for a transactor: join it rather than nesting a savepoint under it.
287
343
  if (executor.isTransaction) {
@@ -291,5 +347,51 @@ class MetafieldValuesService {
291
347
  await executor.transaction(apply);
292
348
  }
293
349
  }
350
+ /**
351
+ * Entries from members' activity feeds, newest first, each with its member, as one page
352
+ * of the members events endpoint reads them.
353
+ *
354
+ * `filter` is a parsed NQL filter over this table's own columns: mapping the feed's
355
+ * names onto them is the feed's business, and applying them is this table's.
356
+ */
357
+ async browseChangeEvents({ limit, filter, }) {
358
+ const filtered = () => {
359
+ const query = this.knex(CHANGE_EVENTS_TABLE);
360
+ return filter ? (0, queries_1.knexify)(query, filter, { tableName: CHANGE_EVENTS_TABLE }) : query;
361
+ };
362
+ // Joined rather than looked up afterwards: an entry cannot outlive its member, whose
363
+ // delete cascades to it, so every entry has one to join.
364
+ const newestFirst = filtered()
365
+ .join(MEMBERS_TABLE, `${MEMBERS_TABLE}.id`, `${CHANGE_EVENTS_TABLE}.member_id`)
366
+ .orderBy([
367
+ { column: `${CHANGE_EVENTS_TABLE}.created_at`, order: 'desc' },
368
+ { column: `${CHANGE_EVENTS_TABLE}.id`, order: 'desc' },
369
+ ]);
370
+ const page = limit === undefined ? newestFirst : newestFirst.limit(limit);
371
+ const [rows, counted] = await Promise.all([
372
+ page.select(`${CHANGE_EVENTS_TABLE}.id`, `${CHANGE_EVENTS_TABLE}.member_id`, `${CHANGE_EVENTS_TABLE}.written_by_type`, `${CHANGE_EVENTS_TABLE}.written_by_id`, `${CHANGE_EVENTS_TABLE}.source`, `${CHANGE_EVENTS_TABLE}.metafields`, `${CHANGE_EVENTS_TABLE}.created_at`, { member_uuid: `${MEMBERS_TABLE}.uuid` }, { member_name: `${MEMBERS_TABLE}.name` }, { member_email: `${MEMBERS_TABLE}.email` }),
373
+ filtered().count({ total: '*' }).first(),
374
+ ]);
375
+ // What to do with an entry whose field list can't be read is decided here, not in the
376
+ // parser: it is still shown, naming no fields. The feed pages through time a page per
377
+ // event type, so dropping an entry would push a valid one off the end of its page for
378
+ // good. Anything else a row can't be read for, the table's constraints rule out, so it
379
+ // throws rather than being hidden.
380
+ const events = rows.map(({ member_uuid: uuid, member_name: name, member_email: email, ...event }) => {
381
+ const fields = schema_1.StoredFieldList.safeParse(event.metafields);
382
+ if (!fields.success) {
383
+ logging_1.default.warn({
384
+ event: { name: 'members.metafields.change_event_unreadable' },
385
+ err: fields.error,
386
+ changeEventId: event.id,
387
+ }, 'Reading an unreadable metafield change entry as naming no fields');
388
+ }
389
+ return schema_1.DbMetafieldChangeEventWithMember.parse({
390
+ event: fields.success ? event : { ...event, metafields: schema_1.StoredFieldList.encode([]) },
391
+ member: { id: event.member_id, uuid, name, email },
392
+ });
393
+ });
394
+ return { events, total: Number(counted?.total ?? 0) };
395
+ }
294
396
  }
295
397
  exports.MetafieldValuesService = MetafieldValuesService;