ghost 6.55.0 → 6.57.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/validate-adapters.js +74 -0
- package/bin/validate-adapters.ts +87 -0
- package/bin/warm-compile-cache.sh +60 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
- package/core/app.js +1 -1
- package/core/boot.js +21 -25
- package/core/bridge.js +10 -12
- package/core/built/admin/assets/Combination-BDvCdg1t.js +41 -0
- package/core/built/admin/assets/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
- package/core/built/admin/assets/about-C7O-AgRZ.js +1 -0
- package/core/built/admin/assets/{account-migration-sjHLx5dy.js → account-migration-Cv_ol1ko.js} +1 -1
- package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-DoYXtnZ8.js → account-migration-DHFbePl6.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +6 -6
- package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-CT4tS2_S.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-ByYWKipo.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-Bxeh6MKW.js → bluesky-sharing-srVnSHDz.js} +16 -16
- package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-C2sos5Ht.js} +3 -3
- package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-B1UBTOoZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-D8sFfdpC.js → dist-MPsb2WZ1.js} +12 -12
- package/core/built/admin/assets/activitypub/{domain-CWueFW6q.js → domain-DzcM9Dzo.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-BGxPU7N5.js → edit-profile-CsQ_4xAk.js} +945 -945
- package/core/built/admin/assets/activitypub/{explore-DBK-u0Ch.js → explore-CJe0DZBK.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-Bcir5RQZ.js → feed-CKV3qWON.js} +33 -33
- package/core/built/admin/assets/activitypub/{field-BV-CIT3d.js → field-DckHfDJf.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-Dlp8RM55.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-CsPy4R0M.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-yC0nfvzl.js → inbox-DAsjzUUe.js} +85 -85
- package/core/built/admin/assets/activitypub/{label-DTRS9TDk.js → label-DWyo9E5T.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-Dwl9t0pb.js → moderation-B0Inc03M.js} +11 -11
- package/core/built/admin/assets/activitypub/{note-B-zKsZ7r.js → note-CSE54lER.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-DjJZD54t.js → notifications-RSwbKqdh.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-CoU8vs6d.js → preferences-CE231n2_.js} +20 -20
- package/core/built/admin/assets/activitypub/{profile-BAsaJekx.js → profile-aaQ1j1Eo.js} +237 -237
- package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cjb0Vs3m.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-CLH3OVTP.js → routes-Djb0mc9k.js} +1139 -1150
- package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-D8pROI5n.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-DA6pNJ-Q.js → settings-DMc32IQl.js} +13 -13
- package/core/built/admin/assets/activitypub/{site-CQD42Pw5.js → site-DVIcsohX.js} +81 -81
- package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-9Yey9usF.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DdYmKRXV.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-hdSkcjZU.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-Cg0saow1.js → tabs-CUqseYzQ.js} +317 -315
- package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-Coni6OXM.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-B046AM38.js} +115 -96
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-Br4aOWOh.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-BRfRYd16.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-m9Dv6f77.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-CPSVZL2H.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-Ci9Z5yat.js +1 -0
- package/core/built/admin/assets/analytics-DqSCVV8U.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-DU3avV7d.js +1 -0
- package/core/built/admin/assets/api-keys-BK0LylyK.js +1 -0
- package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
- package/core/built/admin/assets/{audience-DjEWNhp3.js → audience-DTJtXXWY.js} +1 -1
- package/core/built/admin/assets/automated-emails-PayPCT8L.js +1 -0
- package/core/built/admin/assets/{automations-DhKeS_U5.js → automations-162q5HjL.js} +1 -1
- package/core/built/admin/assets/{automations-Bx3VcFIL.js → automations-dtQ_MRZ7.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-ClMd0-Ft.js → bluesky-sharing-B8Es3AuV.js} +1 -1
- package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
- package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
- package/core/built/admin/assets/{breadcrumb-BD0U2ygg.js → breadcrumb-tg5zL--z.js} +1 -1
- package/core/built/admin/assets/build-CsiH26uS.js +1 -0
- package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
- package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-fjQKRVyY.js → chart-helpers-CZElTGYL.js} +1 -1
- package/core/built/admin/assets/checkbox-Cyo-x7mC.js +1 -0
- package/core/built/admin/assets/chunk.524.cd5498dd4e195f679266.js +25 -0
- package/core/built/admin/assets/chunk.582.6f92ec1f03734bf2ea3f.js +27 -0
- package/core/built/admin/assets/{chunk.481.6c7395a49484bb4583aa.js → chunk.873.b8fc05a8ab877fb819ed.js} +62 -60
- package/core/built/admin/assets/code-editor-view-Dq1rQ4tS.js +1 -0
- package/core/built/admin/assets/color-picker-field-BonBgxhU.js +1 -0
- package/core/built/admin/assets/color-utils-BsrwXuDz.js +1 -0
- package/core/built/admin/assets/combobox-uX5r_paI.js +1 -0
- package/core/built/admin/assets/command-DxwBO2pF.js +1 -0
- package/core/built/admin/assets/comments-Dn-RO-nU.js +1 -0
- package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
- package/core/built/admin/assets/confirmation-modal-B6D6mk6R.js +1 -0
- package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
- package/core/built/admin/assets/content-api-modal-BxQ-7T6u.js +1 -0
- package/core/built/admin/assets/copy-field-BAxL4Zqn.js +1 -0
- package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
- package/core/built/admin/assets/custom-integration-modal---d_l7lc.js +1 -0
- package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
- package/core/built/admin/assets/{date-range-select-BFp58EMk.js → date-range-select-PB2n1HZf.js} +1 -1
- package/core/built/admin/assets/debounce-Dg2fW5Rg.js +3 -0
- package/core/built/admin/assets/design-and-theme-modal-DYRT207a.js +23 -0
- package/core/built/admin/assets/dirty-confirm-dialog-BnDJ39ao.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-iMHrkyV3.js +1 -0
- package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
- package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
- package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS02.js} +1 -1
- package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-D5vhMdXf.js} +1 -1
- package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
- package/core/built/admin/assets/dist-WeOIvSYM.js +23 -0
- package/core/built/admin/assets/{dist-DyViJwIy.js → dist-cFzKLG7y.js} +3 -3
- package/core/built/admin/assets/dist-j1xxxnY8.js +1 -0
- package/core/built/admin/assets/dist-myQaY0ka.js +1 -0
- package/core/built/admin/assets/domain-BR8FIdjD.js +1 -0
- package/core/built/admin/assets/{dropzone-DlK-K2DQ.js → dropzone-hNxmUvwD.js} +1 -1
- package/core/built/admin/assets/{edit-profile-BKT-REJw.js → edit-profile-CENrkY0l.js} +1 -1
- package/core/built/admin/assets/editor-B18D0lm8.js +8 -0
- package/core/built/admin/assets/embed-signup-form-modal-BsCYUMvQ.js +9 -0
- package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
- package/core/built/admin/assets/{explore-ClUpJZOp.js → explore-BmrnljWw.js} +1 -1
- package/core/built/admin/assets/{feed-DF92dUMP.js → feed-Dy4li6i8.js} +1 -1
- package/core/built/admin/assets/{field-Do7lieBA.js → field-Dh_lwk13.js} +1 -1
- package/core/built/admin/assets/filter-sources-D2zDuJH5.js +1 -0
- package/core/built/admin/assets/filters-3JBQcNtJ.js +1 -0
- package/core/built/admin/assets/filters-CFt_BDe4.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-D6Rjxugw.js +1 -0
- package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
- package/core/built/admin/assets/{ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
- package/core/built/admin/assets/{ghost-c4057474943effc568c5659ccc4909d7.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +420 -398
- package/core/built/admin/assets/{ghost-dark-db36d795d21c61426837df40af270966.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-f9VIMNDv.js → gift-link-modal-zMtlJhLQ.js} +1 -1
- package/core/built/admin/assets/{gift-links-CsUhefWG.js → gift-links-CVuqLhsZ.js} +1 -1
- package/core/built/admin/assets/global-data-provider-B949LDXJ.js +1 -0
- package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
- package/core/built/admin/assets/{growth-CVw1My4S.js → growth-C38Xtp8D.js} +2 -2
- package/core/built/admin/assets/{growth-B3I6gNLm.js → growth-CFws67_i.js} +1 -1
- package/core/built/admin/assets/history-modal-ILAOkbxH.js +1 -0
- package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
- package/core/built/admin/assets/html-field-9a9Id_2q.js +1 -0
- package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
- package/core/built/admin/assets/images-CrQKVc9Q.js +1 -0
- package/core/built/admin/assets/{inbox-CFBSu4Ed.js → inbox-BrSRzyvz.js} +6 -6
- package/core/built/admin/assets/index-BHy2DZX-.js +67 -0
- package/core/built/admin/assets/index-Ds2iUoF6.css +1 -0
- package/core/built/admin/assets/index.es-B9tvf-t4.js +1 -0
- package/core/built/admin/assets/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
- package/core/built/admin/assets/input-group-6vEPvX9S.js +1 -0
- package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.js} +1 -1
- package/core/built/admin/assets/integration-header-B4DyXV06.js +1 -0
- package/core/built/admin/assets/integrations-BT6_iJvu.js +1 -0
- package/core/built/admin/assets/invite-user-modal-5xDCEw90.js +1 -0
- package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
- package/core/built/admin/assets/koenig-editor-base-BWLEB9Vf.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9535 -9527
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
- package/core/built/admin/assets/{koenig-lexical-f8vHRYvz.js → koenig-lexical-Cj62r_y9.js} +86 -86
- package/core/built/admin/assets/{kpi-card-DSap9rAy.js → kpi-card-DOUnkZcN.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DU0rn9Cj.js → kpi-card-IHDo8Q56.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-Dz33AV6h.js → kpi-helpers-CjUdECAK.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BtTTrKvJ.js → kpi-tabs-X3DkOYbI.js} +1 -1
- package/core/built/admin/assets/{label-DS8Mi6G5.js → label-Dq6QaOuS.js} +1 -1
- package/core/built/admin/assets/label-picker-8YlhS2U3.js +1 -0
- package/core/built/admin/assets/labels-DAPjKPy2.js +1 -0
- package/core/built/admin/assets/layout-TMj4P-vv.js +1 -0
- package/core/built/admin/assets/limit-modal-BRPsit_B.js +1 -0
- package/core/built/admin/assets/link-helpers-Bzkk68GD.js +1 -0
- package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-CmvsuuV_.js +1 -0
- package/core/built/admin/assets/member-detail-DCvRrkmJ.js +1 -0
- package/core/built/admin/assets/members-DxbSKngD.js +11 -0
- package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
- package/core/built/admin/assets/{moderation-DySbgyUS.js → moderation-Da5lhhWh.js} +1 -1
- package/core/built/admin/assets/multi-select-combobox-CYdkH7KQ.js +1 -0
- package/core/built/admin/assets/navigation-modal-BIoTwtMy.js +1 -0
- package/core/built/admin/assets/newsletter-BptlVoJm.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-BIvxqE0N.js +1 -0
- package/core/built/admin/assets/newsletter-emails-Bf8IZLsP.js +1 -0
- package/core/built/admin/assets/newsletters-BgrIZk0V.js +1 -0
- package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
- package/core/built/admin/assets/{note-C8kxrz6w.js → note-DYcLOmDb.js} +1 -1
- package/core/built/admin/assets/{notifications-CgtSfX7t.js → notifications-B2SPFIV9.js} +1 -1
- package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BZnZITMw.js +1 -0
- package/core/built/admin/assets/{onboarding-route-4qtziA9d.js → onboarding-route-DgdfxkQh.js} +1 -1
- package/core/built/admin/assets/overview-BYQJXSog.js +1 -0
- package/core/built/admin/assets/overview-Dt2_mR6U.js +1 -0
- package/core/built/admin/assets/{page-header-B3Wb9Jqv.js → page-header-CpmxJj5B.js} +1 -1
- package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
- package/core/built/admin/assets/pintura-modal-B4MuBuuO.js +1 -0
- package/core/built/admin/assets/portal-frame-BF0NBIji.js +1 -0
- package/core/built/admin/assets/portal-modal-DMVbm0oy.js +1 -0
- package/core/built/admin/assets/{post-analytics-We-ko5Cb.js → post-analytics-CXWBOGyP.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-ChzjXVPt.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-B6UynTGb.js → post-analytics-provider-Cgw4U_fw.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DFfO93_0.js → post-share-modal-B9Vmk_XM.js} +1 -1
- package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
- package/core/built/admin/assets/preferences-BfzJmnsd.js +5 -0
- package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
- package/core/built/admin/assets/preview-modal-Dw_HtD6S.js +1 -0
- package/core/built/admin/assets/profile-BT5vtv9b.js +1 -0
- package/core/built/admin/assets/radio-group-I7ou7PoJ.js +1 -0
- package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
- package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-DSQfprxH.js} +1 -1
- package/core/built/admin/assets/select-D9oU1IoM.js +1 -0
- package/core/built/admin/assets/settings-CfZFbyu_.js +4 -0
- package/core/built/admin/assets/{settings-WXNeWyU1.js → settings-CpvHWix7.js} +1 -1
- package/core/built/admin/assets/settings-breadcrumbs-K0USP2nw.js +1 -0
- package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
- package/core/built/admin/assets/{share-modal-DsgW3wbg.js → share-modal-ClIyPzc5.js} +1 -1
- package/core/built/admin/assets/slack-modal-1XXqOfiw.js +1 -0
- package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
- package/core/built/admin/assets/{sort-button-Bb_FCl7e.js → sort-button-CdBsm40v.js} +1 -1
- package/core/built/admin/assets/{source-icon-BeaRxUn9.js → source-icon-CEYmVLXR.js} +1 -1
- package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
- package/core/built/admin/assets/{stats-Bjj_kmJ1.js → stats-D8nZCTy5.js} +1 -1
- package/core/built/admin/assets/{stats-view-BxPbW6K1.js → stats-view-CugFVf0g.js} +1 -1
- package/core/built/admin/assets/{step-1-B1e3-1F8.js → step-1-DOYjrORn.js} +1 -1
- package/core/built/admin/assets/{step-2-C5WaeBJo.js → step-2-vA6RsarH.js} +1 -1
- package/core/built/admin/assets/{step-3-B46iVPwu.js → step-3-BtIBxMId.js} +2 -2
- package/core/built/admin/assets/stripe-button-UFP4QRXd.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-C_L3uM6n.js +1 -0
- package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
- package/core/built/admin/assets/{table-CB-4_HBJ.js → table-TMCgCQK7.js} +1 -1
- package/core/built/admin/assets/tabs-Dd6oOKbv.js +1 -0
- package/core/built/admin/assets/tag-detail-D3l-UJPR.js +1 -0
- package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
- package/core/built/admin/assets/tags-WdKQGWR8.js +1 -0
- package/core/built/admin/assets/testimonials-modal-CqWp-xXJ.js +1 -0
- package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
- package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-BkcR2u0A.js +1 -0
- package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
- package/core/built/admin/assets/toggle-group-Guorw93z.js +1 -0
- package/core/built/admin/assets/{topic-filter-DAFT87tQ.js → topic-filter-CbgoWIHn.js} +1 -1
- package/core/built/admin/assets/transistor-modal-DuNnnpal.js +1 -0
- package/core/built/admin/assets/unsplash-modal-y2bVwTHH.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-S0FTd43s.js → use-analytics-data-B-b4wNSC.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-m09J38Mq.js +2 -0
- package/core/built/admin/assets/use-feature-flag-ud0Qz8vt.js +1 -0
- package/core/built/admin/assets/{use-filter-params-Gpoq2E1L.js → use-filter-params-BnbBDc7T.js} +1 -1
- package/core/built/admin/assets/use-filterable-api-iMpDZBxD.js +1 -0
- package/core/built/admin/assets/use-form-vRjI0Kwk.js +1 -0
- package/core/built/admin/assets/{use-growth-stats-CIaiHOVb.js → use-growth-stats-DPU5rt3W.js} +1 -1
- package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
- package/core/built/admin/assets/use-limiter-BkN3E4gw.js +2 -0
- package/core/built/admin/assets/use-pintura-config-C_z8HTG2.js +1 -0
- package/core/built/admin/assets/use-pintura-editor-DNgbkhGp.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-Cq2vBPrA.js → use-post-referrers-C2mXyrp-.js} +1 -1
- package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
- package/core/built/admin/assets/{use-reply-chain-data-DBDRtGu-.js → use-reply-chain-data-DaK8q2qQ.js} +1 -1
- package/core/built/admin/assets/use-setting-group-YlYcoNyP.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-Bf1g1rEh.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-CjYNxdwe.js → use-simple-pagination-D9ckKo5-.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-falHAtuK.js → use-tinybird-query-GSa32hVw.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-D7-8ND1y.js → use-tinybird-token-CKLOZFsw.js} +1 -1
- package/core/built/admin/assets/use-url-input-z-p0Kewm.js +5 -0
- package/core/built/admin/assets/user-detail-modal-BMxOA4Sv.js +1 -0
- package/core/built/admin/assets/{users-Dkv8NQZe.js → users-B6hIds6n.js} +1 -1
- package/core/built/admin/assets/validator-Cr0sMKuW.js +7 -0
- package/core/built/admin/assets/{virtual-list-CvnC9zF2.js → virtual-list-RHeEL05L.js} +1 -1
- package/core/built/admin/assets/{web-jFYWCKDn.js → web-Bw2QMO5u.js} +2 -2
- package/core/built/admin/assets/web-xJtcwMy4.js +1 -0
- package/core/built/admin/assets/zapier-modal-BP2f4-vY.js +1 -0
- package/core/built/admin/index.html +20 -18
- package/core/frontend/helpers/authors.js +1 -1
- package/core/frontend/helpers/link.js +3 -8
- package/core/frontend/helpers/tags.js +1 -1
- package/core/frontend/meta/author-url.js +2 -2
- package/core/frontend/meta/url.js +4 -4
- package/core/frontend/services/data/fetch-data.js +10 -5
- package/core/frontend/services/proxy.js +1 -1
- package/core/frontend/services/routing/api-adapter.js +115 -0
- package/core/frontend/services/routing/api-adapter.ts +157 -0
- package/core/frontend/services/routing/collection-router.js +5 -3
- package/core/frontend/services/routing/controllers/index.js +1 -1
- package/core/frontend/services/routing/controllers/rss.js +26 -31
- package/core/frontend/services/routing/controllers/rss.ts +66 -0
- package/core/frontend/services/routing/controllers/static.js +8 -7
- package/core/frontend/services/routing/events.js +20 -6
- package/core/frontend/services/routing/parent-router.js +17 -4
- package/core/frontend/services/routing/permalink-adapter.js +13 -0
- package/core/frontend/services/routing/permalink-adapter.ts +10 -0
- package/core/frontend/services/routing/registry.js +59 -33
- package/core/frontend/services/routing/router-manager.js +24 -22
- package/core/frontend/services/routing/static-routes-router.js +4 -4
- package/core/frontend/services/routing/taxonomy-router.js +6 -2
- package/core/frontend/services/sitemap/base-site-map-generator.js +1 -28
- package/core/frontend/services/sitemap/site-map-manager.js +22 -57
- package/core/server/adapters/route-settings/utils.js +10 -2
- package/core/server/adapters/route-settings/utils.ts +12 -2
- package/core/server/api/endpoints/email-post.js +1 -1
- package/core/server/api/endpoints/previews.js +1 -1
- package/core/server/api/endpoints/search-index-public.js +3 -3
- package/core/server/api/endpoints/search-index.js +4 -4
- package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/comments.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/pages.js +3 -3
- package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
- package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/users.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/utils/url.js +31 -13
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +2 -2
- package/core/server/api/endpoints/utils/serializers/output/mappers/tags.js +2 -2
- package/core/server/api/endpoints/utils/serializers/output/mappers/users.js +8 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/url.js +18 -11
- package/core/server/data/migrations/versions/6.56/2026-08-03-19-27-51-add-automation-run-step-id-to-automated-email-recipients.js +8 -0
- package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +74 -0
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-54-14-reset-automation-email-analytics.js +25 -0
- package/core/server/data/schema/commands.js +21 -5
- package/core/server/data/schema/fixtures/fixtures.json +2 -2
- package/core/server/data/schema/schema.js +25 -5
- package/core/server/lib/common/to-plain.ts +1 -1
- package/core/server/models/post.js +12 -3
- package/core/server/services/adapter-manager/adapter-manager.js +1 -45
- package/core/server/services/adapter-manager/adapter-manager.ts +2 -48
- package/core/server/services/adapter-manager/adapter-paths.js +24 -0
- package/core/server/services/adapter-manager/adapter-paths.ts +21 -0
- package/core/server/services/adapter-manager/base-classes.js +22 -0
- package/core/server/services/adapter-manager/base-classes.ts +22 -0
- package/core/server/services/adapter-manager/index.js +4 -27
- package/core/server/services/adapter-manager/index.ts +4 -31
- package/core/server/services/adapter-manager/utils.js +50 -0
- package/core/server/services/adapter-manager/utils.ts +57 -0
- package/core/server/services/audience-feedback/audience-feedback-controller.js +1 -1
- package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
- package/core/server/services/automations/automations-repository.ts +3 -1
- package/core/server/services/automations/database-automations-repository.js +33 -14
- package/core/server/services/automations/database-automations-repository.ts +41 -15
- package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
- package/core/server/services/automations/poll.js +3 -1
- package/core/server/services/automations/poll.ts +4 -1
- package/core/server/services/automations/service.js +7 -3
- package/core/server/services/automations/service.ts +7 -3
- package/core/server/services/comments/comments-service-emails.js +1 -1
- package/core/server/services/comments/comments-service.js +1 -1
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +11 -2
- package/core/server/services/email-analytics/email-analytics-service.js +1 -1
- package/core/server/services/email-service/email-service-wrapper.js +2 -1
- package/core/server/services/email-service/mailgun-email-provider.js +15 -0
- package/core/server/services/gift-links/CONTEXT.md +9 -0
- package/core/server/services/gifts/CONTEXT.md +55 -0
- package/core/server/services/gifts/README.md +28 -0
- package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
- package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
- package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
- package/core/server/services/gifts/gift-checkout-offer.js +74 -0
- package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
- package/core/server/services/gifts/gift-codec.js +63 -0
- package/core/server/services/gifts/gift-codec.ts +62 -0
- package/core/server/services/gifts/gift-controller.js +6 -31
- package/core/server/services/gifts/gift-controller.ts +6 -69
- package/core/server/services/gifts/gift-email-service.ts +3 -2
- package/core/server/services/gifts/gift-schema.js +34 -0
- package/core/server/services/gifts/gift-schema.ts +39 -0
- package/core/server/services/gifts/gift-service-wrapper.js +13 -11
- package/core/server/services/gifts/gift-service.js +265 -52
- package/core/server/services/gifts/gift-service.ts +431 -97
- package/core/server/services/gifts/gift.ts +16 -39
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +1 -1
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +2 -4
- package/core/server/services/lib/mailgun-client.js +3 -3
- package/core/server/services/link-tracking/link-click-repository.js +6 -1
- package/core/server/services/link-tracking/link-click-tracking-service.js +12 -3
- package/core/server/services/media-inliner/external-media-inliner.js +12 -8
- package/core/server/services/member-attribution/url-translator.js +5 -7
- package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +4 -3
- package/core/server/services/member-welcome-emails/service.js +79 -15
- package/core/server/services/members/api.js +2 -3
- package/core/server/services/members/import-export/import/completion-email.js +8 -4
- package/core/server/services/members/import-export/import/completion-email.ts +9 -4
- package/core/server/services/members/import-export/import/importer.js +25 -4
- package/core/server/services/members/import-export/import/importer.ts +27 -6
- package/core/server/services/members/import-export/import/row.ts +5 -1
- package/core/server/services/members/import-export/index.js +6 -2
- package/core/server/services/members/import-export/index.ts +10 -4
- package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
- package/core/server/services/members/members-api/members-api.js +13 -7
- package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
- package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
- package/core/server/services/members/members-api/services/payments-service.js +6 -68
- package/core/server/services/members-custom-fields/queries.js +4 -13
- package/core/server/services/members-custom-fields/queries.ts +4 -13
- package/core/server/services/members-custom-fields/schema.js +19 -21
- package/core/server/services/members-custom-fields/schema.ts +18 -20
- package/core/server/services/members-custom-fields/storage.js +94 -27
- package/core/server/services/members-custom-fields/storage.ts +114 -39
- package/core/server/services/members-custom-fields/values-service.js +98 -103
- package/core/server/services/members-custom-fields/values-service.ts +116 -114
- package/core/server/services/mentions/resource-service.js +1 -1
- package/core/server/services/mentions/service.js +2 -2
- package/core/server/services/oembed/oembed-service.js +44 -3
- package/core/server/services/posts/posts-service.js +11 -0
- package/core/server/services/route-settings/dynamic-routing-service.js +10 -62
- package/core/server/services/route-settings/route-settings-parser.js +25 -25
- package/core/server/services/route-settings/route-settings-parser.ts +27 -27
- package/core/server/services/route-settings/validation-errors.js +68 -4
- package/core/server/services/route-settings/validation-errors.ts +79 -4
- package/core/server/services/slack-ping/slack-ping-service.js +2 -2
- package/core/server/services/slack-ping/slack-ping-service.ts +3 -5
- package/core/server/services/stats/content-stats-service.js +3 -3
- package/core/server/services/stats/posts-stats-service.js +29 -25
- package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
- package/core/server/services/stripe/stripe-api.js +10 -0
- package/core/server/services/url/config.js +90 -135
- package/core/server/services/url/index.js +6 -49
- package/core/server/services/url/lazy-find-resource.js +14 -11
- package/core/server/services/url/lazy-find-resource.ts +14 -11
- package/core/server/services/url/lazy-url-service.js +174 -94
- package/core/server/services/url/lazy-url-service.ts +233 -97
- package/core/server/services/url/routable-resources.js +62 -71
- package/core/server/services/url/router-filter.js +12 -4
- package/core/server/services/url/router-filter.ts +12 -4
- package/core/server/services/webhooks/index.js +1 -1
- package/core/server/services/webhooks/serialize.js +2 -1
- package/core/server/web/gift-preview/controller.js +10 -25
- package/core/shared/config/env/config.testing-mysql.json +1 -2
- package/core/shared/config/env/config.testing.json +1 -2
- package/core/shared/events/index.js +1 -2
- package/core/shared/labs.js +4 -4
- package/package.json +10 -10
- package/pnpm-lock.yaml +27 -20
- package/pnpm-workspace.yaml +3 -2
- package/tsconfig.json +1 -0
- package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
- package/core/built/admin/assets/Combination-CJG7Flp-.js +0 -41
- package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +0 -1
- package/core/built/admin/assets/automated-emails-DRy7qY-J.js +0 -7
- package/core/built/admin/assets/checkbox-CWa28lgO.js +0 -1
- package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +0 -25
- package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +0 -27
- package/core/built/admin/assets/code-editor-view-CCr0rBWS.js +0 -1
- package/core/built/admin/assets/comments-DyUSMUdj.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-CUxMfiPa.js +0 -1
- package/core/built/admin/assets/dist-2Tnz2Yfq.js +0 -1
- package/core/built/admin/assets/dist-CjY-f5Z1.js +0 -1
- package/core/built/admin/assets/dist-DDY6GRVz.js +0 -1
- package/core/built/admin/assets/dist-DG78yroX.js +0 -23
- package/core/built/admin/assets/domain-C0bV2UV8.js +0 -1
- package/core/built/admin/assets/editor-Dp865O-4.js +0 -8
- package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
- package/core/built/admin/assets/filter-sources-BV2rnpfA.js +0 -1
- package/core/built/admin/assets/filters-CUXJ2Nm9.js +0 -1
- package/core/built/admin/assets/filters-DfbIWc_l.js +0 -1
- package/core/built/admin/assets/index-9Cw8z3rA.css +0 -1
- package/core/built/admin/assets/index-BGG7NckO.js +0 -67
- package/core/built/admin/assets/input-group-BSgG_lVw.js +0 -1
- package/core/built/admin/assets/label-picker-1DvSuLA1.js +0 -1
- package/core/built/admin/assets/labels-CDSUt4J_.js +0 -1
- package/core/built/admin/assets/layout-yQEi2qDM.js +0 -1
- package/core/built/admin/assets/link-helpers-7EYqvvHs.js +0 -1
- package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +0 -1
- package/core/built/admin/assets/member-detail-BAq7gDaa.js +0 -1
- package/core/built/admin/assets/members-CQpVFJu4.js +0 -10
- package/core/built/admin/assets/modals-ReORvWnu.js +0 -31
- package/core/built/admin/assets/newsletter-CJQvtHqN.js +0 -1
- package/core/built/admin/assets/newsletters-B3QQXfGT.js +0 -1
- package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
- package/core/built/admin/assets/overview-Bf2sbznq.js +0 -1
- package/core/built/admin/assets/overview-CBfePxcr.js +0 -1
- package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +0 -1
- package/core/built/admin/assets/preferences-DJPfHEGu.js +0 -5
- package/core/built/admin/assets/profile-DqBQp-gT.js +0 -1
- package/core/built/admin/assets/select-C5_xEt5b.js +0 -1
- package/core/built/admin/assets/settings-BwiVIXau.js +0 -4
- package/core/built/admin/assets/social-urls-BQPFEk1_.js +0 -8
- package/core/built/admin/assets/switch-z5Y6Uzt6.js +0 -1
- package/core/built/admin/assets/tabs-CCEgKYzq.js +0 -1
- package/core/built/admin/assets/tags-Cg2VJGmG.js +0 -1
- package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
- package/core/built/admin/assets/toggle-group-b3IlxvhV.js +0 -1
- package/core/built/admin/assets/web-B_5GZRvH.js +0 -1
- package/core/server/services/gifts/gift-repository.js +0 -2
- package/core/server/services/gifts/gift-repository.ts +0 -29
- package/core/server/services/route-settings/activation-bridge.js +0 -174
- package/core/server/services/route-settings/activation-bridge.ts +0 -250
- package/core/server/services/url/local-file-cache.js +0 -75
- package/core/server/services/url/queue.js +0 -260
- package/core/server/services/url/resource.js +0 -102
- package/core/server/services/url/resources.js +0 -480
- package/core/server/services/url/url-generator.js +0 -276
- package/core/server/services/url/url-service-facade.js +0 -366
- package/core/server/services/url/url-service-facade.ts +0 -519
- package/core/server/services/url/url-service.js +0 -387
- package/core/server/services/url/urls.js +0 -140
- package/core/shared/events/url-resource-updated-event.js +0 -33
|
@@ -1,519 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UrlServiceFacade
|
|
3
|
-
*
|
|
4
|
-
* Sits in front of the URL service so callers can use a stable, resource-based
|
|
5
|
-
* interface regardless of the underlying implementation. The facade can be
|
|
6
|
-
* built with two backends:
|
|
7
|
-
*
|
|
8
|
-
* - urlService: the legacy eager UrlService that precomputes a full
|
|
9
|
-
* resource → URL map at boot.
|
|
10
|
-
* - lazyUrlService: an on-demand implementation (LazyUrlService) that
|
|
11
|
-
* computes URLs and ownership per call.
|
|
12
|
-
*
|
|
13
|
-
* When `lazyUrlService` is provided the facade routes calls to it; otherwise
|
|
14
|
-
* it delegates to the eager `urlService`. This lets the lazy implementation be
|
|
15
|
-
* swapped in behind a config flag without touching individual callers.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const _ = require('lodash');
|
|
19
|
-
const logging = require('@tryghost/logging');
|
|
20
|
-
const errors = require('@tryghost/errors');
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Routing-level resource. `type` is one of the plural router keys
|
|
24
|
-
* ('posts', 'pages', 'tags', 'authors'). Concrete records carry additional
|
|
25
|
-
* fields (slug, published_at, primary_tag, ...) used by permalink templates.
|
|
26
|
-
*
|
|
27
|
-
* `id` is required: the eager backend's URL lookup is id-based, so a missing
|
|
28
|
-
* `id` would silently return `/404/`. Every in-tree caller already passes an
|
|
29
|
-
* id; the type makes that mandatory.
|
|
30
|
-
*/
|
|
31
|
-
export interface Resource {
|
|
32
|
-
type: string;
|
|
33
|
-
id: string;
|
|
34
|
-
[key: string]: unknown;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface UrlOptions {
|
|
38
|
-
absolute?: boolean;
|
|
39
|
-
withSubdirectory?: boolean;
|
|
40
|
-
// Bulk callers (the sitemap index build) set this: enumeration parity is
|
|
41
|
-
// covered by the getRoutableResources id-set comparison and per-URL
|
|
42
|
-
// parity by organic request traffic, so teeing hundreds of thousands of
|
|
43
|
-
// rows per rebuild would only capture stacks and recompute for nothing.
|
|
44
|
-
// Dies with compare mode.
|
|
45
|
-
skipComparison?: boolean;
|
|
46
|
-
// Diagnostic only: the producing api endpoint (docName/method/apiType),
|
|
47
|
-
// threaded from the output serializer so a compare mismatch or thin-resource
|
|
48
|
-
// throw names its producer in the logs. The compare-log caller stack
|
|
49
|
-
// truncates at the async api-framework boundary, so the stack alone can't.
|
|
50
|
-
// Ignored by both backends; only read into the compare context.
|
|
51
|
-
serializerContext?: {apiType?: string; docName?: string; method?: string};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Eager UrlService's resource envelope: `{config: {type}, data: {...}}`.
|
|
56
|
-
* Returned by `getResource(path)`. The `data` field carries the raw record;
|
|
57
|
-
* `id` is always present because the eager URL service keys its in-memory
|
|
58
|
-
* map by id, and the type makes that contract explicit.
|
|
59
|
-
*/
|
|
60
|
-
export interface LegacyResourceEnvelope {
|
|
61
|
-
config: {type: string; [key: string]: unknown};
|
|
62
|
-
data: {id: string; [key: string]: unknown};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Surface of the eager UrlService that the facade depends on. The full class
|
|
67
|
-
* has many more methods; only those used here need typing.
|
|
68
|
-
*/
|
|
69
|
-
export interface EagerUrlService {
|
|
70
|
-
getUrlByResourceId(id: string, options?: UrlOptions): string;
|
|
71
|
-
owns(routerId: string, id: string): boolean;
|
|
72
|
-
getResource(path: string): LegacyResourceEnvelope | null;
|
|
73
|
-
hasFinished(): boolean;
|
|
74
|
-
onRouterAddedType(...args: unknown[]): unknown;
|
|
75
|
-
onRouterUpdated(...args: unknown[]): unknown;
|
|
76
|
-
// undefined until the eager cache has initialised the type: the compiler
|
|
77
|
-
// enforces the guard at the read site.
|
|
78
|
-
resources: {getAllByType(type: string): Array<{data: Record<string, unknown>}> | undefined};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* On-demand enumeration of the routable rows of a type — the lazy
|
|
83
|
-
* counterpart of the eager service's in-memory cache. Built by
|
|
84
|
-
* `createFetchRoutableResources` (routable-resources.js).
|
|
85
|
-
*/
|
|
86
|
-
export type FetchRoutableResources = (
|
|
87
|
-
type: string,
|
|
88
|
-
options?: {columns?: string[]}
|
|
89
|
-
) => Promise<Array<Record<string, unknown>>>;
|
|
90
|
-
|
|
91
|
-
export interface LazyUrlServiceBackend {
|
|
92
|
-
getUrlForResource(resource: Resource, options?: UrlOptions): string;
|
|
93
|
-
ownsResource(routerId: string, resource: Resource): boolean;
|
|
94
|
-
resolveUrl(path: string): Promise<Resource | null>;
|
|
95
|
-
getRequiredRelations(): string[];
|
|
96
|
-
getRequiredFields(routerType: string): string[];
|
|
97
|
-
hasFinished(): boolean;
|
|
98
|
-
onRouterAddedType(...args: unknown[]): unknown;
|
|
99
|
-
onRouterUpdated(...args: unknown[]): unknown;
|
|
100
|
-
reset(): void;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export class UrlServiceFacade {
|
|
104
|
-
private urlService: EagerUrlService;
|
|
105
|
-
private lazyUrlService: LazyUrlServiceBackend | null;
|
|
106
|
-
private compare: boolean;
|
|
107
|
-
private fetchRoutableResources: FetchRoutableResources | null;
|
|
108
|
-
private enumComparesInFlight: Set<string>;
|
|
109
|
-
|
|
110
|
-
constructor({
|
|
111
|
-
urlService,
|
|
112
|
-
lazyUrlService = null,
|
|
113
|
-
compare = false,
|
|
114
|
-
fetchRoutableResources = null
|
|
115
|
-
}: {
|
|
116
|
-
urlService: EagerUrlService;
|
|
117
|
-
lazyUrlService?: LazyUrlServiceBackend | null;
|
|
118
|
-
compare?: boolean;
|
|
119
|
-
fetchRoutableResources?: FetchRoutableResources | null;
|
|
120
|
-
}) {
|
|
121
|
-
this.urlService = urlService;
|
|
122
|
-
this.lazyUrlService = lazyUrlService;
|
|
123
|
-
this.compare = compare;
|
|
124
|
-
this.fetchRoutableResources = fetchRoutableResources;
|
|
125
|
-
this.enumComparesInFlight = new Set();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
isLazy(): boolean {
|
|
129
|
-
return !!this.lazyUrlService && !this.compare;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
isComparing(): boolean {
|
|
133
|
-
return this.compare && !!this.lazyUrlService;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* The full resource record is required: the lazy backend evaluates NQL
|
|
139
|
-
* filters and applies permalink templates against it.
|
|
140
|
-
*/
|
|
141
|
-
getUrlForResource(resource: Resource, options?: UrlOptions): string {
|
|
142
|
-
if (this.isLazy()) {
|
|
143
|
-
return this.lazyUrlService!.getUrlForResource(resource, options);
|
|
144
|
-
}
|
|
145
|
-
const url = this.urlService.getUrlByResourceId(resource.id, options);
|
|
146
|
-
if (this.isComparing() && !options?.skipComparison) {
|
|
147
|
-
const context = this._compareContext(resource,
|
|
148
|
-
options?.serializerContext ? {serializer: options.serializerContext} : {});
|
|
149
|
-
// Snapshot: callers mutate the resource's nested objects in place
|
|
150
|
-
// after this returns, but the comparison runs later via setImmediate.
|
|
151
|
-
const snapshot = _.cloneDeep(resource);
|
|
152
|
-
setImmediate(() => this._compare('getUrlForResource', url,
|
|
153
|
-
() => this.lazyUrlService!.getUrlForResource(snapshot, options),
|
|
154
|
-
context));
|
|
155
|
-
}
|
|
156
|
-
return url;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* All routable rows of a type. Eager answers from its in-memory cache;
|
|
161
|
-
* lazy fetches from the database on demand. In compare mode the eager
|
|
162
|
-
* answer is returned and the lazy fetch runs in the background, with any
|
|
163
|
-
* id-set divergence logged — counts and id samples only, never row
|
|
164
|
-
* bodies (a large site has hundreds of thousands).
|
|
165
|
-
*/
|
|
166
|
-
async getRoutableResources(type: string, options: {columns?: string[]} = {}): Promise<Array<Record<string, unknown>>> {
|
|
167
|
-
if (this.isLazy()) {
|
|
168
|
-
if (!this.fetchRoutableResources) {
|
|
169
|
-
throw new errors.IncorrectUsageError({
|
|
170
|
-
message: 'getRoutableResources requires an injected fetchRoutableResources in lazy mode'
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
return this.fetchRoutableResources(type, options);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const eagerRows = (this.urlService.resources.getAllByType(type) || []).map(resource => resource.data);
|
|
177
|
-
|
|
178
|
-
// Single-flight per type: rapid invalidation cycles must not stack
|
|
179
|
-
// concurrent full-table comparison walks.
|
|
180
|
-
if (this.isComparing() && this.fetchRoutableResources && !this.enumComparesInFlight.has(type)) {
|
|
181
|
-
this.enumComparesInFlight.add(type);
|
|
182
|
-
void this._compareRoutableResources(type, options, eagerRows).finally(() => {
|
|
183
|
-
this.enumComparesInFlight.delete(type);
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
return eagerRows;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private async _compareRoutableResources(
|
|
190
|
-
type: string,
|
|
191
|
-
options: {columns?: string[]},
|
|
192
|
-
eagerRows: Array<Record<string, unknown>>
|
|
193
|
-
): Promise<void> {
|
|
194
|
-
let lazyRows;
|
|
195
|
-
try {
|
|
196
|
-
lazyRows = await this.fetchRoutableResources!(type, options);
|
|
197
|
-
} catch (err) {
|
|
198
|
-
this._reportLazyError('getRoutableResources', err as Error, {type});
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const eagerIds = new Set(eagerRows.map(row => row.id));
|
|
203
|
-
const lazyIds = new Set(lazyRows.map(row => row.id));
|
|
204
|
-
const missingFromLazy = [...eagerIds].filter(id => !lazyIds.has(id));
|
|
205
|
-
const extraInLazy = [...lazyIds].filter(id => !eagerIds.has(id));
|
|
206
|
-
|
|
207
|
-
if (!missingFromLazy.length && !extraInLazy.length) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
this._report(new errors.InternalServerError({
|
|
211
|
-
message: 'URL service parity mismatch',
|
|
212
|
-
code: 'LAZY_URL_PARITY_MISMATCH',
|
|
213
|
-
errorDetails: {
|
|
214
|
-
method: 'getRoutableResources',
|
|
215
|
-
type,
|
|
216
|
-
eagerCount: eagerIds.size,
|
|
217
|
-
lazyCount: lazyIds.size,
|
|
218
|
-
missingFromLazy: missingFromLazy.slice(0, 10),
|
|
219
|
-
extraInLazy: extraInLazy.slice(0, 10)
|
|
220
|
-
}
|
|
221
|
-
}));
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
ownsResource(routerIdentifier: string, resource: Resource): boolean {
|
|
225
|
-
if (this.isLazy()) {
|
|
226
|
-
return this.lazyUrlService!.ownsResource(routerIdentifier, resource);
|
|
227
|
-
}
|
|
228
|
-
const owns = this.urlService.owns(routerIdentifier, resource.id);
|
|
229
|
-
if (this.isComparing()) {
|
|
230
|
-
const context = this._compareContext(resource, {routerIdentifier});
|
|
231
|
-
// Snapshot, as in getUrlForResource.
|
|
232
|
-
const snapshot = _.cloneDeep(resource);
|
|
233
|
-
setImmediate(() => this._compare('ownsResource', owns,
|
|
234
|
-
() => this.lazyUrlService!.ownsResource(routerIdentifier, snapshot),
|
|
235
|
-
context));
|
|
236
|
-
}
|
|
237
|
-
return owns;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Context for a compare report. Must be built synchronously in the calling
|
|
241
|
-
// frame: the comparison itself runs from setImmediate, where the caller's
|
|
242
|
-
// stack is gone — so a lazy throw reported there names only the URL service
|
|
243
|
-
// internals, not which caller handed over the (possibly thin) resource.
|
|
244
|
-
// `caller` recaptures those frames; `resourceKeys` fingerprints the shape
|
|
245
|
-
// the caller passed (e.g. a Content-API-serialized post vs a full model).
|
|
246
|
-
private _compareContext(resource: Resource, extra: Record<string, unknown> = {}): Record<string, unknown> {
|
|
247
|
-
const caller: {stack?: string} = {};
|
|
248
|
-
Error.captureStackTrace(caller, this._compareContext);
|
|
249
|
-
return {
|
|
250
|
-
type: resource.type,
|
|
251
|
-
id: resource.id,
|
|
252
|
-
status: (resource as Record<string, unknown>).status,
|
|
253
|
-
resourceKeys: Object.keys(resource),
|
|
254
|
-
caller: caller.stack,
|
|
255
|
-
...extra
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Reverse URL lookup. Returns a flat resource shape (e.g. `{type, id, slug}`)
|
|
261
|
-
* rather than the legacy `{config: {type}, data: {...}}` envelope. Async to
|
|
262
|
-
* match the lazy implementation's contract.
|
|
263
|
-
*/
|
|
264
|
-
async resolveUrl(urlPath: string): Promise<Resource | null> {
|
|
265
|
-
if (this.isLazy()) {
|
|
266
|
-
return this.lazyUrlService!.resolveUrl(urlPath);
|
|
267
|
-
}
|
|
268
|
-
const resource = this.urlService.getResource(urlPath);
|
|
269
|
-
// The routing-level type ('posts', 'pages', 'tags', 'authors') wins
|
|
270
|
-
// over any DB type field on resource.data so the flat Resource is
|
|
271
|
-
// unambiguous.
|
|
272
|
-
const eagerResult = resource
|
|
273
|
-
? Object.assign({}, resource.data, {type: resource.config.type}) as Resource
|
|
274
|
-
: null;
|
|
275
|
-
if (this.isComparing()) {
|
|
276
|
-
// Fire-and-forget: don't await lazy so the reverse lookup adds no
|
|
277
|
-
// latency for its callers; the lazy DB read runs in the background.
|
|
278
|
-
// Snapshot the eager result, as in getUrlForResource.
|
|
279
|
-
const eagerSnapshot = _.cloneDeep(eagerResult);
|
|
280
|
-
void this._compareAsync('resolveUrl', eagerSnapshot,
|
|
281
|
-
() => this.lazyUrlService!.resolveUrl(urlPath),
|
|
282
|
-
{path: urlPath},
|
|
283
|
-
(a, b) => this._resolvesToSameResource(a, b));
|
|
284
|
-
}
|
|
285
|
-
return eagerResult;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Returns [] when there is no lazy backend: eager looks URLs up by id and
|
|
289
|
-
// never touches a resource's relations.
|
|
290
|
-
getRequiredRelations(): string[] {
|
|
291
|
-
if (this.lazyUrlService) {
|
|
292
|
-
return this.lazyUrlService.getRequiredRelations();
|
|
293
|
-
}
|
|
294
|
-
return [];
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Columns a resource of this type must carry for the lazy backend to build
|
|
298
|
-
// its URL. [] with no lazy backend: eager looks URLs up by id and never
|
|
299
|
-
// reads these fields.
|
|
300
|
-
getRequiredFields(routerType: string): string[] {
|
|
301
|
-
if (this.lazyUrlService) {
|
|
302
|
-
return this.lazyUrlService.getRequiredFields(routerType);
|
|
303
|
-
}
|
|
304
|
-
return [];
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
hasFinished(): boolean {
|
|
308
|
-
if (this.isLazy()) {
|
|
309
|
-
return this.lazyUrlService!.hasFinished();
|
|
310
|
-
}
|
|
311
|
-
// Track eager when comparing: lazy always reports ready and would gate traffic in early.
|
|
312
|
-
return this.urlService.hasFinished();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// While comparing, register on both so lazy sees the same routers as eager.
|
|
316
|
-
onRouterAddedType(...args: unknown[]): unknown {
|
|
317
|
-
if (this.isComparing()) {
|
|
318
|
-
this._runLazyHook('onRouterAddedType', () => this.lazyUrlService!.onRouterAddedType(...args));
|
|
319
|
-
return this.urlService.onRouterAddedType(...args);
|
|
320
|
-
}
|
|
321
|
-
if (this.lazyUrlService) {
|
|
322
|
-
return this.lazyUrlService.onRouterAddedType(...args);
|
|
323
|
-
}
|
|
324
|
-
return this.urlService.onRouterAddedType(...args);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
onRouterUpdated(...args: unknown[]): unknown {
|
|
328
|
-
if (this.isComparing()) {
|
|
329
|
-
this._runLazyHook('onRouterUpdated', () => this.lazyUrlService!.onRouterUpdated(...args));
|
|
330
|
-
return this.urlService.onRouterUpdated(...args);
|
|
331
|
-
}
|
|
332
|
-
if (this.lazyUrlService) {
|
|
333
|
-
return this.lazyUrlService.onRouterUpdated(...args);
|
|
334
|
-
}
|
|
335
|
-
return this.urlService.onRouterUpdated(...args);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Reset all router registrations. Used when routes.yaml is reloaded in
|
|
340
|
-
* lazy mode. In eager mode the URL service handles resets via its queue.
|
|
341
|
-
*/
|
|
342
|
-
reset(): void {
|
|
343
|
-
if (this.lazyUrlService) {
|
|
344
|
-
this._runLazyHook('reset', () => this.lazyUrlService!.reset());
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Runs a lazy router hook in compare mode. Lazy failures are swallowed and
|
|
349
|
-
// reported so they can never block the authoritative eager hook (or, for
|
|
350
|
-
// reset, break a routes reload).
|
|
351
|
-
private _runLazyHook(method: string, fn: () => void): void {
|
|
352
|
-
try {
|
|
353
|
-
fn();
|
|
354
|
-
} catch (err) {
|
|
355
|
-
this._reportLazyError(method, err as Error, {});
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Runs lazy alongside eager and logs any divergence; eager's value is always
|
|
360
|
-
// returned. Lazy errors are swallowed so a comparison can't break a request.
|
|
361
|
-
private _compare(
|
|
362
|
-
method: string,
|
|
363
|
-
eagerValue: unknown,
|
|
364
|
-
getLazyValue: () => unknown,
|
|
365
|
-
context: Record<string, unknown>,
|
|
366
|
-
isEqual: (a: unknown, b: unknown) => boolean = (a, b) => a === b
|
|
367
|
-
): void {
|
|
368
|
-
let lazyValue: unknown;
|
|
369
|
-
try {
|
|
370
|
-
lazyValue = getLazyValue();
|
|
371
|
-
} catch (err) {
|
|
372
|
-
this._reportLazyError(method, err as Error, context);
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
private async _compareAsync(
|
|
379
|
-
method: string,
|
|
380
|
-
eagerValue: unknown,
|
|
381
|
-
getLazyValue: () => Promise<unknown>,
|
|
382
|
-
context: Record<string, unknown>,
|
|
383
|
-
isEqual: (a: unknown, b: unknown) => boolean = (a, b) => a === b
|
|
384
|
-
): Promise<void> {
|
|
385
|
-
let lazyValue: unknown;
|
|
386
|
-
try {
|
|
387
|
-
lazyValue = await getLazyValue();
|
|
388
|
-
} catch (err) {
|
|
389
|
-
this._reportLazyError(method, err as Error, context);
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
private _isNotFound(value: unknown): boolean {
|
|
396
|
-
return typeof value === 'string' && value.endsWith('/404/');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// resolveUrl's contract is which resource a path maps to, not the exact
|
|
400
|
-
// serialized record. Eager (raw-knex) and lazy (model.toJSON) shape the same
|
|
401
|
-
// DB row differently — lazy carries a `parent` key eager omits, eager keeps
|
|
402
|
-
// __GHOST_URL__ placeholders lazy expands, timestamps differ in precision —
|
|
403
|
-
// so comparing whole records reports the same resolved resource as a
|
|
404
|
-
// mismatch. Compare resolved identity instead.
|
|
405
|
-
private _resolvesToSameResource(a: unknown, b: unknown): boolean {
|
|
406
|
-
if (a === null || b === null) {
|
|
407
|
-
return a === b;
|
|
408
|
-
}
|
|
409
|
-
const ra = a as Resource;
|
|
410
|
-
const rb = b as Resource;
|
|
411
|
-
return ra.id === rb.id && ra.type === rb.type;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Divergences that are not lazy regressions, so logging them only buries
|
|
415
|
-
// the ones that are. Each branch is a class confirmed from production
|
|
416
|
-
// compare data.
|
|
417
|
-
private _isExpectedDivergence(
|
|
418
|
-
method: string,
|
|
419
|
-
eagerValue: unknown,
|
|
420
|
-
lazyValue: unknown,
|
|
421
|
-
context: Record<string, unknown>
|
|
422
|
-
): boolean {
|
|
423
|
-
// Eager leaves tags/authors with no published posts out of its URL map
|
|
424
|
-
// (the shouldHavePosts gate) so they resolve to /404/, while lazy has no
|
|
425
|
-
// cheap way to run that check and returns the real URL. Eager cache
|
|
426
|
-
// staleness (a tag gaining its first post after boot) looks the same.
|
|
427
|
-
// Whether to keep lazy's behaviour is still open, but either way it is
|
|
428
|
-
// not a lazy bug, so exclude it to surface the divergences that are.
|
|
429
|
-
if (method === 'getUrlForResource'
|
|
430
|
-
&& (context.type === 'tags' || context.type === 'authors')
|
|
431
|
-
&& this._isNotFound(eagerValue) && !this._isNotFound(lazyValue)) {
|
|
432
|
-
return true;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// A site's owner starts with the default `ghost-user` slug and is
|
|
436
|
-
// renamed during setup. The rename emits no event the eager cache
|
|
437
|
-
// consumes, so eager serves /author/ghost-user/ until the next boot
|
|
438
|
-
// while lazy has the real slug from the database.
|
|
439
|
-
if (method === 'getUrlForResource'
|
|
440
|
-
&& context.type === 'authors'
|
|
441
|
-
&& typeof eagerValue === 'string' && eagerValue.endsWith('/author/ghost-user/')
|
|
442
|
-
&& !this._isNotFound(lazyValue)) {
|
|
443
|
-
return true;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// lazy returns /404/ where eager serves a real URL: eager cached a
|
|
447
|
-
// resource that is no longer routable (unpublished or deleted since,
|
|
448
|
-
// with no event to evict it). Suppress only when the resource is
|
|
449
|
-
// provably not published — a published resource lazy refuses to route
|
|
450
|
-
// is a real lazy bug, so that keeps logging.
|
|
451
|
-
if (method === 'getUrlForResource'
|
|
452
|
-
&& this._isNotFound(lazyValue) && !this._isNotFound(eagerValue)
|
|
453
|
-
&& typeof context.status === 'string' && context.status !== 'published') {
|
|
454
|
-
return true;
|
|
455
|
-
}
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
private _reportMismatch(
|
|
460
|
-
method: string,
|
|
461
|
-
eagerValue: unknown,
|
|
462
|
-
lazyValue: unknown,
|
|
463
|
-
context: Record<string, unknown>,
|
|
464
|
-
isEqual: (a: unknown, b: unknown) => boolean
|
|
465
|
-
): void {
|
|
466
|
-
if (!isEqual(eagerValue, lazyValue)) {
|
|
467
|
-
if (this._isExpectedDivergence(method, eagerValue, lazyValue, context)) {
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
const {caller, ...details} = context;
|
|
471
|
-
const report = new errors.InternalServerError({
|
|
472
|
-
message: 'URL service parity mismatch',
|
|
473
|
-
code: 'LAZY_URL_PARITY_MISMATCH',
|
|
474
|
-
errorDetails: {method, eager: eagerValue, lazy: lazyValue, ...details}
|
|
475
|
-
});
|
|
476
|
-
this._applyCallerStack(report, caller);
|
|
477
|
-
this._report(report);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
private _reportLazyError(method: string, err: Error, context: Record<string, unknown>): void {
|
|
482
|
-
const {caller, ...details} = context;
|
|
483
|
-
const report = new errors.InternalServerError({
|
|
484
|
-
message: 'Lazy URL service threw during comparison',
|
|
485
|
-
code: 'LAZY_URL_COMPARE_ERROR',
|
|
486
|
-
err,
|
|
487
|
-
errorDetails: {method, ...details}
|
|
488
|
-
});
|
|
489
|
-
// @tryghost/errors copies the wrapped error's enumerable props over the
|
|
490
|
-
// new error, so a thrown error carrying its own errorDetails (e.g. the
|
|
491
|
-
// thin-resource report) silently clobbers the compare context passed
|
|
492
|
-
// above. Re-merge after construction so both survive in the logs.
|
|
493
|
-
const innerDetails = (err as {errorDetails?: Record<string, unknown>}).errorDetails;
|
|
494
|
-
report.errorDetails = {method, ...details, ...innerDetails};
|
|
495
|
-
this._applyCallerStack(report, caller);
|
|
496
|
-
this._report(report);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// The report's own stack is setImmediate scaffolding — the caller frames
|
|
500
|
-
// captured at call time are the stack worth logging.
|
|
501
|
-
private _applyCallerStack(report: Error, caller: unknown): void {
|
|
502
|
-
if (typeof caller !== 'string') {
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const frames = caller.split('\n').slice(1).join('\n');
|
|
506
|
-
report.stack = `${report.name}: ${report.message}\n${frames}`;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
private _report(error: Error): void {
|
|
510
|
-
logging.error(error);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// `export class` already emits `exports.UrlServiceFacade`. We additionally
|
|
515
|
-
// re-attach `module.exports = UrlServiceFacade` AND keep the named export, so
|
|
516
|
-
// both `const UrlServiceFacade = require('./url-service-facade')` and
|
|
517
|
-
// `const { UrlServiceFacade } = require('./url-service-facade')` work.
|
|
518
|
-
module.exports = UrlServiceFacade;
|
|
519
|
-
module.exports.UrlServiceFacade = UrlServiceFacade;
|