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,276 +0,0 @@
|
|
|
1
|
-
const nql = require('@tryghost/nql');
|
|
2
|
-
const debug = require('@tryghost/debug')('services:url:generator');
|
|
3
|
-
const localUtils = require('../../../shared/url-utils').default;
|
|
4
|
-
|
|
5
|
-
// @TODO: merge with filter plugin
|
|
6
|
-
const EXPANSIONS = [{
|
|
7
|
-
key: 'author',
|
|
8
|
-
replacement: 'authors.slug'
|
|
9
|
-
}, {
|
|
10
|
-
key: 'tags',
|
|
11
|
-
replacement: 'tags.slug'
|
|
12
|
-
}, {
|
|
13
|
-
key: 'tag',
|
|
14
|
-
replacement: 'tags.slug'
|
|
15
|
-
}, {
|
|
16
|
-
key: 'authors',
|
|
17
|
-
replacement: 'authors.slug'
|
|
18
|
-
}, {
|
|
19
|
-
key: 'primary_tag',
|
|
20
|
-
replacement: 'primary_tag.slug'
|
|
21
|
-
}, {
|
|
22
|
-
key: 'primary_author',
|
|
23
|
-
replacement: 'primary_author.slug'
|
|
24
|
-
}];
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The UrlGenerator class is responsible to generate urls based on a router's conditions.
|
|
28
|
-
* It is the component which sits between routers and resources and connects them together.
|
|
29
|
-
* Each url generator can own resources. Each resource can only be owned by one generator,
|
|
30
|
-
* because each resource can only live on one url at a time.
|
|
31
|
-
*
|
|
32
|
-
* Each router is represented by a url generator.
|
|
33
|
-
*/
|
|
34
|
-
class UrlGenerator {
|
|
35
|
-
/**
|
|
36
|
-
* @param {Object} options
|
|
37
|
-
* @param {string} options.identifier frontend router ID reference
|
|
38
|
-
* @param {string} options.filter NQL filter string
|
|
39
|
-
* @param {string} options.resourceType resource type (e.g. 'posts', 'tags')
|
|
40
|
-
* @param {string} options.permalink permalink string
|
|
41
|
-
* @param {Object} options.queue instance of the backend Queue
|
|
42
|
-
* @param {Object} options.resources instance of the backend Resources
|
|
43
|
-
* @param {Object} options.urls instance of the backend URLs (used to store the urls)
|
|
44
|
-
* @param {number} options.position an ID of the generator
|
|
45
|
-
*/
|
|
46
|
-
constructor({identifier, filter, resourceType, permalink, queue, resources, urls, position}) {
|
|
47
|
-
this.identifier = identifier;
|
|
48
|
-
this.resourceType = resourceType;
|
|
49
|
-
this.permalink = permalink;
|
|
50
|
-
this.queue = queue;
|
|
51
|
-
this.urls = urls;
|
|
52
|
-
this.resources = resources;
|
|
53
|
-
this.uid = position;
|
|
54
|
-
|
|
55
|
-
// CASE: routers can define custom filters, but not required.
|
|
56
|
-
if (filter) {
|
|
57
|
-
this.filter = filter;
|
|
58
|
-
this.nql = nql(this.filter, {
|
|
59
|
-
expansions: EXPANSIONS,
|
|
60
|
-
transformer: nql.utils.mapKeyValues({
|
|
61
|
-
key: {
|
|
62
|
-
from: 'page',
|
|
63
|
-
to: 'type'
|
|
64
|
-
},
|
|
65
|
-
values: [{
|
|
66
|
-
from: false,
|
|
67
|
-
to: 'post'
|
|
68
|
-
}, {
|
|
69
|
-
from: true,
|
|
70
|
-
to: 'page'
|
|
71
|
-
}]
|
|
72
|
-
})
|
|
73
|
-
});
|
|
74
|
-
debug('filter', this.filter);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
this._listeners();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* @NOTE: currently only used if the permalink setting changes and it's used for this url generator.
|
|
82
|
-
* @TODO: https://github.com/TryGhost/Ghost/issues/10699
|
|
83
|
-
*/
|
|
84
|
-
regenerateResources() {
|
|
85
|
-
const myResources = this.urls.getByGeneratorId(this.uid);
|
|
86
|
-
|
|
87
|
-
myResources.forEach((object) => {
|
|
88
|
-
this.urls.removeResourceId(object.resource.data.id);
|
|
89
|
-
object.resource.release();
|
|
90
|
-
this._try(object.resource);
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* @description Helper function to register listeners for each url generator instance.
|
|
96
|
-
* @private
|
|
97
|
-
*/
|
|
98
|
-
_listeners() {
|
|
99
|
-
/**
|
|
100
|
-
* Listen on two events:
|
|
101
|
-
*
|
|
102
|
-
* - init: bootstrap or url reset
|
|
103
|
-
* - added: resource was added to the database
|
|
104
|
-
*/
|
|
105
|
-
this.queue.register({
|
|
106
|
-
event: 'init',
|
|
107
|
-
tolerance: 100
|
|
108
|
-
}, this._onInit.bind(this));
|
|
109
|
-
|
|
110
|
-
this.queue.register({
|
|
111
|
-
event: 'added'
|
|
112
|
-
}, this._onAdded.bind(this));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* @description Listener which get's called when the resources were fully fetched from the database.
|
|
117
|
-
*
|
|
118
|
-
* Each url generator will be called and can try to own resources now.
|
|
119
|
-
*
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
_onInit() {
|
|
123
|
-
// @NOTE: get the resources of my type e.g. posts.
|
|
124
|
-
const resources = this.resources.getAllByType(this.resourceType);
|
|
125
|
-
|
|
126
|
-
debug('_onInit', this.resourceType, resources.length);
|
|
127
|
-
|
|
128
|
-
for (const resource of resources) {
|
|
129
|
-
this._try(resource);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @description Listener which get's called when a resource was added on runtime.
|
|
135
|
-
* @param {Object} event
|
|
136
|
-
* @param {string} event.type
|
|
137
|
-
* @param {string} event.id
|
|
138
|
-
* @private
|
|
139
|
-
*/
|
|
140
|
-
_onAdded(event) {
|
|
141
|
-
debug('_onAdded', this.toString());
|
|
142
|
-
|
|
143
|
-
// CASE: you are type "pages", but the incoming type is "users"
|
|
144
|
-
if (event.type !== this.resourceType) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const resource = this.resources.getByIdAndType(event.type, event.id);
|
|
149
|
-
this._try(resource);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* @description Try to own a resource and generate it's url if so.
|
|
154
|
-
* @param {import('./resource')} resource - instance of the Resource class
|
|
155
|
-
* @returns {boolean}
|
|
156
|
-
* @private
|
|
157
|
-
*/
|
|
158
|
-
_try(resource) {
|
|
159
|
-
/**
|
|
160
|
-
* CASE: another url generator has taken this resource already.
|
|
161
|
-
*
|
|
162
|
-
* We have to remember that, because each url generator can generate a different url
|
|
163
|
-
* for a resource. So we can't directly check `this.urls.getUrl(url)`.
|
|
164
|
-
*/
|
|
165
|
-
if (resource.isReserved()) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// CASE 1: route has no custom filter, it will own the resource for sure
|
|
170
|
-
let shouldReserve = !this.filter;
|
|
171
|
-
|
|
172
|
-
// CASE 2: find out if my filter matches the resource
|
|
173
|
-
if (!shouldReserve) {
|
|
174
|
-
try {
|
|
175
|
-
shouldReserve = this.nql.queryJSON(resource.data);
|
|
176
|
-
} catch (err) {
|
|
177
|
-
debug(`Failed to queryJSON with filter "${this.filter}"`, err);
|
|
178
|
-
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (shouldReserve) {
|
|
184
|
-
const url = this._generateUrl(resource);
|
|
185
|
-
this.urls.add({
|
|
186
|
-
url: url,
|
|
187
|
-
generatorId: this.uid,
|
|
188
|
-
resource: resource
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
resource.reserve();
|
|
192
|
-
this._resourceListeners(resource);
|
|
193
|
-
return true;
|
|
194
|
-
} else {
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* @description Generate url based on the permalink configuration of the target router.
|
|
201
|
-
*
|
|
202
|
-
* @NOTE We currently generate relative urls (https://github.com/TryGhost/Ghost/commit/7b0d5d465ba41073db0c3c72006da625fa11df32).
|
|
203
|
-
*/
|
|
204
|
-
_generateUrl(resource) {
|
|
205
|
-
return localUtils.replacePermalink(this.permalink, resource.data);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* @description Helper function to register resource listeners.
|
|
210
|
-
*
|
|
211
|
-
* I want to know if my resources changes.
|
|
212
|
-
*
|
|
213
|
-
* If the owned resource get's updated, we simply release/free the resource and push it back to the queue.
|
|
214
|
-
* This is the easiest, less error prone implementation.
|
|
215
|
-
*
|
|
216
|
-
* Imagine you have two collections: `featured:true` and `type:page`.
|
|
217
|
-
* If a published post status get's featured and you have not explicitly defined `featured:false`, we wouldn't
|
|
218
|
-
* be able to figure out if this resource still belongs to me, because the filter still matches.
|
|
219
|
-
*/
|
|
220
|
-
_resourceListeners(resource) {
|
|
221
|
-
const onUpdate = (updatedResource) => {
|
|
222
|
-
// 1. remove old resource
|
|
223
|
-
this.urls.removeResourceId(updatedResource.data.id);
|
|
224
|
-
|
|
225
|
-
// 2. free resource, the url <-> resource connection no longer exists
|
|
226
|
-
updatedResource.release();
|
|
227
|
-
|
|
228
|
-
// 3. post has the change to get owned from a different collection again
|
|
229
|
-
debug('put back in queue', updatedResource.data.id);
|
|
230
|
-
|
|
231
|
-
this.queue.start({
|
|
232
|
-
event: 'added',
|
|
233
|
-
action: 'added:' + resource.data.id,
|
|
234
|
-
eventData: {
|
|
235
|
-
id: resource.data.id,
|
|
236
|
-
type: this.resourceType
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const onRemoved = (removedResource) => {
|
|
242
|
-
this.urls.removeResourceId(removedResource.data.id);
|
|
243
|
-
removedResource.release();
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
resource.removeAllListeners();
|
|
247
|
-
resource.addListener('updated', onUpdate.bind(this));
|
|
248
|
-
resource.addListener('removed', onRemoved.bind(this));
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* @description Figure out if this url generator own's a resource id.
|
|
253
|
-
* @param {string} id
|
|
254
|
-
* @returns {boolean}
|
|
255
|
-
*/
|
|
256
|
-
hasId(id) {
|
|
257
|
-
const existingUrl = this.urls.getByResourceId(id);
|
|
258
|
-
|
|
259
|
-
if (existingUrl && existingUrl.generatorId === this.uid) {
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* @description Get all urls of this url generator.
|
|
268
|
-
* NOTE: the method is only used for testing purposes at the moment.
|
|
269
|
-
* @returns {Array}
|
|
270
|
-
*/
|
|
271
|
-
getUrls() {
|
|
272
|
-
return this.urls.getByGeneratorId(this.uid);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
module.exports = UrlGenerator;
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* UrlServiceFacade
|
|
4
|
-
*
|
|
5
|
-
* Sits in front of the URL service so callers can use a stable, resource-based
|
|
6
|
-
* interface regardless of the underlying implementation. The facade can be
|
|
7
|
-
* built with two backends:
|
|
8
|
-
*
|
|
9
|
-
* - urlService: the legacy eager UrlService that precomputes a full
|
|
10
|
-
* resource → URL map at boot.
|
|
11
|
-
* - lazyUrlService: an on-demand implementation (LazyUrlService) that
|
|
12
|
-
* computes URLs and ownership per call.
|
|
13
|
-
*
|
|
14
|
-
* When `lazyUrlService` is provided the facade routes calls to it; otherwise
|
|
15
|
-
* it delegates to the eager `urlService`. This lets the lazy implementation be
|
|
16
|
-
* swapped in behind a config flag without touching individual callers.
|
|
17
|
-
*/
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.UrlServiceFacade = void 0;
|
|
20
|
-
const _ = require('lodash');
|
|
21
|
-
const logging = require('@tryghost/logging');
|
|
22
|
-
const errors = require('@tryghost/errors');
|
|
23
|
-
class UrlServiceFacade {
|
|
24
|
-
urlService;
|
|
25
|
-
lazyUrlService;
|
|
26
|
-
compare;
|
|
27
|
-
fetchRoutableResources;
|
|
28
|
-
enumComparesInFlight;
|
|
29
|
-
constructor({ urlService, lazyUrlService = null, compare = false, fetchRoutableResources = null }) {
|
|
30
|
-
this.urlService = urlService;
|
|
31
|
-
this.lazyUrlService = lazyUrlService;
|
|
32
|
-
this.compare = compare;
|
|
33
|
-
this.fetchRoutableResources = fetchRoutableResources;
|
|
34
|
-
this.enumComparesInFlight = new Set();
|
|
35
|
-
}
|
|
36
|
-
isLazy() {
|
|
37
|
-
return !!this.lazyUrlService && !this.compare;
|
|
38
|
-
}
|
|
39
|
-
isComparing() {
|
|
40
|
-
return this.compare && !!this.lazyUrlService;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* The full resource record is required: the lazy backend evaluates NQL
|
|
44
|
-
* filters and applies permalink templates against it.
|
|
45
|
-
*/
|
|
46
|
-
getUrlForResource(resource, options) {
|
|
47
|
-
if (this.isLazy()) {
|
|
48
|
-
return this.lazyUrlService.getUrlForResource(resource, options);
|
|
49
|
-
}
|
|
50
|
-
const url = this.urlService.getUrlByResourceId(resource.id, options);
|
|
51
|
-
if (this.isComparing() && !options?.skipComparison) {
|
|
52
|
-
const context = this._compareContext(resource, options?.serializerContext ? { serializer: options.serializerContext } : {});
|
|
53
|
-
// Snapshot: callers mutate the resource's nested objects in place
|
|
54
|
-
// after this returns, but the comparison runs later via setImmediate.
|
|
55
|
-
const snapshot = _.cloneDeep(resource);
|
|
56
|
-
setImmediate(() => this._compare('getUrlForResource', url, () => this.lazyUrlService.getUrlForResource(snapshot, options), context));
|
|
57
|
-
}
|
|
58
|
-
return url;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* All routable rows of a type. Eager answers from its in-memory cache;
|
|
62
|
-
* lazy fetches from the database on demand. In compare mode the eager
|
|
63
|
-
* answer is returned and the lazy fetch runs in the background, with any
|
|
64
|
-
* id-set divergence logged — counts and id samples only, never row
|
|
65
|
-
* bodies (a large site has hundreds of thousands).
|
|
66
|
-
*/
|
|
67
|
-
async getRoutableResources(type, options = {}) {
|
|
68
|
-
if (this.isLazy()) {
|
|
69
|
-
if (!this.fetchRoutableResources) {
|
|
70
|
-
throw new errors.IncorrectUsageError({
|
|
71
|
-
message: 'getRoutableResources requires an injected fetchRoutableResources in lazy mode'
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return this.fetchRoutableResources(type, options);
|
|
75
|
-
}
|
|
76
|
-
const eagerRows = (this.urlService.resources.getAllByType(type) || []).map(resource => resource.data);
|
|
77
|
-
// Single-flight per type: rapid invalidation cycles must not stack
|
|
78
|
-
// concurrent full-table comparison walks.
|
|
79
|
-
if (this.isComparing() && this.fetchRoutableResources && !this.enumComparesInFlight.has(type)) {
|
|
80
|
-
this.enumComparesInFlight.add(type);
|
|
81
|
-
void this._compareRoutableResources(type, options, eagerRows).finally(() => {
|
|
82
|
-
this.enumComparesInFlight.delete(type);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return eagerRows;
|
|
86
|
-
}
|
|
87
|
-
async _compareRoutableResources(type, options, eagerRows) {
|
|
88
|
-
let lazyRows;
|
|
89
|
-
try {
|
|
90
|
-
lazyRows = await this.fetchRoutableResources(type, options);
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
this._reportLazyError('getRoutableResources', err, { type });
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
const eagerIds = new Set(eagerRows.map(row => row.id));
|
|
97
|
-
const lazyIds = new Set(lazyRows.map(row => row.id));
|
|
98
|
-
const missingFromLazy = [...eagerIds].filter(id => !lazyIds.has(id));
|
|
99
|
-
const extraInLazy = [...lazyIds].filter(id => !eagerIds.has(id));
|
|
100
|
-
if (!missingFromLazy.length && !extraInLazy.length) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
this._report(new errors.InternalServerError({
|
|
104
|
-
message: 'URL service parity mismatch',
|
|
105
|
-
code: 'LAZY_URL_PARITY_MISMATCH',
|
|
106
|
-
errorDetails: {
|
|
107
|
-
method: 'getRoutableResources',
|
|
108
|
-
type,
|
|
109
|
-
eagerCount: eagerIds.size,
|
|
110
|
-
lazyCount: lazyIds.size,
|
|
111
|
-
missingFromLazy: missingFromLazy.slice(0, 10),
|
|
112
|
-
extraInLazy: extraInLazy.slice(0, 10)
|
|
113
|
-
}
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
ownsResource(routerIdentifier, resource) {
|
|
117
|
-
if (this.isLazy()) {
|
|
118
|
-
return this.lazyUrlService.ownsResource(routerIdentifier, resource);
|
|
119
|
-
}
|
|
120
|
-
const owns = this.urlService.owns(routerIdentifier, resource.id);
|
|
121
|
-
if (this.isComparing()) {
|
|
122
|
-
const context = this._compareContext(resource, { routerIdentifier });
|
|
123
|
-
// Snapshot, as in getUrlForResource.
|
|
124
|
-
const snapshot = _.cloneDeep(resource);
|
|
125
|
-
setImmediate(() => this._compare('ownsResource', owns, () => this.lazyUrlService.ownsResource(routerIdentifier, snapshot), context));
|
|
126
|
-
}
|
|
127
|
-
return owns;
|
|
128
|
-
}
|
|
129
|
-
// Context for a compare report. Must be built synchronously in the calling
|
|
130
|
-
// frame: the comparison itself runs from setImmediate, where the caller's
|
|
131
|
-
// stack is gone — so a lazy throw reported there names only the URL service
|
|
132
|
-
// internals, not which caller handed over the (possibly thin) resource.
|
|
133
|
-
// `caller` recaptures those frames; `resourceKeys` fingerprints the shape
|
|
134
|
-
// the caller passed (e.g. a Content-API-serialized post vs a full model).
|
|
135
|
-
_compareContext(resource, extra = {}) {
|
|
136
|
-
const caller = {};
|
|
137
|
-
Error.captureStackTrace(caller, this._compareContext);
|
|
138
|
-
return {
|
|
139
|
-
type: resource.type,
|
|
140
|
-
id: resource.id,
|
|
141
|
-
status: resource.status,
|
|
142
|
-
resourceKeys: Object.keys(resource),
|
|
143
|
-
caller: caller.stack,
|
|
144
|
-
...extra
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Reverse URL lookup. Returns a flat resource shape (e.g. `{type, id, slug}`)
|
|
149
|
-
* rather than the legacy `{config: {type}, data: {...}}` envelope. Async to
|
|
150
|
-
* match the lazy implementation's contract.
|
|
151
|
-
*/
|
|
152
|
-
async resolveUrl(urlPath) {
|
|
153
|
-
if (this.isLazy()) {
|
|
154
|
-
return this.lazyUrlService.resolveUrl(urlPath);
|
|
155
|
-
}
|
|
156
|
-
const resource = this.urlService.getResource(urlPath);
|
|
157
|
-
// The routing-level type ('posts', 'pages', 'tags', 'authors') wins
|
|
158
|
-
// over any DB type field on resource.data so the flat Resource is
|
|
159
|
-
// unambiguous.
|
|
160
|
-
const eagerResult = resource
|
|
161
|
-
? Object.assign({}, resource.data, { type: resource.config.type })
|
|
162
|
-
: null;
|
|
163
|
-
if (this.isComparing()) {
|
|
164
|
-
// Fire-and-forget: don't await lazy so the reverse lookup adds no
|
|
165
|
-
// latency for its callers; the lazy DB read runs in the background.
|
|
166
|
-
// Snapshot the eager result, as in getUrlForResource.
|
|
167
|
-
const eagerSnapshot = _.cloneDeep(eagerResult);
|
|
168
|
-
void this._compareAsync('resolveUrl', eagerSnapshot, () => this.lazyUrlService.resolveUrl(urlPath), { path: urlPath }, (a, b) => this._resolvesToSameResource(a, b));
|
|
169
|
-
}
|
|
170
|
-
return eagerResult;
|
|
171
|
-
}
|
|
172
|
-
// Returns [] when there is no lazy backend: eager looks URLs up by id and
|
|
173
|
-
// never touches a resource's relations.
|
|
174
|
-
getRequiredRelations() {
|
|
175
|
-
if (this.lazyUrlService) {
|
|
176
|
-
return this.lazyUrlService.getRequiredRelations();
|
|
177
|
-
}
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
// Columns a resource of this type must carry for the lazy backend to build
|
|
181
|
-
// its URL. [] with no lazy backend: eager looks URLs up by id and never
|
|
182
|
-
// reads these fields.
|
|
183
|
-
getRequiredFields(routerType) {
|
|
184
|
-
if (this.lazyUrlService) {
|
|
185
|
-
return this.lazyUrlService.getRequiredFields(routerType);
|
|
186
|
-
}
|
|
187
|
-
return [];
|
|
188
|
-
}
|
|
189
|
-
hasFinished() {
|
|
190
|
-
if (this.isLazy()) {
|
|
191
|
-
return this.lazyUrlService.hasFinished();
|
|
192
|
-
}
|
|
193
|
-
// Track eager when comparing: lazy always reports ready and would gate traffic in early.
|
|
194
|
-
return this.urlService.hasFinished();
|
|
195
|
-
}
|
|
196
|
-
// While comparing, register on both so lazy sees the same routers as eager.
|
|
197
|
-
onRouterAddedType(...args) {
|
|
198
|
-
if (this.isComparing()) {
|
|
199
|
-
this._runLazyHook('onRouterAddedType', () => this.lazyUrlService.onRouterAddedType(...args));
|
|
200
|
-
return this.urlService.onRouterAddedType(...args);
|
|
201
|
-
}
|
|
202
|
-
if (this.lazyUrlService) {
|
|
203
|
-
return this.lazyUrlService.onRouterAddedType(...args);
|
|
204
|
-
}
|
|
205
|
-
return this.urlService.onRouterAddedType(...args);
|
|
206
|
-
}
|
|
207
|
-
onRouterUpdated(...args) {
|
|
208
|
-
if (this.isComparing()) {
|
|
209
|
-
this._runLazyHook('onRouterUpdated', () => this.lazyUrlService.onRouterUpdated(...args));
|
|
210
|
-
return this.urlService.onRouterUpdated(...args);
|
|
211
|
-
}
|
|
212
|
-
if (this.lazyUrlService) {
|
|
213
|
-
return this.lazyUrlService.onRouterUpdated(...args);
|
|
214
|
-
}
|
|
215
|
-
return this.urlService.onRouterUpdated(...args);
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Reset all router registrations. Used when routes.yaml is reloaded in
|
|
219
|
-
* lazy mode. In eager mode the URL service handles resets via its queue.
|
|
220
|
-
*/
|
|
221
|
-
reset() {
|
|
222
|
-
if (this.lazyUrlService) {
|
|
223
|
-
this._runLazyHook('reset', () => this.lazyUrlService.reset());
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// Runs a lazy router hook in compare mode. Lazy failures are swallowed and
|
|
227
|
-
// reported so they can never block the authoritative eager hook (or, for
|
|
228
|
-
// reset, break a routes reload).
|
|
229
|
-
_runLazyHook(method, fn) {
|
|
230
|
-
try {
|
|
231
|
-
fn();
|
|
232
|
-
}
|
|
233
|
-
catch (err) {
|
|
234
|
-
this._reportLazyError(method, err, {});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
// Runs lazy alongside eager and logs any divergence; eager's value is always
|
|
238
|
-
// returned. Lazy errors are swallowed so a comparison can't break a request.
|
|
239
|
-
_compare(method, eagerValue, getLazyValue, context, isEqual = (a, b) => a === b) {
|
|
240
|
-
let lazyValue;
|
|
241
|
-
try {
|
|
242
|
-
lazyValue = getLazyValue();
|
|
243
|
-
}
|
|
244
|
-
catch (err) {
|
|
245
|
-
this._reportLazyError(method, err, context);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
|
|
249
|
-
}
|
|
250
|
-
async _compareAsync(method, eagerValue, getLazyValue, context, isEqual = (a, b) => a === b) {
|
|
251
|
-
let lazyValue;
|
|
252
|
-
try {
|
|
253
|
-
lazyValue = await getLazyValue();
|
|
254
|
-
}
|
|
255
|
-
catch (err) {
|
|
256
|
-
this._reportLazyError(method, err, context);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
|
|
260
|
-
}
|
|
261
|
-
_isNotFound(value) {
|
|
262
|
-
return typeof value === 'string' && value.endsWith('/404/');
|
|
263
|
-
}
|
|
264
|
-
// resolveUrl's contract is which resource a path maps to, not the exact
|
|
265
|
-
// serialized record. Eager (raw-knex) and lazy (model.toJSON) shape the same
|
|
266
|
-
// DB row differently — lazy carries a `parent` key eager omits, eager keeps
|
|
267
|
-
// __GHOST_URL__ placeholders lazy expands, timestamps differ in precision —
|
|
268
|
-
// so comparing whole records reports the same resolved resource as a
|
|
269
|
-
// mismatch. Compare resolved identity instead.
|
|
270
|
-
_resolvesToSameResource(a, b) {
|
|
271
|
-
if (a === null || b === null) {
|
|
272
|
-
return a === b;
|
|
273
|
-
}
|
|
274
|
-
const ra = a;
|
|
275
|
-
const rb = b;
|
|
276
|
-
return ra.id === rb.id && ra.type === rb.type;
|
|
277
|
-
}
|
|
278
|
-
// Divergences that are not lazy regressions, so logging them only buries
|
|
279
|
-
// the ones that are. Each branch is a class confirmed from production
|
|
280
|
-
// compare data.
|
|
281
|
-
_isExpectedDivergence(method, eagerValue, lazyValue, context) {
|
|
282
|
-
// Eager leaves tags/authors with no published posts out of its URL map
|
|
283
|
-
// (the shouldHavePosts gate) so they resolve to /404/, while lazy has no
|
|
284
|
-
// cheap way to run that check and returns the real URL. Eager cache
|
|
285
|
-
// staleness (a tag gaining its first post after boot) looks the same.
|
|
286
|
-
// Whether to keep lazy's behaviour is still open, but either way it is
|
|
287
|
-
// not a lazy bug, so exclude it to surface the divergences that are.
|
|
288
|
-
if (method === 'getUrlForResource'
|
|
289
|
-
&& (context.type === 'tags' || context.type === 'authors')
|
|
290
|
-
&& this._isNotFound(eagerValue) && !this._isNotFound(lazyValue)) {
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
// A site's owner starts with the default `ghost-user` slug and is
|
|
294
|
-
// renamed during setup. The rename emits no event the eager cache
|
|
295
|
-
// consumes, so eager serves /author/ghost-user/ until the next boot
|
|
296
|
-
// while lazy has the real slug from the database.
|
|
297
|
-
if (method === 'getUrlForResource'
|
|
298
|
-
&& context.type === 'authors'
|
|
299
|
-
&& typeof eagerValue === 'string' && eagerValue.endsWith('/author/ghost-user/')
|
|
300
|
-
&& !this._isNotFound(lazyValue)) {
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
303
|
-
// lazy returns /404/ where eager serves a real URL: eager cached a
|
|
304
|
-
// resource that is no longer routable (unpublished or deleted since,
|
|
305
|
-
// with no event to evict it). Suppress only when the resource is
|
|
306
|
-
// provably not published — a published resource lazy refuses to route
|
|
307
|
-
// is a real lazy bug, so that keeps logging.
|
|
308
|
-
if (method === 'getUrlForResource'
|
|
309
|
-
&& this._isNotFound(lazyValue) && !this._isNotFound(eagerValue)
|
|
310
|
-
&& typeof context.status === 'string' && context.status !== 'published') {
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
315
|
-
_reportMismatch(method, eagerValue, lazyValue, context, isEqual) {
|
|
316
|
-
if (!isEqual(eagerValue, lazyValue)) {
|
|
317
|
-
if (this._isExpectedDivergence(method, eagerValue, lazyValue, context)) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
const { caller, ...details } = context;
|
|
321
|
-
const report = new errors.InternalServerError({
|
|
322
|
-
message: 'URL service parity mismatch',
|
|
323
|
-
code: 'LAZY_URL_PARITY_MISMATCH',
|
|
324
|
-
errorDetails: { method, eager: eagerValue, lazy: lazyValue, ...details }
|
|
325
|
-
});
|
|
326
|
-
this._applyCallerStack(report, caller);
|
|
327
|
-
this._report(report);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
_reportLazyError(method, err, context) {
|
|
331
|
-
const { caller, ...details } = context;
|
|
332
|
-
const report = new errors.InternalServerError({
|
|
333
|
-
message: 'Lazy URL service threw during comparison',
|
|
334
|
-
code: 'LAZY_URL_COMPARE_ERROR',
|
|
335
|
-
err,
|
|
336
|
-
errorDetails: { method, ...details }
|
|
337
|
-
});
|
|
338
|
-
// @tryghost/errors copies the wrapped error's enumerable props over the
|
|
339
|
-
// new error, so a thrown error carrying its own errorDetails (e.g. the
|
|
340
|
-
// thin-resource report) silently clobbers the compare context passed
|
|
341
|
-
// above. Re-merge after construction so both survive in the logs.
|
|
342
|
-
const innerDetails = err.errorDetails;
|
|
343
|
-
report.errorDetails = { method, ...details, ...innerDetails };
|
|
344
|
-
this._applyCallerStack(report, caller);
|
|
345
|
-
this._report(report);
|
|
346
|
-
}
|
|
347
|
-
// The report's own stack is setImmediate scaffolding — the caller frames
|
|
348
|
-
// captured at call time are the stack worth logging.
|
|
349
|
-
_applyCallerStack(report, caller) {
|
|
350
|
-
if (typeof caller !== 'string') {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
const frames = caller.split('\n').slice(1).join('\n');
|
|
354
|
-
report.stack = `${report.name}: ${report.message}\n${frames}`;
|
|
355
|
-
}
|
|
356
|
-
_report(error) {
|
|
357
|
-
logging.error(error);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
exports.UrlServiceFacade = UrlServiceFacade;
|
|
361
|
-
// `export class` already emits `exports.UrlServiceFacade`. We additionally
|
|
362
|
-
// re-attach `module.exports = UrlServiceFacade` AND keep the named export, so
|
|
363
|
-
// both `const UrlServiceFacade = require('./url-service-facade')` and
|
|
364
|
-
// `const { UrlServiceFacade } = require('./url-service-facade')` work.
|
|
365
|
-
module.exports = UrlServiceFacade;
|
|
366
|
-
module.exports.UrlServiceFacade = UrlServiceFacade;
|