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
|
@@ -3,21 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.LazyUrlService = void 0;
|
|
4
4
|
const debug = require('@tryghost/debug')('services:url:lazy');
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
|
+
const logging = require('@tryghost/logging');
|
|
6
7
|
const localUtils = require('../../../shared/url-utils').default;
|
|
7
8
|
const { matchPermalink, toLookupParams } = require('./permalink-matcher');
|
|
8
9
|
const { buildFilter, filterMatches, routerTypeOf } = require('./router-filter');
|
|
10
|
+
const { fetchRoutableResources: defaultFetchRoutableResources } = require('./routable-resources');
|
|
9
11
|
const urlConfig = require('./config');
|
|
10
|
-
// Per-resource-type
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// record columns each filter reads; a resource that reaches URL generation
|
|
15
|
-
// must carry them (a thin one is rejected — see _assertBaseFieldsPresent).
|
|
12
|
+
// Per-resource-type gate on which rows get a URL at all — the forward-lookup
|
|
13
|
+
// counterpart of the fetch filters in routable-resources.js. `fields` lists
|
|
14
|
+
// the record columns each filter reads; a resource that reaches URL generation
|
|
15
|
+
// must carry them (see _missingBaseFields).
|
|
16
16
|
//
|
|
17
17
|
// Authors are intentionally absent: users.visibility is schema-pinned to
|
|
18
|
-
// 'public' (isIn: [['public']]), so
|
|
19
|
-
//
|
|
20
|
-
//
|
|
18
|
+
// 'public' (isIn: [['public']]), so the visibility:public author filter never
|
|
19
|
+
// excludes anyone — every author is routable, and serialized authors drop
|
|
20
|
+
// visibility anyway (#10438).
|
|
21
21
|
const BASE_FILTERS = {
|
|
22
22
|
posts: { filter: 'status:published+type:post', fields: ['status', 'type'] },
|
|
23
23
|
pages: { filter: 'status:published+type:page', fields: ['status', 'type'] },
|
|
@@ -33,19 +33,18 @@ function buildBaseFilters() {
|
|
|
33
33
|
}
|
|
34
34
|
return baseFilters;
|
|
35
35
|
}
|
|
36
|
-
// Columns
|
|
37
|
-
// services/url/config.js)
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// router filters (and neither requires nor force-loads
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
// replaces it. The base filter is unaffected — it runs against the full record.
|
|
36
|
+
// Columns a router filter must not see (the `exclude` lists in
|
|
37
|
+
// services/url/config.js), keyed by resourceType. Ghost previously answered
|
|
38
|
+
// URLs from a precomputed cache that dropped these columns, so a filter
|
|
39
|
+
// referencing one matched it as absent — NQL reads absent as null. This
|
|
40
|
+
// service loads full records and would see the real value, so it strips them
|
|
41
|
+
// before evaluating router filters (and neither requires nor force-loads
|
|
42
|
+
// them), keeping routes.yaml files that rely on that behaviour working. The
|
|
43
|
+
// base filter above is unaffected — it runs against the full record.
|
|
45
44
|
function buildExcludedFilterFields() {
|
|
46
45
|
const excluded = new Map();
|
|
47
46
|
for (const entry of urlConfig) {
|
|
48
|
-
const exclude = entry?.
|
|
47
|
+
const exclude = entry?.exclude;
|
|
49
48
|
if (Array.isArray(exclude)) {
|
|
50
49
|
excluded.set(entry.type, new Set(exclude));
|
|
51
50
|
}
|
|
@@ -53,10 +52,18 @@ function buildExcludedFilterFields() {
|
|
|
53
52
|
return excluded;
|
|
54
53
|
}
|
|
55
54
|
const EMPTY_FIELD_SET = new Set();
|
|
55
|
+
// 1, 10, 100, ... — exact for integers, unlike a log10 comparison.
|
|
56
|
+
function isPowerOfTen(n) {
|
|
57
|
+
let power = 1;
|
|
58
|
+
while (power < n) {
|
|
59
|
+
power *= 10;
|
|
60
|
+
}
|
|
61
|
+
return power === n;
|
|
62
|
+
}
|
|
56
63
|
// Relation roots are loaded via getRequiredRelations (as withRelated), not as
|
|
57
64
|
// scalar columns; `page`/`type` are the router-type discriminator, always set
|
|
58
65
|
// on the resource. Everything else a router filter references is a scalar
|
|
59
|
-
// own-column the resource must carry to be routed
|
|
66
|
+
// own-column the resource must carry to be routed.
|
|
60
67
|
const FILTER_NON_SCALAR_FIELDS = new Set([
|
|
61
68
|
'tag', 'tags', 'author', 'authors', 'primary_tag', 'primary_author', 'page', 'type'
|
|
62
69
|
]);
|
|
@@ -85,20 +92,27 @@ function filterScalarFields(filter) {
|
|
|
85
92
|
}
|
|
86
93
|
const ROUTER_TYPE_TO_DB_TYPE = { posts: 'post', pages: 'page' };
|
|
87
94
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
95
|
+
* Ghost's URL service: computes URLs and ownership per call from the
|
|
96
|
+
* registered router configs. Forward lookups are pure; resolveUrl and
|
|
97
|
+
* getRoutableResources are the only DB-touching paths, and only through the
|
|
98
|
+
* injected findResource / fetchRoutableResources hooks.
|
|
92
99
|
*/
|
|
93
100
|
class LazyUrlService {
|
|
94
101
|
urlUtils;
|
|
95
102
|
findResource;
|
|
103
|
+
fetchRoutableResources;
|
|
96
104
|
// Router configs in registration order, which is also their priority.
|
|
97
105
|
routerConfigs;
|
|
98
106
|
requiredRelations;
|
|
99
107
|
baseFilters;
|
|
100
108
|
excludedFilterFields;
|
|
101
|
-
|
|
109
|
+
// True once routers have been registered. Cleared by reset() so the
|
|
110
|
+
// route-reload window re-gates the maintenance middleware.
|
|
111
|
+
routersReady;
|
|
112
|
+
// How many times each thin-resource cause has been seen — see
|
|
113
|
+
// _degradeThinResource.
|
|
114
|
+
reportedThinResources;
|
|
115
|
+
constructor({ urlUtils = localUtils, findResource, fetchRoutableResources = defaultFetchRoutableResources }) {
|
|
102
116
|
if (typeof findResource !== 'function') {
|
|
103
117
|
throw new errors.IncorrectUsageError({
|
|
104
118
|
message: 'LazyUrlService requires a findResource function'
|
|
@@ -106,10 +120,13 @@ class LazyUrlService {
|
|
|
106
120
|
}
|
|
107
121
|
this.urlUtils = urlUtils;
|
|
108
122
|
this.findResource = findResource;
|
|
123
|
+
this.fetchRoutableResources = fetchRoutableResources;
|
|
109
124
|
this.routerConfigs = [];
|
|
110
125
|
this.requiredRelations = null;
|
|
111
126
|
this.baseFilters = buildBaseFilters();
|
|
112
127
|
this.excludedFilterFields = buildExcludedFilterFields();
|
|
128
|
+
this.routersReady = false;
|
|
129
|
+
this.reportedThinResources = new Map();
|
|
113
130
|
}
|
|
114
131
|
onRouterAddedType(identifier, filter, resourceType, permalink) {
|
|
115
132
|
debug('onRouterAddedType', identifier, resourceType, permalink, filter);
|
|
@@ -121,6 +138,7 @@ class LazyUrlService {
|
|
|
121
138
|
compiledFilter: buildFilter(filter)
|
|
122
139
|
});
|
|
123
140
|
this.requiredRelations = null;
|
|
141
|
+
this.routersReady = true;
|
|
124
142
|
}
|
|
125
143
|
onRouterUpdated() {
|
|
126
144
|
// Defensive: a router update could change a filter the cache derived
|
|
@@ -130,6 +148,8 @@ class LazyUrlService {
|
|
|
130
148
|
reset() {
|
|
131
149
|
this.routerConfigs = [];
|
|
132
150
|
this.requiredRelations = null;
|
|
151
|
+
this.routersReady = false;
|
|
152
|
+
this.reportedThinResources.clear();
|
|
133
153
|
}
|
|
134
154
|
getRequiredRelations() {
|
|
135
155
|
if (this.requiredRelations !== null) {
|
|
@@ -157,10 +177,10 @@ class LazyUrlService {
|
|
|
157
177
|
this.requiredRelations = [...required];
|
|
158
178
|
return [...this.requiredRelations];
|
|
159
179
|
}
|
|
160
|
-
// Columns a resource of this type must carry for the
|
|
180
|
+
// Columns a resource of this type must carry for the service to build
|
|
161
181
|
// its URL: its base-filter columns plus the scalar columns its routers'
|
|
162
182
|
// permalinks substitute and filters read. Relations are covered separately
|
|
163
|
-
// by getRequiredRelations
|
|
183
|
+
// by getRequiredRelations.
|
|
164
184
|
getRequiredFields(routerType) {
|
|
165
185
|
const fields = new Set();
|
|
166
186
|
const excluded = this._excludedFilterFieldsFor(routerType);
|
|
@@ -192,9 +212,10 @@ class LazyUrlService {
|
|
|
192
212
|
fields.add(computed);
|
|
193
213
|
}
|
|
194
214
|
}
|
|
195
|
-
// Skip columns
|
|
196
|
-
//
|
|
197
|
-
//
|
|
215
|
+
// Skip the columns a router filter must not see: their filters are
|
|
216
|
+
// evaluated against an absent value, so requiring them would
|
|
217
|
+
// force-load a column that can't affect the result (see
|
|
218
|
+
// buildExcludedFilterFields).
|
|
198
219
|
filterScalarFields(config.filter).forEach((field) => {
|
|
199
220
|
if (!excluded.has(field)) {
|
|
200
221
|
fields.add(field);
|
|
@@ -204,40 +225,62 @@ class LazyUrlService {
|
|
|
204
225
|
return [...fields];
|
|
205
226
|
}
|
|
206
227
|
hasFinished() {
|
|
207
|
-
return
|
|
228
|
+
return this.routersReady;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* All routable rows of a type. The columns URL computation needs come
|
|
232
|
+
* from the active routing config, so the rows are never thin for it;
|
|
233
|
+
* callers name any extra columns they want back.
|
|
234
|
+
*/
|
|
235
|
+
async getRoutableResources(type, { columns = [] } = {}) {
|
|
236
|
+
return this.fetchRoutableResources(type, {
|
|
237
|
+
columns,
|
|
238
|
+
requiredFields: this.getRequiredFields(type),
|
|
239
|
+
requiredRelations: this.getRequiredRelations()
|
|
240
|
+
});
|
|
208
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* A thin resource — one missing a column its base filter or a router
|
|
244
|
+
* filter reads — is a caller bug: we cannot tell whether it routes. It is
|
|
245
|
+
* reported and degraded to /404/ rather than thrown, because 500ing a page
|
|
246
|
+
* that does route is worse than a 404 on one that may not.
|
|
247
|
+
*/
|
|
209
248
|
getUrlForResource(resource, options = {}) {
|
|
210
249
|
const routerType = routerTypeOf(resource);
|
|
211
250
|
if (!routerType) {
|
|
212
|
-
return this.
|
|
251
|
+
return this.notFoundUrl(options);
|
|
213
252
|
}
|
|
214
253
|
const record = this._recordForFilter(resource);
|
|
215
|
-
// The router (collection) filter is evaluated against
|
|
216
|
-
// column set; the base filter keeps the
|
|
217
|
-
//
|
|
254
|
+
// The router (collection) filter is evaluated against the reduced
|
|
255
|
+
// column set (see buildExcludedFilterFields); the base filter keeps the
|
|
256
|
+
// full record, since it reads status / type / visibility.
|
|
218
257
|
const filterRecord = this._recordForRouterFilter(record, routerType);
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
// only when a router for the type exists, since otherwise the resource
|
|
223
|
-
// 404s regardless.
|
|
258
|
+
// Only resources that pass the per-type base filter (visibility:public
|
|
259
|
+
// tags, status:published posts) get a URL. Checked only when a router
|
|
260
|
+
// for the type exists, since otherwise the resource 404s regardless.
|
|
224
261
|
if (this._hasRouterForType(routerType)) {
|
|
225
|
-
this.
|
|
262
|
+
const missing = this._missingBaseFields(routerType, resource);
|
|
263
|
+
if (missing) {
|
|
264
|
+
return this._degradeThinResource(resource, missing, options);
|
|
265
|
+
}
|
|
226
266
|
if (!this._baseFilterMatches(routerType, record)) {
|
|
227
|
-
return this.
|
|
267
|
+
return this.notFoundUrl(options);
|
|
228
268
|
}
|
|
229
269
|
}
|
|
230
270
|
for (const config of this.routerConfigs) {
|
|
231
271
|
if (config.resourceType !== routerType) {
|
|
232
272
|
continue;
|
|
233
273
|
}
|
|
234
|
-
this.
|
|
274
|
+
const missing = this._missingRouterFields(config, resource, routerType);
|
|
275
|
+
if (missing) {
|
|
276
|
+
return this._degradeThinResource(resource, missing, options);
|
|
277
|
+
}
|
|
235
278
|
if (filterMatches(config.compiledFilter, filterRecord)) {
|
|
236
279
|
const path = this.urlUtils.replacePermalink(config.permalink, resource);
|
|
237
280
|
return this._formatPath(path, options);
|
|
238
281
|
}
|
|
239
282
|
}
|
|
240
|
-
return this.
|
|
283
|
+
return this.notFoundUrl(options);
|
|
241
284
|
}
|
|
242
285
|
ownsResource(routerIdentifier, resource) {
|
|
243
286
|
if (!resource) {
|
|
@@ -247,15 +290,15 @@ class LazyUrlService {
|
|
|
247
290
|
if (!routerType) {
|
|
248
291
|
return false;
|
|
249
292
|
}
|
|
250
|
-
// A resource failing its base filter
|
|
251
|
-
//
|
|
293
|
+
// A resource failing its base filter has no URL, so no router owns it.
|
|
294
|
+
// Mirrors the base-filter gate in getUrlForResource.
|
|
252
295
|
const record = this._recordForFilter(resource);
|
|
253
296
|
if (!this._baseFilterMatches(routerType, record)) {
|
|
254
297
|
return false;
|
|
255
298
|
}
|
|
256
299
|
const filterRecord = this._recordForRouterFilter(record, routerType);
|
|
257
300
|
// Ownership is exclusive: only the first matching router of the type
|
|
258
|
-
// owns the resource
|
|
301
|
+
// owns the resource.
|
|
259
302
|
const owner = this.routerConfigs.find(c => c.resourceType === routerType && filterMatches(c.compiledFilter, filterRecord));
|
|
260
303
|
return !!owner && owner.identifier === routerIdentifier;
|
|
261
304
|
}
|
|
@@ -299,11 +342,11 @@ class LazyUrlService {
|
|
|
299
342
|
}
|
|
300
343
|
return null;
|
|
301
344
|
}
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
// captured params match it. Without this, derived/relation
|
|
305
|
-
// query can't filter on (year/month, primary_tag) would
|
|
306
|
-
// 200-ing a URL
|
|
345
|
+
// Only a URL that equals the resource's own generated (canonical) URL
|
|
346
|
+
// resolves, so we regenerate the record's URL for this permalink and
|
|
347
|
+
// confirm the captured params match it. Without this, derived/relation
|
|
348
|
+
// segments the query can't filter on (year/month, primary_tag) would
|
|
349
|
+
// resolve any slug, 200-ing a URL that has no page.
|
|
307
350
|
_matchesCanonicalUrl(config, params, resource) {
|
|
308
351
|
const canonicalPath = this.urlUtils.replacePermalink(config.permalink, resource);
|
|
309
352
|
const canonicalParams = matchPermalink(config.permalink, canonicalPath);
|
|
@@ -314,6 +357,50 @@ class LazyUrlService {
|
|
|
314
357
|
const canonical = canonicalParams;
|
|
315
358
|
return Object.keys(captured).every(key => canonical[key] === captured[key]);
|
|
316
359
|
}
|
|
360
|
+
// Reports which columns were missing alongside what the caller handed over
|
|
361
|
+
// — the resource's shape, and what the active routing config needs — so
|
|
362
|
+
// the fetch that under-fetched can be found from the log line alone.
|
|
363
|
+
//
|
|
364
|
+
// Reported on the first occurrence of each distinct cause, then at each
|
|
365
|
+
// order of magnitude. A caller that under-fetches does so for every row it
|
|
366
|
+
// serializes, so a browse over a large collection would otherwise emit one
|
|
367
|
+
// error per row — but reporting only once would hide that the problem is
|
|
368
|
+
// still happening, and how widely. `occurrences` carries that instead.
|
|
369
|
+
// Cleared on reset(), since a new routing config can make a previously
|
|
370
|
+
// thin resource fine (or newly broken).
|
|
371
|
+
_degradeThinResource(resource, thin, options) {
|
|
372
|
+
const producedBy = options.serializerContext;
|
|
373
|
+
// The producer is part of the key: two endpoints under-fetching the
|
|
374
|
+
// same way are two bugs, and the endpoint set is small enough that
|
|
375
|
+
// this cannot flood.
|
|
376
|
+
const key = [
|
|
377
|
+
thin.resourceType,
|
|
378
|
+
thin.routerIdentifier ?? '',
|
|
379
|
+
thin.missing.join(','),
|
|
380
|
+
producedBy ? `${producedBy.apiType}:${producedBy.docName}:${producedBy.method}` : ''
|
|
381
|
+
].join('|');
|
|
382
|
+
const occurrences = (this.reportedThinResources.get(key) ?? 0) + 1;
|
|
383
|
+
this.reportedThinResources.set(key, occurrences);
|
|
384
|
+
if (!isPowerOfTen(occurrences)) {
|
|
385
|
+
return this.notFoundUrl(options);
|
|
386
|
+
}
|
|
387
|
+
logging.error(new errors.InternalServerError({
|
|
388
|
+
message: 'URL service could not build a URL, degraded to /404/',
|
|
389
|
+
code: 'LAZY_URL_RESOLUTION_ERROR',
|
|
390
|
+
errorDetails: {
|
|
391
|
+
method: 'getUrlForResource',
|
|
392
|
+
type: resource.type,
|
|
393
|
+
id: resource.id,
|
|
394
|
+
status: resource.status,
|
|
395
|
+
resourceKeys: Object.keys(resource),
|
|
396
|
+
requiredRelations: this.getRequiredRelations(),
|
|
397
|
+
occurrences,
|
|
398
|
+
...(producedBy ? { serializer: producedBy } : {}),
|
|
399
|
+
...thin
|
|
400
|
+
}
|
|
401
|
+
}));
|
|
402
|
+
return this.notFoundUrl(options);
|
|
403
|
+
}
|
|
317
404
|
_hasRouterForType(routerType) {
|
|
318
405
|
return this.routerConfigs.some(config => config.resourceType === routerType);
|
|
319
406
|
}
|
|
@@ -326,32 +413,23 @@ class LazyUrlService {
|
|
|
326
413
|
}
|
|
327
414
|
// A resource that reaches URL generation must carry the columns its base
|
|
328
415
|
// filter reads (status for posts/pages, visibility for tags) — production
|
|
329
|
-
// callers always do
|
|
330
|
-
// them the filter can't be evaluated
|
|
331
|
-
//
|
|
332
|
-
|
|
416
|
+
// callers always do, via full models or the serializers' force-load.
|
|
417
|
+
// Without them the filter can't be evaluated at all, so we cannot tell a
|
|
418
|
+
// genuine miss from a caller that under-fetched.
|
|
419
|
+
_missingBaseFields(routerType, resource) {
|
|
333
420
|
const base = this.baseFilters.get(routerType);
|
|
334
421
|
if (!base) {
|
|
335
|
-
return;
|
|
422
|
+
return null;
|
|
336
423
|
}
|
|
337
424
|
const r = resource;
|
|
338
425
|
const missing = base.fields.filter(field => r[field] === undefined);
|
|
339
426
|
if (missing.length === 0) {
|
|
340
|
-
return;
|
|
427
|
+
return null;
|
|
341
428
|
}
|
|
342
|
-
|
|
343
|
-
message: 'Thin resource passed to LazyUrlService.getUrlForResource',
|
|
344
|
-
code: 'LAZY_URL_THIN_RESOURCE',
|
|
345
|
-
errorDetails: {
|
|
346
|
-
resourceType: routerType,
|
|
347
|
-
resourceId: resource.id,
|
|
348
|
-
baseFilter: base.filter,
|
|
349
|
-
missing
|
|
350
|
-
}
|
|
351
|
-
});
|
|
429
|
+
return { resourceType: routerType, baseFilter: base.filter, missing };
|
|
352
430
|
}
|
|
353
431
|
// Normalizes the plural router type to the singular DB value for filter
|
|
354
|
-
// evaluation only, so page
|
|
432
|
+
// evaluation only, so `page:` filters in routes.yaml match.
|
|
355
433
|
_recordForFilter(resource) {
|
|
356
434
|
const record = resource;
|
|
357
435
|
const dbType = ROUTER_TYPE_TO_DB_TYPE[record.type];
|
|
@@ -360,8 +438,7 @@ class LazyUrlService {
|
|
|
360
438
|
_excludedFilterFieldsFor(routerType) {
|
|
361
439
|
return this.excludedFilterFields.get(routerType) ?? EMPTY_FIELD_SET;
|
|
362
440
|
}
|
|
363
|
-
// Strips the columns
|
|
364
|
-
// filter is evaluated against the same reduced shape eager uses — an
|
|
441
|
+
// Strips the columns a router/collection filter must not see, so an
|
|
365
442
|
// excluded column reads as absent (→ null in NQL) rather than its real
|
|
366
443
|
// value. Only affects filters that reference an excluded column; every
|
|
367
444
|
// other filter sees an identical record.
|
|
@@ -387,8 +464,16 @@ class LazyUrlService {
|
|
|
387
464
|
}
|
|
388
465
|
return path;
|
|
389
466
|
}
|
|
390
|
-
|
|
391
|
-
|
|
467
|
+
/**
|
|
468
|
+
* The /404/ a resource gets when nothing routes it, formatted for the given
|
|
469
|
+
* options. Deliberately omits the argument `_formatPath` passes.
|
|
470
|
+
*
|
|
471
|
+
* The subdirectory is not lost by that omission: `createUrl` takes it from
|
|
472
|
+
* its own base whenever the url is relative, so a subdirectory install gets
|
|
473
|
+
* `/blog/404/` from here either way. The third argument `_formatPath`
|
|
474
|
+
* passes is `trailingSlash`, and `/404/` already ends in one.
|
|
475
|
+
*/
|
|
476
|
+
notFoundUrl(options = {}) {
|
|
392
477
|
if (options.absolute) {
|
|
393
478
|
return this.urlUtils.createUrl('/404/', options.absolute);
|
|
394
479
|
}
|
|
@@ -398,13 +483,13 @@ class LazyUrlService {
|
|
|
398
483
|
return '/404/';
|
|
399
484
|
}
|
|
400
485
|
// A filtered router that references a relation the resource doesn't carry
|
|
401
|
-
// can't be evaluated
|
|
402
|
-
//
|
|
403
|
-
// a thin one is
|
|
404
|
-
//
|
|
405
|
-
|
|
486
|
+
// can't be evaluated, so the resource would 404 for a reason that has
|
|
487
|
+
// nothing to do with the routing config. Callers must hand over
|
|
488
|
+
// fully-inflated resources; a thin one is reported rather than silently
|
|
489
|
+
// 404'd (see getUrlForResource).
|
|
490
|
+
_missingRouterFields(config, resource, routerType) {
|
|
406
491
|
if (!config.filter) {
|
|
407
|
-
return;
|
|
492
|
+
return null;
|
|
408
493
|
}
|
|
409
494
|
const r = resource;
|
|
410
495
|
const missing = [];
|
|
@@ -420,8 +505,8 @@ class LazyUrlService {
|
|
|
420
505
|
if (/\bprimary_author\b/.test(config.filter) && r.primary_author === undefined) {
|
|
421
506
|
missing.push('primary_author');
|
|
422
507
|
}
|
|
423
|
-
//
|
|
424
|
-
//
|
|
508
|
+
// Excluded columns are never required — their filters match them as
|
|
509
|
+
// absent, so a resource lacking one is not thin here.
|
|
425
510
|
const excluded = this._excludedFilterFieldsFor(routerType);
|
|
426
511
|
for (const field of filterScalarFields(config.filter)) {
|
|
427
512
|
if (!excluded.has(field) && r[field] === undefined) {
|
|
@@ -429,19 +514,14 @@ class LazyUrlService {
|
|
|
429
514
|
}
|
|
430
515
|
}
|
|
431
516
|
if (missing.length === 0) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
routerIdentifier: config.identifier,
|
|
441
|
-
filter: config.filter,
|
|
442
|
-
missing
|
|
443
|
-
}
|
|
444
|
-
});
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
resourceType: routerType,
|
|
521
|
+
routerIdentifier: config.identifier,
|
|
522
|
+
filter: config.filter,
|
|
523
|
+
missing
|
|
524
|
+
};
|
|
445
525
|
}
|
|
446
526
|
}
|
|
447
527
|
exports.LazyUrlService = LazyUrlService;
|