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
@@ -90,7 +90,7 @@ function formatResponse(post, context, locals = {}) {
90
90
 
91
91
  prepareContextResource(post);
92
92
 
93
- let entry = {
93
+ const entry = {
94
94
  post: post,
95
95
  };
96
96
 
@@ -20,6 +20,8 @@ module.exports = function renderer(req, res, data) {
20
20
  if (res.destroyed && !res.writableEnded) {
21
21
  debug('Client gone before render, skipping: ' + req.originalUrl);
22
22
  res.statusCode = 499;
23
+ // end() anyway: the request queue only frees a slot on res.end()
24
+ res.end();
23
25
  return;
24
26
  }
25
27
 
@@ -56,6 +58,7 @@ module.exports = function renderer(req, res, data) {
56
58
  if (res.destroyed && !res.writableEnded) {
57
59
  debug('Client gone during render, discarding: ' + req.originalUrl);
58
60
  res.statusCode = 499;
61
+ res.end();
59
62
  return;
60
63
  }
61
64
 
@@ -118,7 +118,7 @@ templates.getEntryTemplateHierarchy = function getEntryTemplateHierarchy(postObj
118
118
  templates.pickTemplate = function pickTemplate(templateList, fallback) {
119
119
  let template;
120
120
 
121
- if (!_.isArray(templateList)) {
121
+ if (!Array.isArray(templateList)) {
122
122
  templateList = [templateList];
123
123
  }
124
124
 
@@ -44,7 +44,7 @@ module.exports = function staticController(req, res, next) {
44
44
 
45
45
  return Promise.all(promises)
46
46
  .then(function handleResult(result) {
47
- let response = {};
47
+ const response = {};
48
48
 
49
49
  if (res.routerOptions.data) {
50
50
  response.data = {};
@@ -83,7 +83,7 @@ class ParentRouter {
83
83
  * @private
84
84
  */
85
85
  _respectDominantRouter(req, res, next, slug) {
86
- let siteRouter = this._getSiteRouter(req);
86
+ const siteRouter = this._getSiteRouter(req);
87
87
  let targetRoute = null;
88
88
 
89
89
  // CASE: iterate over routers and check whether a router has a redirect for the target slug enabled.
@@ -71,7 +71,7 @@ class Registry {
71
71
  * @returns {ParentRouter|undefined}
72
72
  */
73
73
  getRouterByName(name) {
74
- for (let routerKey in this.routers) {
74
+ for (const routerKey in this.routers) {
75
75
  if (this.routers[routerKey].name === name) {
76
76
  return this.routers[routerKey];
77
77
  }
@@ -1,27 +1,28 @@
1
- const _ = require('lodash');
2
- const xml = require('xml');
3
1
  const moment = require('moment');
4
- const path = require('path');
5
2
  const urlUtils = require('../../../shared/url-utils').default;
6
- const localUtils = require('./utils');
7
-
8
- // Sitemap specific xml namespace declarations that should not change
9
- const XMLNS_DECLS = {
10
- _attr: {
11
- xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9',
12
- 'xmlns:image': 'http://www.google.com/schemas/sitemap-image/1.1',
13
- },
14
- };
3
+ const sitemapXml = require('./sitemap-xml');
15
4
 
16
5
  class BaseSiteMapGenerator {
17
6
  constructor() {
18
- this.nodeLookup = {};
19
- this.nodeTimeLookup = {};
7
+ // id -> {loc, ts, imageLoc}: one flat record per resource, rather than
8
+ // the nested element tree the xml package used to take plus a parallel
9
+ // map of Moments. Both were held for the lifetime of the index, and at
10
+ // 10k posts they measured ~1.1 kB per resource against ~300 B for this.
11
+ this.nodeLookup = new Map();
20
12
  this.siteMapContent = new Map();
21
13
  this.lastModified = 0;
22
14
  this.maxPerPage = 50000;
23
15
  }
24
16
 
17
+ /**
18
+ * How many resources this generator holds. Read by the index generator to
19
+ * work out how many pages the type needs, so it does not have to know how
20
+ * they are stored.
21
+ */
22
+ get size() {
23
+ return this.nodeLookup.size;
24
+ }
25
+
25
26
  hasCanonicalUrl(datum, url) {
26
27
  if (!datum?.canonical_url) {
27
28
  return false;
@@ -46,72 +47,58 @@ class BaseSiteMapGenerator {
46
47
  }
47
48
 
48
49
  generateXmlFromNodes(page) {
49
- // Get a mapping of node to timestamp
50
- let nodesToProcess = _.map(this.nodeLookup, (node, id) => {
51
- return {
52
- id: id,
53
- // Using negative here to sort newest to oldest
54
- ts: -(this.nodeTimeLookup[id] || 0),
55
- node: node,
56
- };
57
- });
58
-
59
- // Sort nodes by timestamp
60
- nodesToProcess = _.sortBy(nodesToProcess, 'ts');
50
+ // Sort newest to oldest. The records are sorted in place of a wrapper
51
+ // object per resource, so a render allocates one array of references.
52
+ const records = [...this.nodeLookup.values()];
53
+ records.sort((a, b) => b.ts - a.ts);
61
54
 
62
55
  // Get the page of nodes that was requested
63
- nodesToProcess = nodesToProcess.slice((page - 1) * this.maxPerPage, page * this.maxPerPage);
56
+ const pageRecords = records.slice((page - 1) * this.maxPerPage, page * this.maxPerPage);
64
57
 
65
58
  // Do not generate empty sitemaps
66
- if (nodesToProcess.length === 0) {
59
+ if (pageRecords.length === 0) {
67
60
  return null;
68
61
  }
69
62
 
70
- // Grab just the nodes
71
- const nodes = _.map(nodesToProcess, 'node');
72
-
73
- const data = {
74
- // Concat the elements to the _attr declaration
75
- urlset: [XMLNS_DECLS].concat(nodes),
76
- };
77
-
78
- // Generate full xml
79
- let sitemapXml = localUtils.getDeclarations() + xml(data);
80
-
81
- // Perform url transformations
82
- // - Necessary because sitemap data is supplied by the router which
83
- // uses knex directly bypassing model-layer attribute transforms
84
- sitemapXml = urlUtils.transformReadyToAbsolute(sitemapXml);
85
-
86
- return sitemapXml;
63
+ return sitemapXml.renderUrlSet(pageRecords);
87
64
  }
88
65
 
89
66
  addUrl(url, datum) {
90
- // Computed once and threaded through: the three consumers below each
91
- // used to construct their own moment() from the same datum, which
92
- // dominates the cost of bulk adds.
67
+ if (this.hasCanonicalUrl(datum, url)) {
68
+ return;
69
+ }
70
+
71
+ // Computed once and threaded through: the consumers below each used to
72
+ // construct their own moment() from the same datum, which dominates the
73
+ // cost of bulk adds.
93
74
  const lastModified = this.getLastModifiedForDatum(datum);
94
- const node = this.createUrlNodeFromDatum(url, datum, lastModified);
95
75
 
96
- if (node && !this.hasCanonicalUrl(datum, url)) {
97
- this.updateLastModified(datum, lastModified);
98
- this.updateLookups(datum, node, lastModified);
99
- // force regeneration of xml
100
- this.siteMapContent.clear();
101
- }
76
+ this.updateLastModified(datum, lastModified);
77
+ this.updateLookups(datum, this.createRecordFromDatum(url, datum, lastModified));
78
+ // force regeneration of xml
79
+ this.siteMapContent.clear();
102
80
  }
103
81
 
104
82
  /**
105
- * @returns {moment.Moment}
83
+ * The parse stays with moment, which is more forgiving than `new Date` of
84
+ * the shapes the database hands back; only what is stored changes, from a
85
+ * Moment to the epoch milliseconds the sort and the lastmod both want.
86
+ *
87
+ * @returns {number} epoch milliseconds
106
88
  */
107
89
  getLastModifiedForDatum(datum) {
108
- if (datum.updated_at || datum.published_at || datum.created_at) {
109
- const modifiedDate = datum.updated_at || datum.published_at || datum.created_at;
90
+ const modifiedDate = datum.updated_at || datum.published_at || datum.created_at;
110
91
 
111
- return moment(modifiedDate);
112
- } else {
113
- return moment();
92
+ if (!modifiedDate) {
93
+ return Date.now();
114
94
  }
95
+
96
+ const lastModified = moment(modifiedDate).valueOf();
97
+
98
+ // An unparseable date used to render an empty <lastmod>; now it would
99
+ // throw out of `new Date(NaN).toISOString()` and take the whole sitemap
100
+ // with it, so fall back to now.
101
+ return Number.isNaN(lastModified) ? Date.now() : lastModified;
115
102
  }
116
103
 
117
104
  updateLastModified(datum, lastModified = this.getLastModifiedForDatum(datum)) {
@@ -121,54 +108,40 @@ class BaseSiteMapGenerator {
121
108
  }
122
109
 
123
110
  /**
124
- *
125
111
  * @param {string} url
126
112
  * @param {Object} datum
127
- * @returns
113
+ * @param {number} [lastModified] epoch milliseconds
114
+ * @returns {{loc: string, ts: number, imageLoc: string|null}}
128
115
  */
129
- createUrlNodeFromDatum(url, datum, lastModified = this.getLastModifiedForDatum(datum)) {
130
- let node;
131
- let imgNode;
132
-
133
- node = {
134
- url: [{ loc: url }, { lastmod: lastModified.toISOString() }],
116
+ createRecordFromDatum(url, datum, lastModified = this.getLastModifiedForDatum(datum)) {
117
+ return {
118
+ // Transformed here rather than over the finished document: the two
119
+ // urls are the only transform-ready values a sitemap carries, and a
120
+ // regex across several megabytes of xml both costs and leaves a rope.
121
+ loc: urlUtils.transformReadyToAbsolute(url),
122
+ ts: lastModified,
123
+ imageLoc: this.createImageLocFromDatum(datum),
135
124
  };
136
-
137
- imgNode = this.createImageNodeFromDatum(datum);
138
-
139
- if (imgNode) {
140
- node.url.push(imgNode);
141
- }
142
-
143
- return node;
144
125
  }
145
126
 
146
- createImageNodeFromDatum(datum) {
127
+ createImageLocFromDatum(datum) {
147
128
  // Check for cover first because user has cover but the rest only have image
148
129
  const image = datum.cover_image || datum.profile_image || datum.feature_image;
149
130
 
150
- let imageUrl;
151
- let imageEl;
152
-
153
131
  if (!image) {
154
- return;
132
+ return null;
155
133
  }
156
134
 
157
135
  // Grab the image url
158
- imageUrl = urlUtils.urlFor('image', { image: image }, true);
136
+ const imageUrl = urlUtils.urlFor('image', { image: image }, true);
159
137
 
160
- // Verify the url structure
138
+ // Verify the url structure. Checked before the transform, as it always
139
+ // has been: the url the validator sees is the one urlFor returned.
161
140
  if (!this.validateImageUrl(imageUrl)) {
162
- return;
141
+ return null;
163
142
  }
164
143
 
165
- // Create the weird xml node syntax structure that is expected
166
- imageEl = [{ 'image:loc': imageUrl }, { 'image:caption': path.basename(imageUrl) }];
167
-
168
- // Return the node to be added to the url xml node
169
- return {
170
- 'image:image': imageEl,
171
- };
144
+ return urlUtils.transformReadyToAbsolute(imageUrl);
172
145
  }
173
146
 
174
147
  validateImageUrl(imageUrl) {
@@ -185,14 +158,12 @@ class BaseSiteMapGenerator {
185
158
  return content;
186
159
  }
187
160
 
188
- updateLookups(datum, node, lastModified = this.getLastModifiedForDatum(datum)) {
189
- this.nodeLookup[datum.id] = node;
190
- this.nodeTimeLookup[datum.id] = lastModified;
161
+ updateLookups(datum, record) {
162
+ this.nodeLookup.set(datum.id, record);
191
163
  }
192
164
 
193
165
  reset() {
194
- this.nodeLookup = {};
195
- this.nodeTimeLookup = {};
166
+ this.nodeLookup.clear();
196
167
  this.siteMapContent.clear();
197
168
  this.lastModified = 0;
198
169
  }
@@ -1,37 +1,31 @@
1
1
  const _ = require('lodash');
2
- const xml = require('xml');
3
- const moment = require('moment');
4
2
  const urlUtils = require('../../../shared/url-utils').default;
5
- const localUtils = require('./utils');
6
-
7
- const XMLNS_DECLS = {
8
- _attr: {
9
- xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9',
10
- },
11
- };
3
+ const sitemapXml = require('./sitemap-xml');
12
4
 
13
5
  class SiteMapIndexGenerator {
14
6
  constructor(options) {
15
7
  options = options || {};
16
8
  this.types = options.types;
17
9
  this.maxPerPage = options.maxPerPage;
10
+ // The index is the one sitemap crawlers fetch repeatedly, and rendering
11
+ // it counts every resource of every type. Cached until something
12
+ // invalidates the index; the manager resets this when it does.
13
+ this.siteMapContent = null;
18
14
  }
19
15
 
20
16
  getXml() {
21
- const urlElements = this.generateSiteMapUrlElements();
17
+ if (this.siteMapContent !== null) {
18
+ return this.siteMapContent;
19
+ }
22
20
 
23
- const data = {
24
- // Concat the elements to the _attr declaration
25
- sitemapindex: [XMLNS_DECLS].concat(urlElements),
26
- };
21
+ this.siteMapContent = sitemapXml.renderSiteMapIndex(this.generateSiteMapUrlElements());
27
22
 
28
- // Return the xml
29
- return localUtils.getDeclarations() + xml(data);
23
+ return this.siteMapContent;
30
24
  }
31
25
 
32
26
  generateSiteMapUrlElements() {
33
27
  return _.map(this.types, (resourceType) => {
34
- const noOfPages = Math.ceil(Object.keys(resourceType.nodeLookup).length / this.maxPerPage);
28
+ const noOfPages = Math.ceil(resourceType.size / this.maxPerPage);
35
29
  const pages = [];
36
30
  for (let i = 0; i < noOfPages; i++) {
37
31
  const page = i === 0 ? '' : `-${i + 1}`;
@@ -39,16 +33,17 @@ class SiteMapIndexGenerator {
39
33
  { relativeUrl: '/sitemap-' + resourceType.name + page + '.xml' },
40
34
  true,
41
35
  );
42
- const lastModified = resourceType.lastModified;
43
36
 
44
- pages.push({
45
- sitemap: [{ loc: url }, { lastmod: moment(lastModified).toISOString() }],
46
- });
37
+ pages.push({ loc: url, ts: resourceType.lastModified });
47
38
  }
48
39
 
49
40
  return pages;
50
41
  }).flat();
51
42
  }
43
+
44
+ reset() {
45
+ this.siteMapContent = null;
46
+ }
52
47
  }
53
48
 
54
49
  module.exports = SiteMapIndexGenerator;
@@ -1,3 +1,5 @@
1
+ const { performance } = require('node:perf_hooks');
2
+ const { setImmediate: yieldToEventLoop } = require('node:timers/promises');
1
3
  const errors = require('@tryghost/errors');
2
4
  const urlUtils = require('../../../shared/url-utils').default;
3
5
  const IndexMapGenerator = require('./site-map-index-generator');
@@ -22,11 +24,22 @@ const SITEMAP_COLUMNS = [
22
24
  'canonical_url',
23
25
  ];
24
26
 
27
+ const RESOURCE_TYPES = ['posts', 'pages', 'tags', 'authors'];
28
+
29
+ // Longest stretch a build runs before yielding to the event loop.
30
+ const DEFAULT_BUILD_SLICE_MS = 10;
31
+
32
+ // Builds a reader will start before giving up on a site that keeps changing.
33
+ const MAX_BUILD_ATTEMPTS = 3;
34
+
25
35
  class SiteMapManager {
26
36
  constructor(options) {
27
37
  options = options || {};
28
38
 
29
39
  options.maxPerPage = options.maxPerPage || 50000;
40
+ // Every build creates fresh generators from these.
41
+ this._options = options;
42
+ this._buildSliceMs = options.buildSliceMs ?? DEFAULT_BUILD_SLICE_MS;
30
43
 
31
44
  this.pages = options.pages || this.createPagesGenerator(options);
32
45
  this.posts = options.posts || this.createPostsGenerator(options);
@@ -54,7 +67,7 @@ class SiteMapManager {
54
67
  this._indexEpoch = 0;
55
68
  // Static/collection route entries only arrive via RouteRegistered,
56
69
  // which fires at boot and routes reload. They are recorded here so
57
- // every rebuild can replay them after resetting the generators.
70
+ // every rebuild can replay them into its fresh generators.
58
71
  this._routerEntries = [];
59
72
 
60
73
  routingEvents.on('RouteRegistered', ({ path, type, id }) => {
@@ -66,7 +79,6 @@ class SiteMapManager {
66
79
  datum: { id, staticRoute: type === 'StaticRoutesRouter' },
67
80
  };
68
81
  this._routerEntries.push(entry);
69
- this.pages.addUrl(entry.url, entry.datum);
70
82
  // A router registering after a build must not leave a
71
83
  // zero-router index marked built — the CDN would pin it.
72
84
  this._invalidateIndex();
@@ -90,13 +102,13 @@ class SiteMapManager {
90
102
  });
91
103
  }
92
104
 
93
- createIndexGenerator(options) {
105
+ createIndexGenerator(options, types = this) {
94
106
  return new IndexMapGenerator({
95
107
  types: {
96
- pages: this.pages,
97
- posts: this.posts,
98
- authors: this.authors,
99
- tags: this.tags,
108
+ pages: types.pages,
109
+ posts: types.posts,
110
+ authors: types.authors,
111
+ tags: types.tags,
100
112
  },
101
113
  maxPerPage: options.maxPerPage,
102
114
  });
@@ -133,81 +145,108 @@ class SiteMapManager {
133
145
  * no caller can render from an unbuilt index. The index is built on first
134
146
  * read; the invalidation signals empty it and the next read rebuilds.
135
147
  *
136
- * Concurrent readers share one build. A build whose result was
137
- * invalidated while it ran is discarded and the read fails — the index
138
- * must never serve pre-invalidation data (the CDN would pin it for the
139
- * full cache maxAge), and a 503 is retried by crawlers and stored by
140
- * nobody. Deliberately no retry; if SITEMAP_BUILD_SUPERSEDED shows up in
141
- * the logs at any rate worth caring about, add one then.
148
+ * Reads wait for the rebuild rather than being served the previous index.
149
+ * site.changed is emitted by the same response that purges the CDN, so the
150
+ * first read after an invalidation is the one the CDN stores for the full
151
+ * cache maxAge: serving the previous index there would pin a sitemap
152
+ * missing the change on every publish.
153
+ *
154
+ * Concurrent readers share one build. A build invalidated while it runs is
155
+ * abandoned and a fresh one started; a site changing faster than the index
156
+ * can be built fails the read with a 503, which crawlers retry and nobody
157
+ * stores.
142
158
  */
143
159
  async _ensureIndexReady() {
144
- if (this._indexBuilt) {
145
- return;
146
- }
147
- if (!this._buildInFlight) {
148
- this._buildInFlight = this._buildIndex().finally(() => {
149
- this._buildInFlight = null;
150
- });
160
+ for (let attempt = 0; attempt < MAX_BUILD_ATTEMPTS && !this._indexBuilt; attempt++) {
161
+ if (!this._buildInFlight) {
162
+ this._buildInFlight = this._buildIndex().finally(() => {
163
+ this._buildInFlight = null;
164
+ });
165
+ }
166
+ await this._buildInFlight;
151
167
  }
152
- await this._buildInFlight;
153
168
 
154
169
  if (!this._indexBuilt) {
155
170
  throw new errors.MaintenanceError({
156
- message: 'Sitemap index build was invalidated by a concurrent site change',
171
+ message: 'Sitemap index build was repeatedly invalidated by concurrent site changes',
157
172
  code: 'SITEMAP_BUILD_SUPERSEDED',
158
173
  });
159
174
  }
160
175
  }
161
176
 
177
+ /**
178
+ * Builds into fresh generators and swaps them in once complete, so the
179
+ * build can yield to the event loop without any reader seeing a partial
180
+ * index. The epoch is checked after every yield: an invalidation (including
181
+ * a RouteRegistered mid-build) abandons the build without swapping.
182
+ */
162
183
  async _buildIndex() {
163
184
  const epoch = this._indexEpoch;
164
185
  const urlService = this._getUrlService();
165
186
  const fetch = (type) => urlService.getRoutableResources(type, { columns: SITEMAP_COLUMNS });
166
187
 
167
- const [posts, pages, tags, authors] = await Promise.all([
168
- fetch('posts'),
169
- fetch('pages'),
170
- fetch('tags'),
171
- fetch('authors'),
172
- ]);
188
+ const [posts, pages, tags, authors] = await Promise.all(RESOURCE_TYPES.map(fetch));
173
189
  const resources = { posts, pages, tags, authors };
174
190
 
175
191
  if (epoch !== this._indexEpoch) {
176
- // Invalidated while fetching: leave the generators alone and let
177
- // _ensureIndexReady start over.
178
192
  return;
179
193
  }
180
- // Everything from here on is synchronous, so no request can observe
181
- // a half-applied index.
182
- this.posts.reset();
183
- this.pages.reset();
184
- this.tags.reset();
185
- this.users.reset();
194
+
195
+ const next = {
196
+ posts: this.createPostsGenerator(this._options),
197
+ pages: this.createPagesGenerator(this._options),
198
+ tags: this.createTagsGenerator(this._options),
199
+ authors: this.createUsersGenerator(this._options),
200
+ };
186
201
  for (const entry of this._routerEntries) {
187
- this.pages.addUrl(entry.url, entry.datum);
202
+ next.pages.addUrl(entry.url, entry.datum);
188
203
  }
189
- for (const type of ['posts', 'pages', 'tags', 'authors']) {
190
- for (const datum of resources[type]) {
191
- this._applyResource(type, datum);
204
+
205
+ let sliceStart = performance.now();
206
+ for (const type of RESOURCE_TYPES) {
207
+ const rows = resources[type];
208
+ for (let i = 0; i < rows.length; i++) {
209
+ this._applyResource(next, type, rows[i]);
210
+ // Release each row once applied, so rows and records are not both
211
+ // fully resident.
212
+ rows[i] = undefined;
213
+
214
+ if (performance.now() - sliceStart >= this._buildSliceMs) {
215
+ await yieldToEventLoop();
216
+ if (epoch !== this._indexEpoch) {
217
+ return;
218
+ }
219
+ sliceStart = performance.now();
220
+ }
192
221
  }
193
222
  }
223
+
224
+ this.posts = next.posts;
225
+ this.pages = next.pages;
226
+ this.tags = next.tags;
227
+ this.users = this.authors = next.authors;
228
+ // The index generator holds references to the generators it counts.
229
+ this.index = this.createIndexGenerator(this._options, next);
194
230
  this._indexBuilt = true;
195
231
  }
196
232
 
197
233
  _invalidateIndex() {
198
234
  this._indexBuilt = false;
199
235
  this._indexEpoch += 1;
236
+ // The index generator's cached xml is valid exactly while _indexBuilt
237
+ // is, so the two are dropped together.
238
+ this.index.reset();
200
239
  }
201
240
 
202
241
  /**
203
- * Add a single resource to the index.
242
+ * Add a single resource to a set of generators being built.
204
243
  */
205
- _applyResource(type, datum) {
244
+ _applyResource(generators, type, datum) {
206
245
  const url = this._getUrlService().getUrlForResource({ ...datum, type }, { absolute: true });
207
246
  // Exact match on the not-found sentinel: a real resource can carry
208
247
  // a slug like "404" (/tag/404/) and must stay in the sitemap.
209
248
  if (url && url !== this._notFoundUrl()) {
210
- this[type].addUrl(url, datum);
249
+ generators[type].addUrl(url, datum);
211
250
  }
212
251
  }
213
252
 
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.escapeXml = escapeXml;
7
+ exports.getDeclarations = getDeclarations;
8
+ exports.renderUrlSet = renderUrlSet;
9
+ exports.renderSiteMapIndex = renderSiteMapIndex;
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const url_utils_1 = __importDefault(require("../../../shared/url-utils"));
12
+ // Ghost renders exactly two sitemap documents, both with a shape the
13
+ // sitemaps.org schema fixes: a <urlset> per resource type, and the
14
+ // <sitemapindex> that lists them. They are emitted here directly rather than
15
+ // through a generic xml library — for a shape this fixed a library spends
16
+ // several times as long walking an object tree as writing the tags costs, and
17
+ // keeping both documents in one module keeps escaping in one auditable place.
18
+ const URLSET_OPEN = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' +
19
+ ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">';
20
+ const URLSET_CLOSE = '</urlset>';
21
+ const SITEMAPINDEX_OPEN = '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
22
+ const SITEMAPINDEX_CLOSE = '</sitemapindex>';
23
+ // The characters the xml package escaped in text content, kept character for
24
+ // character so the rendered sitemaps do not change.
25
+ const XML_ESCAPES = {
26
+ '&': '&amp;',
27
+ '"': '&quot;',
28
+ "'": '&apos;',
29
+ '<': '&lt;',
30
+ '>': '&gt;',
31
+ };
32
+ function escapeXml(value) {
33
+ return value.replace(/[&"'<>]/g, (char) => XML_ESCAPES[char]);
34
+ }
35
+ function getDeclarations() {
36
+ const baseUrl = url_utils_1.default.urlFor('sitemap_xsl', true).replace(/^(http:|https:)/, '');
37
+ return ('<?xml version="1.0" encoding="UTF-8"?>' +
38
+ '<?xml-stylesheet type="text/xsl" href="' +
39
+ baseUrl +
40
+ '"?>');
41
+ }
42
+ /**
43
+ * The <urlset> document for one page of one resource type.
44
+ *
45
+ * @param entries in the order they should appear
46
+ */
47
+ function renderUrlSet(entries) {
48
+ // Joined rather than concatenated: join returns a flat string, where
49
+ // repeated concatenation leaves a rope whose fragments are held for as long
50
+ // as the cached sitemap is.
51
+ const parts = [getDeclarations(), URLSET_OPEN];
52
+ for (const entry of entries) {
53
+ parts.push('<url><loc>', escapeXml(entry.loc), '</loc><lastmod>', new Date(entry.ts).toISOString(), '</lastmod>');
54
+ if (entry.imageLoc) {
55
+ parts.push('<image:image><image:loc>', escapeXml(entry.imageLoc), '</image:loc><image:caption>', escapeXml(node_path_1.default.basename(entry.imageLoc)), '</image:caption></image:image>');
56
+ }
57
+ parts.push('</url>');
58
+ }
59
+ parts.push(URLSET_CLOSE);
60
+ return parts.join('');
61
+ }
62
+ /**
63
+ * The <sitemapindex> document listing every page of every resource type.
64
+ */
65
+ function renderSiteMapIndex(entries) {
66
+ const parts = [getDeclarations(), SITEMAPINDEX_OPEN];
67
+ for (const entry of entries) {
68
+ parts.push('<sitemap><loc>', escapeXml(entry.loc), '</loc><lastmod>', new Date(entry.ts).toISOString(), '</lastmod></sitemap>');
69
+ }
70
+ parts.push(SITEMAPINDEX_CLOSE);
71
+ return parts.join('');
72
+ }