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
@@ -2,6 +2,7 @@ const logging = require('@tryghost/logging');
2
2
  const ObjectID = require('bson-objectid').default;
3
3
  const errors = require('@tryghost/errors');
4
4
  const tpl = require('@tryghost/tpl');
5
+ const SendEmailJob = require('./jobs/send-email-job').default;
5
6
  const messages = {
6
7
  emailErrorPartialFailure:
7
8
  'An error occurred, and your newsletter was only partially sent. Please retry sending the remaining emails.',
@@ -17,7 +18,7 @@ const SHUTDOWN_CODE = 'BULK_EMAIL_SHUTDOWN_IN_PROGRESS';
17
18
  * @typedef {import('./email-renderer')} EmailRenderer
18
19
  * @typedef {import('./domain-warming-service').DomainWarmingService} DomainWarmingService
19
20
  * @typedef {import('./email-renderer').MemberLike} MemberLike
20
- * @typedef {object} JobsService
21
+ * @typedef {import('../jobs-service/jobs-service').JobsService} JobsService
21
22
  * @typedef {object} Email
22
23
  * @typedef {object} Newsletter
23
24
  * @typedef {object} Post
@@ -188,23 +189,20 @@ class BatchSendingService {
188
189
  }
189
190
 
190
191
  /**
191
- * Schedules a background job that sends the email in the background if it is pending or failed.
192
+ * Dispatches the job that sends the email; the job itself only proceeds if the email
193
+ * is pending or failed.
194
+ * Resolves when dispatch completes, not when the email is sent.
192
195
  * @param {Email} email
193
- * @returns {void}
196
+ * @returns {Promise<void>}
194
197
  */
195
- scheduleEmail(email) {
198
+ async scheduleEmail(email) {
199
+ await this.#jobsService.dispatch(new SendEmailJob({ emailId: email.id }));
196
200
  logging.info(`[Background Job] batch-sending-service-job queued for email ${email.id}`);
197
- return this.#jobsService.addJob({
198
- name: 'batch-sending-service-job',
199
- job: this.emailJob.bind(this),
200
- data: { emailId: email.id },
201
- offloaded: false,
202
- });
203
201
  }
204
202
 
205
203
  /**
206
- * @private
207
- * @param {{emailId: string}} data Data passed from the job service. We only need the emailId because we need to refetch the email anyway to make sure the status is right and 'locked'.
204
+ * Sends an email after refetching it and acquiring its status lock.
205
+ * @param {{emailId: string}} data Identifier of the email to refetch and lock.
208
206
  */
