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.
- package/bin/create-migration.js +15 -0
- package/components/tryghost-adapter-base-scheduling-0.2.4.tgz +0 -0
- package/components/tryghost-api-framework-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.6.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.1.tgz +0 -0
- package/components/tryghost-metafield-types-0.0.0.tgz +0 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/css/screen.css +15 -2
- package/content/themes/casper/assets/js/init.js +37 -9
- package/content/themes/casper/package.json +8 -8
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/source.js +1 -1
- package/content/themes/source/assets/css/screen.css +14 -1
- package/content/themes/source/assets/js/main.js +24 -4
- package/content/themes/source/package.json +7 -7
- package/core/boot.js +22 -16
- package/core/bridge.js +1 -1
- package/core/built/admin/assets/Combination-DQ6f9A5P.js +41 -0
- package/core/built/admin/assets/{PolarAngleAxis-a81ieQUO.js → PolarAngleAxis-BBaU7PRV.js} +1 -1
- package/core/built/admin/assets/RadialBarChart-Bp20JPnA.js +1 -0
- package/core/built/admin/assets/{about-39CAfroa.js → about-Dp43j18M.js} +1 -1
- package/core/built/admin/assets/accordion-h62UzFhj.js +1 -0
- package/core/built/admin/assets/account-migration-CBTbtX6Q.js +1 -0
- package/core/built/admin/assets/action-list-DC9QpX-l.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-KA6Jjf9L.js → account-migration-B1DUZS4Q.js} +8 -8
- package/core/built/admin/assets/activitypub/activitypub.js +24 -11
- package/core/built/admin/assets/activitypub/avatar-CRGud8yf.js +153 -0
- package/core/built/admin/assets/activitypub/{avatar-flipboard-csQaNeYK.js → avatar-flipboard-BZNFAXfm.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-C7nTDMvw.js → bluesky-sharing-Cg_v-LwS.js} +30 -31
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-Ck3O4oo2.js → chunk-KS7C4IRE-CTUL98Lc.js} +2498 -1885
- package/core/built/admin/assets/activitypub/{content-formatters-CLcjaBwV.js → content-formatters-D7GWP7nH.js} +23 -24
- package/core/built/admin/assets/activitypub/{copy-DcOh8kXw.js → copy-CgzTvoeR.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-fZ-X8i4-.js → domain-C3G_nkHU.js} +46 -46
- package/core/built/admin/assets/activitypub/{edit-profile-BqMLQV88.js → edit-profile-C8cCY-AK.js} +927 -928
- package/core/built/admin/assets/activitypub/{explore-B4yPoP8E.js → explore-NlDT4inq.js} +11 -11
- package/core/built/admin/assets/activitypub/{feed-BRC_mvAe.js → feed-DF22jCTV.js} +10 -10
- package/core/built/admin/assets/activitypub/{field-COacNgOt.js → field-95-iuTte.js} +4 -4
- package/core/built/admin/assets/activitypub/{hash-C7u-irQQ.js → hash-R7LMLnhY.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-CbTTdDQ3.js → inbox-DUJUiV6x.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-BW-yvUoN.js → inbox-DerExBzA.js} +416 -414
- package/core/built/admin/assets/activitypub/{label-kPMmYI7l.js → label-EwCn1QYc.js} +4 -4
- package/core/built/admin/assets/activitypub/layout-Dy6NnW1G.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-CuidJzc0.js → moderation-BSkfiVYo.js} +73 -75
- package/core/built/admin/assets/activitypub/{note-CAMRRSqD.js → note-DOnAJTXK.js} +9 -9
- package/core/built/admin/assets/activitypub/{notifications-BmNlXGgp.js → notifications-DZc6XTjG.js} +27 -27
- package/core/built/admin/assets/activitypub/{onboarding-Dmun9jr_.js → onboarding-8vEIwYTH.js} +3 -3
- package/core/built/admin/assets/activitypub/{onboarding-Bqg7fh_L.js → onboarding-DYVqbNmp.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences--oH19wf9.js → preferences-DDok6F2N.js} +1418 -1390
- package/core/built/admin/assets/activitypub/{profile-DH_tQx2D.js → profile-DjVa2hUk.js} +254 -255
- package/core/built/admin/assets/activitypub/{reply-Cap9PxSH.js → reply-DtXBpHxu.js} +2 -2
- package/core/built/admin/assets/activitypub/routes-Cw302e1c.js +4946 -0
- package/core/built/admin/assets/activitypub/{separator-CAxxu8VV.js → separator-FpRpVDd3.js} +8 -9
- package/core/built/admin/assets/activitypub/settings-yK-puALn.js +314 -0
- package/core/built/admin/assets/activitypub/{site-BYps63oh.js → site-CHw0R2PA.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-CKjR2KAm.js → step-1-737bnvxk.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-2-B6PKfOUQ.js → step-2-DBaIsAsM.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-C-qBbcUo.js → step-3-BBmy9m9V.js} +23 -23
- package/core/built/admin/assets/activitypub/tabs-LQ2xirdm.js +1101 -0
- package/core/built/admin/assets/activitypub/{topic-filter-DsUOuu0k.js → topic-filter-75Nu0CKj.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CX9snsLs.js → use-activity-pub-queries-CYI2QJMs.js} +58 -58
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-DsJTGDSS.js → use-navigate-with-base-path-B4jZ8hyg.js} +3924 -1403
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-AJi9zkzm.js → use-reply-chain-data-BHmFxc3U.js} +6 -6
- package/core/built/admin/assets/activitypub/users-CSjOwpT9.js +886 -0
- package/core/built/admin/assets/activitypub/{x-DR8AT3-4.js → x-B9ptWCds.js} +2 -2
- package/core/built/admin/assets/{add-integration-modal-B12oRifD.js → add-integration-modal-BwePhq95.js} +1 -1
- package/core/built/admin/assets/add-newsletter-modal-Zv0I7Msm.js +1 -0
- package/core/built/admin/assets/{add-recommendation-modal-CJf92edY.js → add-recommendation-modal-cdLv0wHy.js} +1 -1
- package/core/built/admin/assets/analytics-DY8_SaP7.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-C-2yWwag.js +1 -0
- package/core/built/admin/assets/api-keys-BHKATUqQ.js +1 -0
- package/core/built/admin/assets/{audience-6GXevic-.js → audience-wRcSN0jV.js} +1 -1
- package/core/built/admin/assets/{automated-emails-DJQ4uNZo.js → automated-emails-CzUa3v4d.js} +1 -1
- package/core/built/admin/assets/automations-DupSsSOr.js +1 -0
- package/core/built/admin/assets/badge-CI1Z0xyG.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-B8PC1SBj.js +1 -0
- package/core/built/admin/assets/box-LUuYRT-1.js +1 -0
- package/core/built/admin/assets/brand-icon-CrmbX86T.js +1 -0
- package/core/built/admin/assets/breadcrumb-DDDpzLyo.js +1 -0
- package/core/built/admin/assets/button-D414TI3B.js +1 -0
- package/core/built/admin/assets/calendar-Cp_opo3i.js +1 -0
- package/core/built/admin/assets/card-C2dfd5u0.js +1 -0
- package/core/built/admin/assets/chart-B_UNLXse.js +63 -0
- package/core/built/admin/assets/chart-helpers-Czu_9QpJ.js +1 -0
- package/core/built/admin/assets/checkbox-CFFqf0Nh.js +1 -0
- package/core/built/admin/assets/{chunk.368.22ce1b732c0b40abd3f1.js → chunk.201.6961b57ed7a281ac193e.js} +23 -19
- package/core/built/admin/assets/chunk.524.0b850e72ec49a0fa3882.js +25 -0
- package/core/built/admin/assets/chunk.582.db2ae9e968256e8d58ac.js +27 -0
- package/core/built/admin/assets/code-editor-view-Bd_nnHKk.js +1 -0
- package/core/built/admin/assets/code-editor-view-BeU3Yf9M.js +1 -0
- package/core/built/admin/assets/color-picker-Bzm1V7qg.js +3 -0
- package/core/built/admin/assets/color-picker-field-DP0moHz9.js +1 -0
- package/core/built/admin/assets/combobox-6_ruuTuj.js +1 -0
- package/core/built/admin/assets/command-B5wfm6pd.js +1 -0
- package/core/built/admin/assets/comments-BB26dUSl.js +1 -0
- package/core/built/admin/assets/confirmation-context-znVJD4pX.js +1 -0
- package/core/built/admin/assets/container-CHHif8km.js +1 -0
- package/core/built/admin/assets/{content-api-modal-0ZjuLcQ8.js → content-api-modal-DOHwl0yZ.js} +1 -1
- package/core/built/admin/assets/context-menu-BpY46Dlb.js +1 -0
- package/core/built/admin/assets/copy-field-DRBx6Ady.js +1 -0
- package/core/built/admin/assets/create-ghost-browse-value-source-eIXo5Qws.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-DrSlETOI.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-Dgo425Ec.js +1 -0
- package/core/built/admin/assets/data-list-DM8yqpXi.js +1 -0
- package/core/built/admin/assets/date-range-select-DdC23c13.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-CbSu9IT4.js +2 -0
- package/core/built/admin/assets/detail-page-CN4NdhpV.js +1 -0
- package/core/built/admin/assets/dirty-confirm-dialog-BT_Vq9TD.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-CLZhMv8J.js → disabled-sources-indicator-C5xtW0Q2.js} +1 -1
- package/core/built/admin/assets/dist-B40T-RCQ.js +1 -0
- package/core/built/admin/assets/dist-CrlaEoaK.js +1 -0
- package/core/built/admin/assets/domain-BPReCZa8.js +1 -0
- package/core/built/admin/assets/dropzone-B2auMkf2.js +5 -0
- package/core/built/admin/assets/edit-profile-B7XTBzKe.js +1 -0
- package/core/built/admin/assets/editor-BcaC9dqm.js +8 -0
- package/core/built/admin/assets/editor-screen-FuMpItC8.js +27 -0
- package/core/built/admin/assets/email-sending-status-provider-9d81o1C2.js +1 -0
- package/core/built/admin/assets/embed-signup-form-modal-CbVbMsxl.js +8 -0
- package/core/built/admin/assets/empty-indicator-CSQts7GV.js +1 -0
- package/core/built/admin/assets/explore-DbSXFTJN.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-DFXA4vfJ.js +1 -0
- package/core/built/admin/assets/feed-W7RlF-Gp.js +1 -0
- package/core/built/admin/assets/field-BXEh3ptU.js +1 -0
- package/core/built/admin/assets/filter-sources-DHK2IoCk.js +1 -0
- package/core/built/admin/assets/filters-0SYNTqyQ.js +1 -0
- package/core/built/admin/assets/filters-DTtZKQyq.js +1 -0
- package/core/built/admin/assets/{first-promoter-modal-wr73Dldc.js → first-promoter-modal-DcoN_OXu.js} +1 -1
- package/core/built/admin/assets/{ghost-a1e6e172c6917e8ef8fa6b0007756d9b.js → ghost-25495154132af7305cb7b646fd83f48f.js} +562 -514
- package/core/built/admin/assets/gift-link-modal-BX-Q1TOk.js +1 -0
- package/core/built/admin/assets/grid-BEUIDQX7.js +1 -0
- package/core/built/admin/assets/growth-G6PLO4aF.js +1 -0
- package/core/built/admin/assets/growth-tkD-fBOY.js +3 -0
- package/core/built/admin/assets/heading-ih-fOVqF.js +1 -0
- package/core/built/admin/assets/history-modal-Ca-gStoq.js +1 -0
- package/core/built/admin/assets/html-field-B3CrKV9Y.js +1 -0
- package/core/built/admin/assets/images-H-BIm8OQ.js +1 -0
- package/core/built/admin/assets/{inbox-DgSZ6w2p.js → inbox-DBbDVMPz.js} +14 -14
- package/core/built/admin/assets/index-DOF2jDq0.js +4 -0
- package/core/built/admin/assets/index-JrrNchW1.css +2 -0
- package/core/built/admin/assets/input-group-CvH-3d3F.js +1 -0
- package/core/built/admin/assets/input-surface-CL23Mlh8.js +1 -0
- package/core/built/admin/assets/integration-header-C7GXGis_.js +1 -0
- package/core/built/admin/assets/invite-user-modal-EJ2_6RpV.js +1 -0
- package/core/built/admin/assets/isPlainObject-BzzgUf1E.js +1 -0
- package/core/built/admin/assets/{koenig-lexical-CXVgQFUk.js → koenig-lexical-Dkwee5FG.js} +1 -1
- package/core/built/admin/assets/koenig-loader-BzXCXHoA.js +2 -0
- package/core/built/admin/assets/kpi-card-iCDUTgOR.js +1 -0
- package/core/built/admin/assets/kpi-card-jTnpLKGr.js +1 -0
- package/core/built/admin/assets/kpi-helpers-Criu0YAf.js +1 -0
- package/core/built/admin/assets/kpi-tabs-DbOmmyOo.js +1 -0
- package/core/built/admin/assets/label-Du0yqMqL.js +1 -0
- package/core/built/admin/assets/label-picker-Bq1XXr1D.js +1 -0
- package/core/built/admin/assets/{labels-KMkFoRG6.js → labels-0x2NfLtC.js} +1 -1
- package/core/built/admin/assets/layout-Ckwzsnsu.js +1 -0
- package/core/built/admin/assets/lib-CCz2mjm8.js +2 -0
- package/core/built/admin/assets/link-helpers-o9GgBTni.js +1 -0
- package/core/built/admin/assets/{links-FwNCVyVe.js → links-Ctujreuh.js} +1 -1
- package/core/built/admin/assets/list-page-CH_wnDC3.js +1 -0
- package/core/built/admin/assets/member-activity-CsAh6MxK.js +1 -0
- package/core/built/admin/assets/member-activity-feed-0ytsQbtY.js +1 -0
- package/core/built/admin/assets/member-custom-fields-CTFGnnYv.js +1 -0
- package/core/built/admin/assets/member-detail-QU3vB1ML.js +1 -0
- package/core/built/admin/assets/members-CtMVO_AB.js +4 -0
- package/core/built/admin/assets/modal-page-8Ipfl51B.js +1 -0
- package/core/built/admin/assets/moderation-B8VtA-W0.js +1 -0
- package/core/built/admin/assets/multi-select-combobox-_9wEYmGv.js +1 -0
- package/core/built/admin/assets/navigation-modal-CGcjJvkc.js +1 -0
- package/core/built/admin/assets/newsletter-BpHjjqmq.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-DzTbGOyo.js +1 -0
- package/core/built/admin/assets/newsletter-emails-BMKH4Osp.js +1 -0
- package/core/built/admin/assets/newsletters-BxC84_cL.js +1 -0
- package/core/built/admin/assets/note-BPtEzzZS.js +1 -0
- package/core/built/admin/assets/notifications-ng3ulEdp.js +1 -0
- package/core/built/admin/assets/offers-container-modal-CwqnRa3T.js +1 -0
- package/core/built/admin/assets/onboarding-route-CplZzd7F.js +1 -0
- package/core/built/admin/assets/overview-B0yD9OoX.js +1 -0
- package/core/built/admin/assets/overview-C4eJzQqi.js +1 -0
- package/core/built/admin/assets/page-header-CdCpekG9.js +1 -0
- package/core/built/admin/assets/pagemenu-CKQD9hoP.js +1 -0
- package/core/built/admin/assets/pages-route-CKsnxIvw.js +1 -0
- package/core/built/admin/assets/{pintura-modal-BljM7hf8.js → pintura-modal-C5jzeyGk.js} +1 -1
- package/core/built/admin/assets/portal-frame-DYo0bh0E.js +1 -0
- package/core/built/admin/assets/portal-modal-C0d8xjKa.js +1 -0
- package/core/built/admin/assets/post-analytics-C5C7IyAm.js +1 -0
- package/core/built/admin/assets/post-analytics-header-DKWHwoK2.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-CgreZDjS.js → post-analytics-provider-BqCPPxmW.js} +1 -1
- package/core/built/admin/assets/post-share-modal-wxmqbAv_.js +1 -0
- package/core/built/admin/assets/posts-list-screen-B978ZO0R.js +2 -0
- package/core/built/admin/assets/posts-route-CDqxKazl.js +1 -0
- package/core/built/admin/assets/preferences-CnejTRc4.js +5 -0
- package/core/built/admin/assets/preview-chrome-C7yZWVLZ.js +1 -0
- package/core/built/admin/assets/preview-modal-CFUxUGHp.js +1 -0
- package/core/built/admin/assets/profile-ClRGiOqA.js +1 -0
- package/core/built/admin/assets/radio-group-CTmLo0h9.js +1 -0
- package/core/built/admin/assets/router-provider-DDZvgS3V.js +1 -0
- package/core/built/admin/assets/select-D_4xWFYv.js +1 -0
- package/core/built/admin/assets/settings-BRl7-7a9.js +1 -0
- package/core/built/admin/assets/settings-BoQcPa-y.js +4 -0
- package/core/built/admin/assets/settings-breadcrumbs-C9Yxl81q.js +1 -0
- package/core/built/admin/assets/{dist-BXHrDV19.js → shade-scope-Cccj-c7I-D0XwkdKQ.js} +5 -3
- package/core/built/admin/assets/share-modal-DBqZobuR.js +1 -0
- package/core/built/admin/assets/simple-pagination-D5t_ox6x.js +1 -0
- package/core/built/admin/assets/skeleton-CPcTiTSX.js +1 -0
- package/core/built/admin/assets/{slack-modal-D7W_fNON.js → slack-modal-BF9ex8cd.js} +1 -1
- package/core/built/admin/assets/sort-button-C0EXGjnS.js +1 -0
- package/core/built/admin/assets/{source-icon-CQboPZQ3.js → source-icon-BzzftCQ2.js} +1 -1
- package/core/built/admin/assets/stats-view-CzpGoHzF.js +1 -0
- package/core/built/admin/assets/{step-1-BKhjy0PV.js → step-1-CBUYUPPF.js} +1 -1
- package/core/built/admin/assets/{step-2-PxA1OM7R.js → step-2-CEIXUwQa.js} +1 -1
- package/core/built/admin/assets/{step-3-BJbAiq16.js → step-3-C1HA5i1N.js} +2 -2
- package/core/built/admin/assets/stripe-button-DMFsLs80.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-CEvxCUTv.js +1 -0
- package/core/built/admin/assets/switch-DoOJF2yg.js +1 -0
- package/core/built/admin/assets/table-BDYbUHJz.js +1 -0
- package/core/built/admin/assets/tabs-BZyz4ZCH.js +1 -0
- package/core/built/admin/assets/tag-detail-CFzaz_9E.js +2 -0
- package/core/built/admin/assets/tag-picker-BKUCZuRc.js +1 -0
- package/core/built/admin/assets/tags-Bs1FiD7m.js +1 -0
- package/core/built/admin/assets/{testimonials-modal-Dkt6ODLW.js → testimonials-modal-CVm0_7E-.js} +1 -1
- package/core/built/admin/assets/text-BLI0C8Mc.js +1 -0
- package/core/built/admin/assets/textarea-C5qolByg.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-Cmwzi-e7.js +1 -0
- package/core/built/admin/assets/toggle-group-BTL1QsWY.js +1 -0
- package/core/built/admin/assets/{topic-filter-DVOe3D-b.js → topic-filter-BMjhquv7.js} +1 -1
- package/core/built/admin/assets/transistor-modal-hMv53jF8.js +1 -0
- package/core/built/admin/assets/trend-badge-1w_HEzkG.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-DXt7Brum.js → unsplash-modal-jzC8lEB2.js} +1 -1
- package/core/built/admin/assets/use-analytics-data-B3lGOxsC.js +1 -0
- package/core/built/admin/assets/use-availability-DnKqOyqd.js +1 -0
- package/core/built/admin/assets/{use-check-theme-limit-error-JD-J5aaZ.js → use-check-theme-limit-error-C1tc2P4N.js} +2 -2
- package/core/built/admin/assets/use-filter-params-DU-aiaTb.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-CEMSAYPg.js → use-filterable-api-CxCrnPxG.js} +1 -1
- package/core/built/admin/assets/use-growth-stats-E13rMQUE.js +1 -0
- package/core/built/admin/assets/use-limiter-DlL7au90.js +19 -0
- package/core/built/admin/assets/{use-navigate-with-base-path-88EQpw2k.js → use-navigate-with-base-path-BzgNwMAx.js} +1 -1
- package/core/built/admin/assets/{use-pintura-config-BmU45Ruy.js → use-pintura-config-CtEV1JQY.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-Bz2bZ4iT.js → use-pintura-editor-CaxE64kD.js} +1 -1
- package/core/built/admin/assets/use-reply-chain-data-BST2OgCM.js +1 -0
- package/core/built/admin/assets/use-sending-eta-DR9ueMH8.js +1 -0
- package/core/built/admin/assets/use-setting-group-ldtX1Vsk.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-CzGU4sus.js +1 -0
- package/core/built/admin/assets/use-staff-users-DoeXaPsb.js +1 -0
- package/core/built/admin/assets/use-tinybird-query-BaBNCaj0.js +1 -0
- package/core/built/admin/assets/{use-tinybird-token-Lk1RSn0t.js → use-tinybird-token-CnZ19NDf.js} +1 -1
- package/core/built/admin/assets/{use-url-input-DpmbDppC.js → use-url-input-C5KHJw79.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-Dm0KfPeR.js +1 -0
- package/core/built/admin/assets/{vendor-2dd263291938d407802568d3357ec69e.js → vendor-97e91ff8dd1457c668f9ef103ee1a77b.js} +2748 -2977
- package/core/built/admin/assets/virtual-list-tAuPUICw.js +1 -0
- package/core/built/admin/assets/web-BQlKcW2x.js +2 -0
- package/core/built/admin/assets/web-NTT5DkEz.js +1 -0
- package/core/built/admin/assets/{zapier-modal-BafLUO4R.js → zapier-modal-DsHG9Oev.js} +1 -1
- package/core/built/admin/index.html +22 -24
- package/core/frontend/apps/private-blogging/index.js +2 -2
- package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
- package/core/frontend/apps/private-blogging/lib/router.js +1 -1
- package/core/frontend/helpers/cancel_link.js +3 -3
- package/core/frontend/helpers/content.js +2 -2
- package/core/frontend/helpers/content_api_url.js +1 -1
- package/core/frontend/helpers/date.js +1 -1
- package/core/frontend/helpers/foreach.js +5 -5
- package/core/frontend/helpers/get.js +7 -4
- package/core/frontend/helpers/ghost_head.js +2 -2
- package/core/frontend/helpers/has.js +1 -3
- package/core/frontend/helpers/link.js +7 -7
- package/core/frontend/helpers/link_class.js +2 -2
- package/core/frontend/helpers/navigation.js +1 -2
- package/core/frontend/helpers/recommendations.js +5 -6
- package/core/frontend/helpers/tiers.js +1 -1
- package/core/frontend/helpers/total_members.js +1 -1
- package/core/frontend/helpers/total_paid_members.js +1 -1
- package/core/frontend/meta/author-url.js +1 -1
- package/core/frontend/meta/modified-date.js +1 -1
- package/core/frontend/meta/published-date.js +1 -1
- package/core/frontend/meta/schema.js +3 -8
- package/core/frontend/meta/structured-data.js +1 -2
- package/core/frontend/services/assets-minification/minifier.js +1 -1
- package/core/frontend/services/data/entry-lookup.js +1 -1
- package/core/frontend/services/data/fetch-data.js +2 -2
- package/core/frontend/services/rendering/format-response.js +1 -1
- package/core/frontend/services/rendering/renderer.js +3 -0
- package/core/frontend/services/rendering/templates.js +1 -1
- package/core/frontend/services/routing/controllers/static.js +1 -1
- package/core/frontend/services/routing/parent-router.js +1 -1
- package/core/frontend/services/routing/registry.js +1 -1
- package/core/frontend/services/sitemap/base-site-map-generator.js +68 -97
- package/core/frontend/services/sitemap/site-map-index-generator.js +16 -21
- package/core/frontend/services/sitemap/site-map-manager.js +82 -43
- package/core/frontend/services/sitemap/sitemap-xml.js +72 -0
- package/core/frontend/services/theme-engine/handlebars/utils.js +8 -8
- package/core/frontend/services/theme-engine/i18n/i18n.js +4 -5
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +1 -1
- package/core/frontend/services/theme-engine/preview.js +1 -1
- package/core/frontend/utils/member-count.js +2 -2
- package/core/frontend/web/routers/serve-public-file.js +1 -1
- package/core/server/adapters/scheduling/README.md +4 -0
- package/core/server/adapters/scheduling/get-scheduler-idempotency-key.js +21 -0
- package/core/server/adapters/storage/LocalStorageBase.js +2 -1
- package/core/server/api/endpoints/automated-emails.js +8 -2
- package/core/server/api/endpoints/db.js +1 -1
- package/core/server/api/endpoints/emails.js +3 -3
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/member-signin-urls.js +1 -1
- package/core/server/api/endpoints/members.js +3 -3
- package/core/server/api/endpoints/oembed.js +1 -1
- package/core/server/api/endpoints/posts.js +1 -1
- package/core/server/api/endpoints/settings.js +2 -2
- package/core/server/api/endpoints/themes.js +5 -5
- package/core/server/api/endpoints/utils/serializers/input/pages.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +8 -5
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/pages.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/posts.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/search-index.js +11 -11
- package/core/server/api/endpoints/utils/serializers/output/settings.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/utils/extra-attrs.js +3 -3
- package/core/server/api/endpoints/utils/validators/input/automated_emails.js +23 -33
- package/core/server/api/endpoints/utils/validators/input/settings.js +3 -3
- package/core/server/data/exporter/exporter.js +1 -2
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/importer/handlers/image.js +1 -2
- package/core/server/data/importer/handlers/json.js +1 -3
- package/core/server/data/importer/handlers/markdown.js +4 -8
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/content-file-importer.js +4 -8
- package/core/server/data/importer/importers/data/base.js +10 -10
- package/core/server/data/importer/importers/data/custom-theme-settings-importer.js +1 -1
- package/core/server/data/importer/importers/data/data-importer.js +3 -4
- package/core/server/data/importer/importers/data/posts-importer.js +10 -10
- package/core/server/data/importer/importers/data/products-importer.js +3 -3
- package/core/server/data/importer/importers/data/settings-importer.js +2 -2
- package/core/server/data/importer/importers/data/stripe-prices-importer.js +1 -1
- package/core/server/data/importer/importers/data/stripe-products-importer.js +1 -1
- package/core/server/data/importer/importers/data/tags-importer.js +1 -1
- package/core/server/data/importer/importers/data/users-importer.js +2 -2
- package/core/server/data/importer/importers/json-to-html.js +12 -9
- package/core/server/data/migrations/versions/5.0/2022-05-11-13-12-rename-settings.js +1 -1
- package/core/server/data/migrations/versions/6.65/2026-09-16-15-35-26-add-members-metafield-change-events.js +17 -0
- package/core/server/data/schema/fixtures/fixture-manager.js +3 -3
- package/core/server/data/schema/index.js +1 -1
- package/core/server/data/schema/schema.js +26 -0
- package/core/server/data/schema/validator.js +96 -123
- package/core/server/data/seeders/importers/email-recipients-importer.js +2 -2
- package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +2 -2
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
- package/core/server/data/tinybird/pipes/mv_hits.pipe +25 -20
- package/core/server/data/tinybird/scripts/docker-analytics-manager.js +2 -2
- package/core/server/ghost-server.js +1 -19
- package/core/server/lib/image/image-size.js +1 -3
- package/core/server/lib/request-external.js +53 -117
- package/core/server/models/base/plugins/actions.js +2 -3
- package/core/server/models/base/plugins/crud.js +1 -2
- package/core/server/models/base/plugins/events.js +4 -4
- package/core/server/models/base/plugins/raw-knex.js +3 -3
- package/core/server/models/base/plugins/sanitize.js +1 -1
- package/core/server/models/benefit.js +1 -1
- package/core/server/models/collection.js +1 -1
- package/core/server/models/comment.js +3 -3
- package/core/server/models/invite.js +2 -5
- package/core/server/models/label.js +3 -6
- package/core/server/models/member.js +7 -10
- package/core/server/models/newsletter.js +1 -1
- package/core/server/models/permission.js +3 -6
- package/core/server/models/post.js +20 -23
- package/core/server/models/product.js +4 -1
- package/core/server/models/relations/authors.js +9 -12
- package/core/server/models/role.js +2 -5
- package/core/server/models/settings.js +3 -3
- package/core/server/models/tag.js +2 -5
- package/core/server/models/user.js +8 -10
- package/core/server/models/webhook.js +2 -4
- package/core/server/notify.js +2 -2
- package/core/server/services/adapter-manager/utils.js +6 -1
- package/core/server/services/api-version-compatibility/legacy-api-path-match.js +2 -2
- package/core/server/services/auth/api-key/admin.js +2 -2
- package/core/server/services/auth/api-key/content.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -3
- package/core/server/services/automations/README.md +19 -0
- package/core/server/services/automations/poll.js +3 -1
- package/core/server/services/automations/service.js +2 -2
- package/core/server/services/automations/welcome-email-automation-poll.js +199 -293
- package/core/server/services/comments/comments-controller.js +9 -30
- package/core/server/services/comments/comments-service-email-renderer.js +1 -1
- package/core/server/services/comments/comments-service-emails.js +1 -1
- package/core/server/services/comments/comments-service.js +27 -58
- package/core/server/services/comments/comments-stats-service.js +1 -1
- package/core/server/services/content-import/import/staged-file.js +9 -9
- package/core/server/services/content-import/index.js +1 -1
- package/core/server/services/custom-redirects/validation.js +1 -1
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +23 -55
- package/core/server/services/email-analytics/index.js +35 -19
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.js +31 -60
- package/core/server/services/email-service/README.md +35 -0
- package/core/server/services/email-service/batch-sending-service.js +13 -14
- package/core/server/services/email-service/email-renderer.js +50 -14
- package/core/server/services/email-service/email-service-wrapper.js +3 -2
- package/core/server/services/email-service/email-service.js +33 -8
- package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +2 -1
- package/core/server/services/email-service/email-templates/partials/styles.hbs +0 -6
- package/core/server/services/email-service/email-templates/template.hbs +1 -1
- package/core/server/services/email-service/jobs/send-email-job.js +12 -0
- package/core/server/services/email-service/mailgun-email-provider.js +7 -2
- package/core/server/services/email-service/sending-service.js +1 -1
- package/core/server/services/gifts/gift-service.js +12 -3
- package/core/server/services/gifts/index.js +0 -2
- package/core/server/services/gifts/jobs/index.js +12 -24
- package/core/server/services/gifts/jobs/send-gift-reminders-job.js +6 -46
- package/core/server/services/invitations/accept.js +2 -2
- package/core/server/services/jobs/job-service.js +0 -28
- package/core/server/services/jobs-service/jobs-service.js +5 -0
- package/core/server/services/jobs-service/register-job-handlers.js +17 -1
- package/core/server/services/lib/mailgun-client.js +16 -1
- package/core/server/services/lib/mailgun-recipient-message-id.js +49 -0
- package/core/server/services/limits.js +1 -1
- package/core/server/services/mail/ghost-mailer.js +1 -1
- package/core/server/services/member-attribution/member-attribution-service.js +1 -1
- package/core/server/services/member-attribution/referrer-translator.js +1 -1
- package/core/server/services/members/account-service.js +3 -3
- package/core/server/services/members/api.js +1 -1
- package/core/server/services/members/import-export/import/importer.js +25 -16
- package/core/server/services/members/import-export/import/spool.js +11 -15
- package/core/server/services/members/import-export/index.js +6 -4
- package/core/server/services/members/jobs/index.js +1 -1
- package/core/server/services/members/jobs/members-import-job.js +18 -0
- package/core/server/services/members/member-avatar.js +18 -0
- package/core/server/services/members/members-api/controllers/member-controller.js +2 -2
- package/core/server/services/members/members-api/members-api.js +4 -4
- package/core/server/services/members/members-api/repositories/event-repository.js +36 -1
- package/core/server/services/members/members-api/repositories/member-repository.js +55 -17
- package/core/server/services/members/members-api/repositories/product-repository.js +1 -1
- package/core/server/services/members/members-api/services/member-bread-service.js +4 -7
- package/core/server/services/members/service.js +7 -2
- package/core/server/services/members/stats/members-stats.js +2 -2
- package/core/server/services/members-events/event-storage.js +2 -2
- package/core/server/services/members-metafields/actions.js +23 -2
- package/core/server/services/members-metafields/bindings-service.js +5 -5
- package/core/server/services/members-metafields/definitions-service.js +2 -7
- package/core/server/services/members-metafields/index.js +2 -1
- package/core/server/services/members-metafields/queries.js +7 -1
- package/core/server/services/members-metafields/schema.js +49 -1
- package/core/server/services/members-metafields/values-service.js +112 -10
- package/core/server/services/mentions/mention-sending-service.js +2 -2
- package/core/server/services/mentions/mention.js +3 -4
- package/core/server/services/mentions/mentions-api.js +4 -1
- package/core/server/services/mentions-jobs/job-service.js +0 -28
- package/core/server/services/milestones/milestones-service.js +1 -1
- package/core/server/services/newsletters/newsletters-service.js +1 -1
- package/core/server/services/notifications/notifications.js +3 -3
- package/core/server/services/oembed/oembed-service.js +79 -3
- package/core/server/services/permissions/can-this.js +1 -2
- package/core/server/services/post-scheduling/post-scheduling.js +8 -1
- package/core/server/services/posts/post-scheduling.js +7 -0
- package/core/server/services/settings/settings-bread-service.js +3 -3
- package/core/server/services/settings/settings-utils.js +1 -1
- package/core/server/services/settings-helpers/settings-helpers.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +9 -9
- package/core/server/services/stats/posts-stats-service.js +10 -10
- package/core/server/services/stats/referrers-stats-service.js +1 -1
- package/core/server/services/stats/subscription-stats-service.js +3 -3
- package/core/server/services/stats/utils/tinybird.js +8 -1
- package/core/server/services/stripe/stripe-api.js +3 -3
- package/core/server/services/stripe/stripe-migrations.js +6 -6
- package/core/server/services/themes/storage.js +21 -26
- package/core/server/services/themes/theme-storage.js +2 -2
- package/core/server/services/tiers/tier.js +15 -15
- package/core/server/services/tinybird-sync/index.js +3 -0
- package/core/server/services/tinybird-sync/tinybird-sync-service.js +7 -2
- package/core/server/services/update-check/update-check-service.js +3 -3
- package/core/server/services/webhooks/serialize.js +1 -1
- package/core/server/web/api/app.js +0 -5
- package/core/server/web/api/middleware/upload.js +29 -2
- package/core/server/web/parent/middleware/index.js +1 -1
- package/core/server/web/parent/middleware/log-request.js +5 -0
- package/core/server/web/parent/middleware/queue-request.js +104 -59
- package/core/server/web/shared/middleware/api/spam-prevention.js +2 -2
- package/core/shared/config/defaults.json +6 -1
- package/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js +1 -1
- package/core/shared/labs.js +3 -2
- package/core/shared/settings-cache/cache-manager.js +1 -1
- package/nodemon.json +1 -1
- package/package.json +6 -7
- package/pnpm-lock.yaml +126 -728
- package/pnpm-workspace.yaml +4 -7
- package/tsconfig.json +1 -1
- package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
- package/core/built/admin/assets/Combination-DWfJPcz9.js +0 -41
- package/core/built/admin/assets/RadialBarChart-CHRBhiBN.js +0 -1
- package/core/built/admin/assets/_baseAssignValue-DcPokYW8.js +0 -1
- package/core/built/admin/assets/accordion-CJmRaqK7.js +0 -1
- package/core/built/admin/assets/account-migration-DYf7skbb.js +0 -1
- package/core/built/admin/assets/action-list-Crnh-_UI.js +0 -1
- package/core/built/admin/assets/activitypub/avatar-DVJF5bxP.js +0 -261
- package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +0 -1
- package/core/built/admin/assets/activitypub/routes-DdyY9uFF.js +0 -7181
- package/core/built/admin/assets/activitypub/settings-B6EEcVkW.js +0 -315
- package/core/built/admin/assets/activitypub/tabs-DLhZtVde.js +0 -1083
- package/core/built/admin/assets/activitypub/users-inweyKL5.js +0 -1499
- package/core/built/admin/assets/add-newsletter-modal-CTvXiIqY.js +0 -1
- package/core/built/admin/assets/analytics-PHR1Fggi.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-Bql0CXdp.js +0 -1
- package/core/built/admin/assets/api-keys-CubH-7_H.js +0 -1
- package/core/built/admin/assets/automations-DcZSLFDU.js +0 -1
- package/core/built/admin/assets/badge-qP06OT7C.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-CJw4d4Mf.js +0 -1
- package/core/built/admin/assets/box-CZ_LSVSy.js +0 -1
- package/core/built/admin/assets/brand-icon-D3ffDYaC.js +0 -1
- package/core/built/admin/assets/breadcrumb-Chn1Ip_q.js +0 -1
- package/core/built/admin/assets/button-DvIh2Bq-.js +0 -1
- package/core/built/admin/assets/calendar-1QGoStFM.js +0 -1
- package/core/built/admin/assets/card-FHQ4D10L.js +0 -1
- package/core/built/admin/assets/chart-helpers-CNpA7re_.js +0 -1
- package/core/built/admin/assets/chart-jVz9Nf9t.js +0 -63
- package/core/built/admin/assets/checkbox-DKht9xMl.js +0 -1
- package/core/built/admin/assets/chunk.524.cc9be98e44890cedc8c8.js +0 -25
- package/core/built/admin/assets/chunk.582.e6fe742b3217207b11a6.js +0 -27
- package/core/built/admin/assets/code-editor-view-BLQIV85-.js +0 -1
- package/core/built/admin/assets/code-editor-view-Cg5eJxcr.js +0 -1
- package/core/built/admin/assets/color-picker-DQoghc0t.js +0 -3
- package/core/built/admin/assets/color-picker-field-BbeK5RRs.js +0 -1
- package/core/built/admin/assets/combobox-weO4FBbv.js +0 -1
- package/core/built/admin/assets/command-DlLrCm9V.js +0 -1
- package/core/built/admin/assets/comments-BjTitLmP.js +0 -1
- package/core/built/admin/assets/confirmation-context-IyqoXVk-.js +0 -1
- package/core/built/admin/assets/container-DAimjOF_.js +0 -1
- package/core/built/admin/assets/context-menu-BtioYFrg.js +0 -1
- package/core/built/admin/assets/copy-field-C6ujqJlP.js +0 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-DAZMWZNq.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-Dm8QI_J4.js +0 -1
- package/core/built/admin/assets/data-list-C0CKkeWW.js +0 -1
- package/core/built/admin/assets/date-range-select-DrjHQdgz.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-CXVOzBWu.js +0 -2
- package/core/built/admin/assets/detail-page-QpLIL-EO.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-DWwxREGh.js +0 -1
- package/core/built/admin/assets/dist-Bf-HQtVL.js +0 -1
- package/core/built/admin/assets/dist-CUwte9v_.js +0 -1
- package/core/built/admin/assets/dist-FEQQAwTe.js +0 -1
- package/core/built/admin/assets/domain-DBrBic6q.js +0 -1
- package/core/built/admin/assets/dropzone-D61QVcEY.js +0 -5
- package/core/built/admin/assets/edit-profile-jCqJBxqw.js +0 -1
- package/core/built/admin/assets/editor-8Fm4UBEd.js +0 -8
- package/core/built/admin/assets/editor-screen-BSX_uC2B.js +0 -27
- package/core/built/admin/assets/email-sending-status-provider-DnNhlIvt.js +0 -1
- package/core/built/admin/assets/embed-signup-form-modal-DqtBs1WE.js +0 -8
- package/core/built/admin/assets/empty-indicator-L4Ql5Db7.js +0 -1
- package/core/built/admin/assets/en-Cx75dJv5.js +0 -1
- package/core/built/admin/assets/explore-DXhDCtKw.js +0 -1
- package/core/built/admin/assets/feature-image-placeholder-e2q6TcH6.js +0 -1
- package/core/built/admin/assets/feed-mmt4noNL.js +0 -1
- package/core/built/admin/assets/field-p7KnP98B.js +0 -1
- package/core/built/admin/assets/filter-bar-CiIKBg3D.js +0 -1
- package/core/built/admin/assets/filter-sources-I-dRaQxl.js +0 -1
- package/core/built/admin/assets/filters-1injvyxJ.js +0 -1
- package/core/built/admin/assets/filters-DnpO6GWX.js +0 -1
- package/core/built/admin/assets/gift-link-modal-C3H7YFXO.js +0 -1
- package/core/built/admin/assets/grid-5vplBYri.js +0 -1
- package/core/built/admin/assets/growth-B1og1kQp.js +0 -1
- package/core/built/admin/assets/growth-Bf6OHZlK.js +0 -3
- package/core/built/admin/assets/heading-BB21QAbb.js +0 -1
- package/core/built/admin/assets/history-modal-t_qEnxPx.js +0 -1
- package/core/built/admin/assets/html-field-COLQbV4r.js +0 -1
- package/core/built/admin/assets/images-4mvoc3lB.js +0 -1
- package/core/built/admin/assets/index-B3HZU2i8.css +0 -2
- package/core/built/admin/assets/index-DDd-zhNL.js +0 -4
- package/core/built/admin/assets/inline-B8H6NzIw.js +0 -1
- package/core/built/admin/assets/input-group-CLIN2ykI.js +0 -1
- package/core/built/admin/assets/input-surface-wSO5CRy5.js +0 -1
- package/core/built/admin/assets/integration-header-CKGrXNVP.js +0 -1
- package/core/built/admin/assets/invite-user-modal-BaN2mdhI.js +0 -1
- package/core/built/admin/assets/koenig-loader-B1XPPY5C.js +0 -2
- package/core/built/admin/assets/kpi-card-CRLPVlFW.js +0 -1
- package/core/built/admin/assets/kpi-card-CRPbW8NZ.js +0 -1
- package/core/built/admin/assets/kpi-helpers-D7zT96bp.js +0 -1
- package/core/built/admin/assets/kpi-tabs-DE_yh9Go.js +0 -1
- package/core/built/admin/assets/label-CgeBxy4o.js +0 -1
- package/core/built/admin/assets/label-picker-DGDp4Vwr.js +0 -1
- package/core/built/admin/assets/layout-DWnOMly4.js +0 -1
- package/core/built/admin/assets/lib-_QYaUU89.js +0 -2
- package/core/built/admin/assets/link-helpers-8J7ixADV.js +0 -1
- package/core/built/admin/assets/list-page-DLMP0PxC.js +0 -1
- package/core/built/admin/assets/member-activity-ClMWFyvN.js +0 -1
- package/core/built/admin/assets/member-activity-feed-BQY5Rwaj.js +0 -1
- package/core/built/admin/assets/member-custom-fields-Dd_ie1zG.js +0 -1
- package/core/built/admin/assets/member-detail-DjwkGVne.js +0 -1
- package/core/built/admin/assets/members--N9lhk1N.js +0 -4
- package/core/built/admin/assets/modal-page-Dz-9TP4E.js +0 -1
- package/core/built/admin/assets/moderation-DfYBKq9s.js +0 -1
- package/core/built/admin/assets/multi-select-combobox-BcBEzUW_.js +0 -1
- package/core/built/admin/assets/navigation-modal-Cd-scywg.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-CFRY6vg_.js +0 -1
- package/core/built/admin/assets/newsletter-emails-4ntck1AC.js +0 -1
- package/core/built/admin/assets/newsletter-o6eCJtab.js +0 -1
- package/core/built/admin/assets/newsletters-Bmlv0edG.js +0 -1
- package/core/built/admin/assets/note-CWs3LF-0.js +0 -1
- package/core/built/admin/assets/notifications-BjSvTJqh.js +0 -1
- package/core/built/admin/assets/offers-container-modal-Bc9A196w.js +0 -1
- package/core/built/admin/assets/onboarding-route-Di1LJYEE.js +0 -1
- package/core/built/admin/assets/overview-DE7NHZQN.js +0 -1
- package/core/built/admin/assets/overview-DEF-ZY6x.js +0 -1
- package/core/built/admin/assets/page-header-B-G_g42C.js +0 -1
- package/core/built/admin/assets/pagemenu-CnkTDO71.js +0 -1
- package/core/built/admin/assets/pages-route-VT7r7YhG.js +0 -1
- package/core/built/admin/assets/portal-frame-D9V8iq5x.js +0 -1
- package/core/built/admin/assets/portal-modal-B5OOR9ZD.js +0 -1
- package/core/built/admin/assets/post-analytics-header-J0WciLx6.js +0 -1
- package/core/built/admin/assets/post-analytics-rGb-PgD-.js +0 -1
- package/core/built/admin/assets/post-share-modal-D0BbTo4W.js +0 -1
- package/core/built/admin/assets/posts-list-screen-CO5jAWoU.js +0 -2
- package/core/built/admin/assets/posts-route-DV-CXWK9.js +0 -1
- package/core/built/admin/assets/preferences-Tac29oG6.js +0 -5
- package/core/built/admin/assets/preview-chrome-C2lQgZBZ.js +0 -1
- package/core/built/admin/assets/preview-modal-CjRbBr8U.js +0 -1
- package/core/built/admin/assets/profile-yiM1f_Og.js +0 -1
- package/core/built/admin/assets/radio-group-CKROWsw4.js +0 -1
- package/core/built/admin/assets/router-provider-CU87qxXw.js +0 -1
- package/core/built/admin/assets/select-CKaC4ggu.js +0 -1
- package/core/built/admin/assets/settings-81kxUa28.js +0 -1
- package/core/built/admin/assets/settings-BVXFqBVK.js +0 -4
- package/core/built/admin/assets/settings-breadcrumbs-czGRx-iu.js +0 -1
- package/core/built/admin/assets/shade-app-CcS3f8MZ-D5BnfHck.js +0 -3
- package/core/built/admin/assets/share-modal-BL-16PiB.js +0 -1
- package/core/built/admin/assets/skeleton-DwCva0-b.js +0 -1
- package/core/built/admin/assets/sort-button-Dquc83jY.js +0 -1
- package/core/built/admin/assets/stack-BURz77VS.js +0 -1
- package/core/built/admin/assets/stats-view-NDja_g96.js +0 -1
- package/core/built/admin/assets/stripe-button-C3cfCAou.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-FyE3Ey2c.js +0 -1
- package/core/built/admin/assets/switch-zfUUy9Jh.js +0 -1
- package/core/built/admin/assets/table-dE4GTIRT.js +0 -1
- package/core/built/admin/assets/tabs-B3XK93sR.js +0 -1
- package/core/built/admin/assets/tag-detail-D4P-tZdM.js +0 -2
- package/core/built/admin/assets/tag-picker-BNSZ6Qxw.js +0 -1
- package/core/built/admin/assets/tags-BsZcaJ6h.js +0 -1
- package/core/built/admin/assets/text-DC-ot6u2.js +0 -1
- package/core/built/admin/assets/textarea-B4FffFmE.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-a77Z2VMH.js +0 -1
- package/core/built/admin/assets/toggle-group-D2p4okmJ.js +0 -1
- package/core/built/admin/assets/transistor-modal-C8RMkhOD.js +0 -1
- package/core/built/admin/assets/trend-badge-B9v7kMF8.js +0 -1
- package/core/built/admin/assets/types-uYQ1HgqL.js +0 -1
- package/core/built/admin/assets/use-analytics-data-ChHqiZXv.js +0 -1
- package/core/built/admin/assets/use-availability-hPdeG76y.js +0 -1
- package/core/built/admin/assets/use-filter-params-BSFigDIn.js +0 -1
- package/core/built/admin/assets/use-growth-stats-CqqdnESN.js +0 -1
- package/core/built/admin/assets/use-limiter-DBGjYqu7.js +0 -19
- package/core/built/admin/assets/use-reply-chain-data-DmD2yX5D.js +0 -1
- package/core/built/admin/assets/use-sending-eta-CQeG6OP_.js +0 -1
- package/core/built/admin/assets/use-setting-group-Dza6bQ7W.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BIjI5mL3.js +0 -1
- package/core/built/admin/assets/use-simple-pagination-DwVxe-Lo.js +0 -1
- package/core/built/admin/assets/use-staff-users-i_Mq9OGR.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-DXoF74NM.js +0 -1
- package/core/built/admin/assets/user-detail-modal-BU4QCHE7.js +0 -1
- package/core/built/admin/assets/virtual-list-DScLNuU8.js +0 -1
- package/core/built/admin/assets/web-BZS-t4H0.js +0 -1
- package/core/built/admin/assets/web-DxAUxnK3.js +0 -2
- package/core/frontend/services/sitemap/utils.js +0 -17
- package/core/server/services/automations/get-scheduler-idempotency-key.js +0 -13
- package/core/server/web/api/testmode/index.js +0 -1
- package/core/server/web/api/testmode/jobs/cpu-hog.js +0 -15
- package/core/server/web/api/testmode/jobs/graceful-job.js +0 -50
- package/core/server/web/api/testmode/jobs/say-hello.js +0 -17
- package/core/server/web/api/testmode/routes.js +0 -120
|
@@ -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 (!
|
|
121
|
+
if (!Array.isArray(templateList)) {
|
|
122
122
|
templateList = [templateList];
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -83,7 +83,7 @@ class ParentRouter {
|
|
|
83
83
|
* @private
|
|
84
84
|
*/
|
|
85
85
|
_respectDominantRouter(req, res, next, slug) {
|
|
86
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
56
|
+
const pageRecords = records.slice((page - 1) * this.maxPerPage, page * this.maxPerPage);
|
|
64
57
|
|
|
65
58
|
// Do not generate empty sitemaps
|
|
66
|
-
if (
|
|
59
|
+
if (pageRecords.length === 0) {
|
|
67
60
|
return null;
|
|
68
61
|
}
|
|
69
62
|
|
|
70
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
* @
|
|
113
|
+
* @param {number} [lastModified] epoch milliseconds
|
|
114
|
+
* @returns {{loc: string, ts: number, imageLoc: string|null}}
|
|
128
115
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
189
|
-
this.nodeLookup
|
|
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
|
|
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
|
-
|
|
17
|
+
if (this.siteMapContent !== null) {
|
|
18
|
+
return this.siteMapContent;
|
|
19
|
+
}
|
|
22
20
|
|
|
23
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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:
|
|
97
|
-
posts:
|
|
98
|
-
authors:
|
|
99
|
-
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
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
202
|
+
next.pages.addUrl(entry.url, entry.datum);
|
|
188
203
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
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
|
-
|
|
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
|
+
'&': '&',
|
|
27
|
+
'"': '"',
|
|
28
|
+
"'": ''',
|
|
29
|
+
'<': '<',
|
|
30
|
+
'>': '>',
|
|
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
|
+
}
|