209
207
  async emailJob({ emailId }) {
210
208
  logging.info(`[Background Job] batch-sending-service-job started for email ${emailId}`);
@@ -270,6 +268,7 @@ class BatchSendingService {
270
268
  { ...this.#getAfterRetryConfig(), description: `email ${emailId} -> submitted` },
271
269
  );
272
270
  logging.info(
271
+ { system: { event: 'send_email.submitted', email_id: emailId } },
273
272
  `[Background Job] batch-sending-service-job completed for email ${emailId} in ${Date.now() - startTime}ms`,
274
273
  );
275
274
  } catch (e) {
@@ -820,7 +819,7 @@ class BatchSendingService {
820
819
  let succeeded = false;
821
820
 
822
821
  try {
823
- let members = await this.retryDb(
822
+ const members = await this.retryDb(
824
823
  async () => {
825
824
  const m = await this.getBatchMembers(batch.id);
826
825
 
@@ -958,7 +957,7 @@ class BatchSendingService {
958
957
  * @returns {Promise<MemberLike[]>}
959
958
  */
960
959
  async getBatchMembers(batchId) {
961
- let models = await this.#models.EmailRecipient.findAll({
960
+ const models = await this.#models.EmailRecipient.findAll({
962
961
  filter: `batch_id:'${batchId}'`,
963
962
  withRelated: ['member', 'member.stripeSubscriptions', 'member.products'],
964
963
  });
@@ -65,6 +65,19 @@ function escapeHtml(unsafe) {
65
65
  .replace(/'/g, '&#039;');
66
66
  }
67
67
 
68
+ /**
69
+ * @param {string} html
70
+ * @returns {string}
71
+ */
72
+ function fixOutlookChars(html) {
73
+ return html
74
+ .replace(/&apos;/g, '&#39;')
75
+ .replace(/→/g, '&rarr;')
76
+ .replace(/–/g, '&ndash;')
77
+ .replace(/“/g, '&ldquo;')
78
+ .replace(/”/g, '&rdquo;');
79
+ }
80
+
68
81
  /**
69
82
  * @param {string} locale
70
83
  * @returns {boolean}
@@ -754,23 +767,52 @@ class EmailRenderer {
754
767
  // Convert DOM back to HTML
755
768
  html = $.html(); // () Fix for vscode syntax highlighter
756
769
 
770
+ // Personalize the entire name row after CSS inlining so an absent name also
771
+ // removes its label and markup, including in clients that discard stylesheets.
772
+ const nameRow = $('.subscription-details .subscription-name');
773
+ const nameHtml = nameRow.length ? $.html(nameRow) : '';
774
+ const nameText = nameRow.text();
775
+ const plaintextHtml = nameHtml
776
+ ? html.replace(nameHtml, '<p>%%{subscription_name_text}%%</p>')
777
+ : html;
778
+ if (nameHtml) {
779
+ html = html.replace(nameHtml, '%%{subscription_name_html}%%');
780
+ }
781
+
757
782
  // Replacement strings
758
783
  const replacementDefinitions = this.buildReplacementDefinitions({
759
784
  html,
760
785
  newsletterUuid: newsletter.get('uuid'),
761
786
  });
762
787
 
788
+ if (nameHtml) {
789
+ const outlookNameHtml = fixOutlookChars(nameHtml);
790
+ replacementDefinitions.push(
791
+ {
792
+ id: 'subscription_name_html',
793
+ token: /%%\{subscription_name_html\}%%/g,
794
+ getValue: (member) =>
795
+ member.name?.trim()
796
+ ? outlookNameHtml.replace('%%{name}%%', () => escapeHtml(member.name))
797
+ : '',
798
+ trusted: true, // Member name is already HTML-escaped
799
+ },
800
+ {
801
+ id: 'subscription_name_text',
802
+ token: /%%\{subscription_name_text\}%%/g,
803
+ getValue: (member) =>
804
+ member.name?.trim() ? nameText.replace('%%{name}%%', () => member.name) : '',
805
+ },
806
+ );
807
+ }
808
+
763
809
  // TODO: normalizeReplacementStrings (replace unsupported replacement strings)
764
810
 
765
811
  // Convert HTML to plaintext
766
- const plaintext = htmlToPlaintext.email(html);
812
+ const plaintext = htmlToPlaintext.email(plaintextHtml);
767
813
 
768
814
  // Fix any unsupported chars in Outlook
769
- html = html.replace(/&apos;/g, '&#39;');
770
- html = html.replace(/→/g, '&rarr;');
771
- html = html.replace(/–/g, '&ndash;');
772
- html = html.replace(/“/g, '&ldquo;');
773
- html = html.replace(/”/g, '&rdquo;');
815
+ html = fixOutlookChars(html);
774
816
 
775
817
  return {
776
818
  html,
@@ -813,7 +855,7 @@ class EmailRenderer {
813
855
  isMemberTrialing(member) {
814
856
  // Do we have an active subscription?
815
857
  if (member.status === 'paid') {
816
- let activeSubscription = member.subscriptions.find((subscription) => {
858
+ const activeSubscription = member.subscriptions.find((subscription) => {
817
859
  return subscription.status === 'trialing';
818
860
  });
819
861
 
@@ -965,12 +1007,6 @@ class EmailRenderer {
965
1007
  return member.name;
966
1008
  },
967
1009
  },
968
- {
969
- id: 'name_class',
970
- getValue: (member) => {
971
- return member.name ? '' : 'hidden';
972
- },
973
- },
974
1010
  {
975
1011
  id: 'email',
976
1012
  getValue: (member) => {
@@ -1183,7 +1219,7 @@ class EmailRenderer {
1183
1219
  */
1184
1220
  #getEmailPreheader(postModel, audience, html) {
1185
1221
  let plaintext = postModel.get('plaintext');
1186
- let customExcerpt = postModel.get('custom_excerpt');
1222
+ const customExcerpt = postModel.get('custom_excerpt');
1187
1223
  if (customExcerpt) {
1188
1224
  return customExcerpt;
1189
1225
  } else {
@@ -1,3 +1,4 @@
1
+ const assert = require('node:assert/strict');
1
2
  const debug = require('@tryghost/debug')('i18n');
2
3
  const url = require('../../api/endpoints/utils/serializers/output/utils/url');
3
4
  const events = require('../../lib/common/events');
@@ -14,10 +15,11 @@ class EmailServiceWrapper {
14
15
  return jsonModel.url;
15
16
  }
16
17
 
17
- init({ ghostServer } = {}) {
18
+ init({ ghostServer, jobsService } = {}) {
18
19
  if (this.service) {
19
20
  return;
20
21
  }
22
+ assert(jobsService, 'Email service requires the jobs service');
21
23
 
22
24
  const EmailService = require('./email-service');
23
25
  const EmailController = require('./email-controller');
@@ -36,7 +38,6 @@ class EmailServiceWrapper {
36
38
  const configService = require('../../../shared/config');
37
39
  const settingsCache = require('../../../shared/settings-cache');
38
40
  const settingsHelpers = require('../settings-helpers');
39
- const jobsService = require('../jobs');
40
41
  const membersService = require('../members');
41
42
  const db = require('../../data/db');
42
43
  const sentry = require('../../../shared/sentry');
@@ -204,7 +204,7 @@ class EmailService {
204
204
  });
205
205
 
206
206
  try {
207
- this.#batchSendingService.scheduleEmail(email);
207
+ await this.#batchSendingService.scheduleEmail(email);
208
208
  } catch (e) {
209
209
  await email.save(
210
210
  {
@@ -334,7 +334,7 @@ class EmailService {
334
334
  logging.warn(`Email resume: scheduling ${email.id} for re-send ${JSON.stringify(breadcrumb)}`);
335
335
 
336
336
  // Skip checkLimits — this email already passed limits when first sent.
337
- this.#batchSendingService.scheduleEmail(email);
337
+ await this.#batchSendingService.scheduleEmail(email);
338
338
  }
339
339
 
340
340
  async #buildResumeBreadcrumb(email) {
@@ -392,10 +392,22 @@ class EmailService {
392
392
  // so we have a immediate response when retrying an email (schedule can take a while to kick off sometimes)
393
393
  await email.save({ status: 'pending' }, { patch: true });
394
394
 
395
- this.#batchSendingService.scheduleEmail(email);
395
+ try {
396
+ await this.#batchSendingService.scheduleEmail(email);
397
+ } catch (e) {
398
+ await email.save({ status: 'failed' }, { patch: true });
399
+ throw e;
400
+ }
396
401
  return email;
397
402
  }
398
403
 
404
+ /**
405
+ * @param {import('./jobs/send-email-job').default} job
406
+ */
407
+ async handleSendEmailJob(job) {
408
+ await this.#batchSendingService.emailJob({ emailId: job.emailId });
409
+ }
410
+
399
411
  /**
400
412
  * @params {string|null} [audienceStatus] - the audience's free/paid status
401
413
  * ('status:free' / 'status:-free'), see EmailRenderer#describeSegment
@@ -476,11 +488,24 @@ class EmailService {
476
488
  * @return {string}
477
489
  */
478
490
  replaceDefinitions(htmlOrPlaintext, replacements, member) {
479
- // Do manual replacements with an example member
480
- for (const replacement of replacements) {
481
- htmlOrPlaintext = htmlOrPlaintext.replace(replacement.token, replacement.getValue(member));
491
+ if (!replacements.length) {
492
+ return htmlOrPlaintext;
482
493
  }
483
- return htmlOrPlaintext;
494
+ const values = replacements.map(({ token, getValue }) => ({ token, value: getValue(member) }));
495
+ const tokens = new RegExp(
496
+ replacements.map(({ token }) => `(?:${token.source})`).join('|'),
497
+ 'g',
498
+ );
499
+
500
+ // Only match the original body, never placeholders inside inserted member data.
501
+ return htmlOrPlaintext.replace(tokens, (match) => {
502
+ for (const { token, value } of values) {
503
+ if (match.search(token) !== -1) {
504
+ return value;
505
+ }
506
+ }
507
+ return match;
508
+ });
484
509
  }
485
510
 
486
511
  /**
@@ -497,7 +522,7 @@ class EmailService {
497
522
  const exampleMember = await this.getExampleMember(null, audience.status);
498
523
 
499
524
  const subject = this.#emailRenderer.getSubject(post);
500
- let { html, plaintext, replacements } = await this.#emailRenderer.renderBody(
525
+ const { html, plaintext, replacements } = await this.#emailRenderer.renderBody(
501
526
  post,
502
527
  newsletter,
503
528
  renderSegment,
@@ -1,4 +1,5 @@
1
- <td dir="ltr" valign="top" align="center" style="display: inline-block; box-sizing: border-box; vertical-align: top; font-family: inherit; text-align: center; padding: 0 4px 4px; cursor: pointer; width: {{#if cellWidth}}{{cellWidth}}{{else}}30%{{/if}};">
1
+ {{!-- Keep the native table-cell display; inline display values make Firefox wrap sibling cells. --}}
2
+ <td dir="ltr" valign="top" align="center" style="box-sizing: border-box; vertical-align: top; font-family: inherit; text-align: center; padding: 0 4px 4px; cursor: pointer; width: {{#if cellWidth}}{{cellWidth}}{{else}}30%{{/if}};">
2
3
  <a href="{{href}}" target="_blank">
3
4
  <img src={{iconUrl}} border="0" width={{width}} height={{height}} alt="{{buttonText}}" {{#if iconClass}}class="{{iconClass}}"{{/if}}>
4
5
  <p class="feedback-button-text">{{t buttonText}}</p>
@@ -1176,12 +1176,6 @@ a[data-flickr-embed] img {
1176
1176
  }
1177
1177
  }
1178
1178
 
1179
- @media all {
1180
- .subscription-details p.hidden {
1181
- display: none !important;
1182
- }
1183
- }
1184
-
1185
1179
  {{#if showBadge}}
1186
1180
  .footer-powered {
1187
1181
  text-align: center;
@@ -59,7 +59,7 @@
59
59
  <table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
60
60
  <tr>
61
61
  <td class="subscription-details">
62
- <p class="%%{name_class}%%">{{t 'Name'}}: %%{name, "not provided"}%%</p>
62
+ <p class="subscription-name">{{t 'Name'}}: %%{name}%%</p>
63
63
  <p>{{t 'Email'}}: <a href="#">%%{email}%%</a></p>
64
64
  <p>{{t 'Member since'}}: %%{created_at}%%</p>
65
65
  </td>
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const job_1 = require("../../jobs-service/job");
4
+ class SendEmailJob extends job_1.Job {
5
+ static type = 'send-email';
6
+ emailId;
7
+ constructor({ emailId }) {
8
+ super();
9
+ this.emailId = emailId;
10
+ }
11
+ }
12
+ exports.default = SendEmailJob;
@@ -30,7 +30,7 @@ const { escapeExpression } = require('handlebars');
30
30
 
31
31
  /**
32
32
  * @typedef {object} EmailProviderSuccessResponse
33
- * @prop {string} id
33
+ * @prop {string|null} id Null when the batch carries a per-recipient Message-Id
34
34
  */
35
35
 
36
36
  class MailgunEmailProvider {
@@ -134,6 +134,11 @@ class MailgunEmailProvider {
134
134
  messageData.deliveryTime = options.deliveryTime;
135
135
  }
136
136
 
137
+ // only newsletters opt in; automation and gift emails rely on the id Mailgun returns
138
+ if (this.#config.get('bulkEmail:perRecipientMessageId') === true) {
139
+ messageData.perRecipientMessageId = true;
140
+ }
141
+
137
142
  // create recipient data for Mailgun using replacement definitions
138
143
  const htmlEscapedIds = new Set(
139
144
  replacementDefinitions.filter((def) => !def.trusted).map((def) => def.id),
@@ -163,7 +168,7 @@ class MailgunEmailProvider {
163
168
 
164
169
  // Return mailgun provider id, trim <> from response
165
170
  return {
166
- id: response.id.trim().replace(/^<|>$/g, ''),
171
+ id: response.id === null ? null : response.id.trim().replace(/^<|>$/g, ''),
167
172
  };
168
173
  } catch (e) {
169
174
  let ghostError;
@@ -59,7 +59,7 @@ const logging = require('@tryghost/logging');
59
59
 
60
60
  /**
61
61
  * @typedef {object} EmailProviderSuccessResponse
62
- * @prop {string} id
62
+ * @prop {string|null} id Null when the batch carries a per-recipient Message-Id
63
63
  */
64
64
 
65
65
  class SendingService {
@@ -917,15 +917,13 @@ class GiftService {
917
917
  }, '[Background Job] clean-gifts finished its cleanup phases');
918
918
  }
919
919
  async processReminders() {
920
+ const startedAt = Date.now();
920
921
  const now = new Date();
921
922
  const toRemind = await this.deps.giftRepository.findPendingReminder({
922
923
  now,
923
924
  reminderLeadMs: GIFT_REMINDER_LEAD_MS,
924
925
  reminderFloorMs: GIFT_REMINDER_FLOOR_MS,
925
926
  });
926
- if (toRemind.length === 0) {
927
- return { remindedCount: 0, skippedCount: 0, failedCount: 0 };
928
- }
929
927
  let remindedCount = 0;
930
928
  let skippedCount = 0;
931
929
  let failedCount = 0;
@@ -944,6 +942,17 @@ class GiftService {
944
942
  failedCount += 1;
945
943
  }
946
944
  }
945
+ // The jobs service's lifecycle log carries no counts, so the poll reports
946
+ // its own summary here for the daily job and the exact scheduler alike.
947
+ logging_1.default.info({
948
+ system: {
949
+ event: 'send_gift_reminders.completed',
950
+ reminded_count: remindedCount,
951
+ skipped_count: skippedCount,
952
+ failed_count: failedCount,
953
+ duration_ms: Date.now() - startedAt,
954
+ },
955
+ }, `[Background Job] send-gift-reminders processed reminders: ${remindedCount} sent, ${skippedCount} not due, ${failedCount} rejected`);
947
956
  return { remindedCount, skippedCount, failedCount };
948
957
  }
949
958
  async sendReminderForGift(token) {
@@ -28,7 +28,6 @@ function init(options) {
28
28
  const { SubscriptionActivatedEvent } = require('../../../shared/events');
29
29
  const StartGiftReminderFlushEvent = require('./events/start-gift-reminder-flush-event');
30
30
  const { StartGiftDeliveryFlushEvent } = require('./events/start-gift-delivery-flush-event');
31
- const jobs = require('./jobs');
32
31
  const emailAnalyticsJobs = require('../email-analytics/jobs');
33
32
  const { GhostMailer } = require('../mail');
34
33
  const MailgunClient = require('../lib/mailgun-client');
@@ -155,7 +154,6 @@ function init(options) {
155
154
  logging.error(err, `Failed to process gift delivery ${event.data.deliveryId}`);
156
155
  }
157
156
  });
158
- jobs.scheduleGiftReminderJob();
159
157
  }
160
158
  // Re-arms future deliveries and retries sends interrupted by a previous
161
159
  // shutdown. Runs after all services initialise because recovery sends need
@@ -1,9 +1,8 @@
1
- const path = require('path');
2
1
  const logging = require('@tryghost/logging');
3
- const jobManager = require('../../jobs');
4
2
  const CleanGiftsJob = require('./clean-gifts-job').default;
3
+ const SendGiftRemindersJob = require('./send-gift-reminders-job').default;
5
4
 
6
- let hasScheduled = {
5
+ const hasScheduled = {
7
6
  cleanup: false,
8
7
  reminders: false,
9
8
  };
@@ -23,25 +22,6 @@ function randomOffPeakDailyCron() {
23
22
  return `${s} ${m} ${h} * * *`;
24
23
  }
25
24
 
26
- function scheduleJob(key, name, jobFile) {
27
- if (alreadyScheduledOrTest(key)) {
28
- return hasScheduled[key];
29
- }
30
-
31
- const at = randomOffPeakDailyCron();
32
-
33
- logging.info(`[Background Job] ${name} scheduled at ${at}`);
34
- jobManager.addJob({
35
- at,
36
- job: path.resolve(__dirname, jobFile),
37
- name,
38
- });
39
-
40
- hasScheduled[key] = true;
41
-
42
- return true;
43
- }
44
-
45
25
  module.exports = {
46
26
  async scheduleGiftCleanupJob(jobsService) {
47
27
  if (alreadyScheduledOrTest('cleanup')) {
@@ -55,7 +35,15 @@ module.exports = {
55
35
  hasScheduled.cleanup = true;
56
36
  },
57
37
 
58
- scheduleGiftReminderJob() {
59
- return scheduleJob('reminders', 'send-gift-reminders', 'send-gift-reminders-job.js');
38
+ async scheduleGiftReminderJob(jobsService) {
39
+ if (alreadyScheduledOrTest('reminders')) {
40
+ return;
41
+ }
42
+
43
+ const cron = randomOffPeakDailyCron();
44
+ logging.info(`[Background Job] send-gift-reminders scheduled at ${cron}`);
45
+ await jobsService.scheduleRecurring(new SendGiftRemindersJob(), { cron });
46
+
47
+ hasScheduled.reminders = true;
60
48
  },
61
49
  };
@@ -1,47 +1,7 @@
1
- const { parentPort } = require('node:worker_threads');
2
- const StartGiftReminderFlushEvent = require('../events/start-gift-reminder-flush-event');
3
-
4
- // Recurring job to send gift reminder emails. The actual work runs on the
5
- // main thread via a domain event so we don't have to re-initialize every
6
- // service dependency inside this worker
7
-
8
- // Exit early when cancelled to prevent stalling shutdown. No cleanup needed
9
- // when cancelling as everything is idempotent - any gift that hasn't yet had
10
- // its reminder recorded will be picked up on the next run
11
- function cancel() {
12
- if (parentPort) {
13
- parentPort.postMessage('cancelled before completion');
14
- parentPort.postMessage('cancelled');
15
- } else {
16
- setTimeout(() => {
17
- process.exit(0);
18
- }, 1000);
19
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const job_1 = require("../../jobs-service/job");
4
+ class SendGiftRemindersJob extends job_1.Job {
5
+ static type = 'send-gift-reminders';
20
6
  }
21
-
22
- if (parentPort) {
23
- parentPort.once('message', (message) => {
24
- if (message === 'cancel') {
25
- return cancel();
26
- }
27
- });
28
- }
29
-
30
- (async () => {
31
- if (parentPort) {
32
- // Bounce to the main thread via the JobManager's event bridge - the
33
- // gifts service subscribes to this event and runs the work in the
34
- // already-initialised main process
35
- parentPort.postMessage({
36
- event: {
37
- type: StartGiftReminderFlushEvent.name,
38
- },
39
- });
40
- parentPort.postMessage('dispatched to main process');
41
- parentPort.postMessage('done');
42
- } else {
43
- setTimeout(() => {
44
- process.exit(0);
45
- }, 1000);
46
- }
47
- })();
7
+ exports.default = SendGiftRemindersJob;
@@ -20,7 +20,7 @@ async function accept(invitation) {
20
20
  return models.Base.transaction(async (transacting) => {
21
21
  const options = { context: { internal: true }, transacting, forUpdate: true };
22
22
 
23
- let invite = await models.Invite.findOne({ token: inviteToken, status: 'sent' }, options);
23
+ const invite = await models.Invite.findOne({ token: inviteToken, status: 'sent' }, options);
24
24
 
25
25
  if (!invite) {
26
26
  throw new errors.NotFoundError({ message: tpl(messages.inviteNotFound) });
@@ -32,7 +32,7 @@ async function accept(invitation) {
32
32
 
33
33
  const email = invite.get('email');
34
34
 
35
- let user = await models.User.findOne({ email }, options);
35
+ const user = await models.User.findOne({ email }, options);
36
36
  if (user) {
37
37
  throw new errors.ValidationError({
38
38
  message: tpl(messages.inviteEmailAlreadyExist.message),
@@ -21,33 +21,6 @@ const workerMessageHandler = ({ name, message }) => {
21
21
  }
22
22
  };
23
23
 
24
- const initTestMode = () => {
25
- // Output job queue length every 5 seconds
26
- setInterval(() => {
27
- logging.warn(
28
- `${jobManager.inlineQueue.length()} jobs in the queue. Idle: ${jobManager.inlineQueue.idle()}`,
29
- );
30
-
31
- const runningScheduledjobs = Object.keys(jobManager.bree.workers);
32
- if (Object.keys(jobManager.bree.workers).length) {
33
- logging.warn(
34
- `${Object.keys(jobManager.bree.workers).length} jobs running: ${runningScheduledjobs}`,
35
- );
36
- }
37
-
38
- const scheduledJobs = Object.keys(jobManager.bree.intervals);
39
- if (Object.keys(jobManager.bree.intervals).length) {
40
- logging.warn(
41
- `${Object.keys(jobManager.bree.intervals).length} scheduled jobs: ${scheduledJobs}`,
42
- );
43
- }
44
-
45
- if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) {
46
- logging.warn('No scheduled or running jobs');
47
- }
48
- }, 5000);
49
- };
50
-
51
24
  const jobManager = new JobManager({
52
25
  errorHandler,
53
26
  workerMessageHandler,
@@ -58,4 +31,3 @@ const jobManager = new JobManager({
58
31
  });
59
32
 
60
33
  module.exports = jobManager;
61
- module.exports.initTestMode = initTestMode;
@@ -68,6 +68,11 @@ class JobsService {
68
68
  const queue = this.#queueByType.get(type);
69
69
  return queue === undefined ? undefined : { queue };
70
70
  }
71
+ /**
72
+ * Resolves when the backend's enqueue call completes, without waiting for the
73
+ * handler to run. Enqueue errors reject this promise. Resolution does not
74
+ * guarantee execution: the backend controls what happens to work during shutdown.
75
+ */
71
76
  async dispatch(job) {
72
77
  const envelope = this.#buildEnvelope(job);
73
78
  await this.#backend.enqueue(envelope, this.#routingFor(envelope.type));
@@ -40,12 +40,15 @@ exports.default = registerJobHandlers;
40
40
  const clean_tokens_job_1 = __importDefault(require("../members/jobs/clean-tokens-job"));
41
41
  const clean_expired_comped_job_1 = __importDefault(require("../members/jobs/clean-expired-comped-job"));
42
42
  const clean_gifts_job_1 = __importDefault(require("../gifts/jobs/clean-gifts-job"));
43
+ const send_gift_reminders_job_1 = __importDefault(require("../gifts/jobs/send-gift-reminders-job"));
43
44
  const external_media_inliner_job_1 = __importDefault(require("../media-inliner/external-media-inliner-job"));
44
45
  const content_csv_import_job_1 = __importDefault(require("../content-import/jobs/content-csv-import-job"));
45
46
  const contentImport = __importStar(require("../content-import"));
47
+ const members_import_job_1 = __importDefault(require("../members/jobs/members-import-job"));
46
48
  const update_check_job_1 = __importDefault(require("../update-check/jobs/update-check-job"));
47
49
  const process_webmention_job_1 = __importDefault(require("../mentions/process-webmention-job"));
48
50
  const send_webmentions_job_1 = __importDefault(require("../mentions/send-webmentions-job"));
51
+ const send_email_job_1 = __importDefault(require("../email-service/jobs/send-email-job"));
49
52
  const updateCheck = require('../update-check');
50
53
  // Webmention processing fetches external pages and is triggered by
51
54
  // unauthenticated requests, so webmention jobs run in their own lane where a
@@ -54,7 +57,11 @@ const updateCheck = require('../update-check');
54
57
  // this shared declaration so none can declare the queue with a different
55
58
  // concurrency.
56
59
  const WEBMENTIONS_QUEUE = { queue: 'webmentions', concurrency: 3 };
57
- function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInliner, mentionsController, mentionsSendingService, }) {
60
+ // Keep newsletter sends independent of imports and other shared work. Two sends
61
+ // can progress at once, each with its own two batch workers, so a long send or
62
+ // retry does not hold up every other newsletter.
63
+ const EMAIL_QUEUE = { queue: 'email', concurrency: 2 };
64
+ function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInliner, mentionsController, mentionsSendingService, membersService, emailService, }) {
58
65
  jobsService.handle(clean_tokens_job_1.default, async () => {
59
66
  await memberJobs.cleanTokens();
60
67
  });
@@ -64,12 +71,18 @@ function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInline
64
71
  jobsService.handle(clean_gifts_job_1.default, async () => {
65
72
  await giftService.cleanup();
66
73
  });
74
+ jobsService.handle(send_gift_reminders_job_1.default, async () => {
75
+ await giftService.processReminders();
76
+ });
67
77
  jobsService.handle(external_media_inliner_job_1.default, async (job) => {
68
78
  await mediaInliner.inline(job.domains);
69
79
  });
70
80
  jobsService.handle(content_csv_import_job_1.default, async (job) => {
71
81
  await contentImport.handleJob(job);
72
82
  });
83
+ jobsService.handle(members_import_job_1.default, async (job) => {
84
+ await membersService.handleImportJob(job);
85
+ });
73
86
  jobsService.handle(update_check_job_1.default, async () => {
74
87
  await updateCheck({ rethrowErrors: true });
75
88
  });
@@ -79,4 +92,7 @@ function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInline
79
92
  jobsService.handle(send_webmentions_job_1.default, async (job) => {
80
93
  await mentionsSendingService.sendWebmentions(job);
81
94
  }, WEBMENTIONS_QUEUE);
95
+ jobsService.handle(send_email_job_1.default, async (job) => {
96
+ await emailService.handleSendEmailJob(job);
97
+ }, EMAIL_QUEUE);
82
98
  }