ghost 6.57.1 → 6.58.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/LICENSE +22 -0
- package/bin/validate-adapters.js +1 -5
- package/components/ghost-storage-base-3.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-scheduling-0.2.2.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.2.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.4.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/boot.js +35 -3
- package/core/built/admin/assets/{about-BNojRaxf.js → about-B_BvSyLF.js} +1 -1
- package/core/built/admin/assets/{account-migration-DMS8JsM-.js → account-migration-oJUGxnzT.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DHFbePl6.js → account-migration-BpDeIF3G.js} +8 -8
- package/core/built/admin/assets/activitypub/activitypub.js +4 -4
- package/core/built/admin/assets/activitypub/{avatar-CT4tS2_S.js → avatar-B0SpBPVL.js} +3 -3
- package/core/built/admin/assets/activitypub/{avatar-flipboard-ByYWKipo.js → avatar-flipboard-DKt1LGcE.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-srVnSHDz.js → bluesky-sharing-aPMtrS3L.js} +10 -10
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-D4HEcEwi.js → chunk-KS7C4IRE-CnTrsgmM.js} +26 -15
- package/core/built/admin/assets/activitypub/{content-formatters-C2sos5Ht.js → content-formatters-BORfuA9G.js} +318 -284
- package/core/built/admin/assets/activitypub/{copy-B1UBTOoZ.js → copy-D-YvNi2E.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-MPsb2WZ1.js → dist-Dqggkccr.js} +5 -5
- package/core/built/admin/assets/activitypub/{domain-DzcM9Dzo.js → domain-CPdiB27_.js} +10 -10
- package/core/built/admin/assets/activitypub/{edit-profile-CsQ_4xAk.js → edit-profile-BY2PawIe.js} +7 -7
- package/core/built/admin/assets/activitypub/{explore-CJe0DZBK.js → explore-Sp9bgAwq.js} +11 -11
- package/core/built/admin/assets/activitypub/{feed-CKV3qWON.js → feed-BjtF4ohL.js} +10 -10
- package/core/built/admin/assets/activitypub/{field-DckHfDJf.js → field-BDOYVTg6.js} +4 -4
- package/core/built/admin/assets/activitypub/{hash-Dlp8RM55.js → hash-DqXYHgzG.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-CsPy4R0M.js → inbox-CZ7IOAMn.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DAsjzUUe.js → inbox-z1o2j1Tb.js} +11 -11
- package/core/built/admin/assets/activitypub/{label-DWyo9E5T.js → label-1jhN-qhD.js} +4 -4
- package/core/built/admin/assets/activitypub/layout-BRMezuty.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-B0Inc03M.js → moderation-CZudJi8N.js} +9 -9
- package/core/built/admin/assets/activitypub/{note-CSE54lER.js → note-nr_m_5yb.js} +8 -8
- package/core/built/admin/assets/activitypub/{notifications-RSwbKqdh.js → notifications-BdcyivbT.js} +10 -10
- package/core/built/admin/assets/activitypub/{onboarding-BFDkzeR2.js → onboarding-B4kwav9u.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-D6rcBdPe.js → onboarding-DZNElJYP.js} +3 -3
- package/core/built/admin/assets/activitypub/{preferences-CE231n2_.js → preferences-CNqy1x9C.js} +13 -13
- package/core/built/admin/assets/activitypub/{profile-aaQ1j1Eo.js → profile-DlY4rhbO.js} +15 -15
- package/core/built/admin/assets/activitypub/{reply-Cjb0Vs3m.js → reply-DASIN0Ec.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-Djb0mc9k.js → routes-CEHdEZzS.js} +31 -31
- package/core/built/admin/assets/activitypub/{separator-D8pROI5n.js → separator-B3k35sUV.js} +4 -4
- package/core/built/admin/assets/activitypub/{settings-DMc32IQl.js → settings-CrSV1cux.js} +6 -6
- package/core/built/admin/assets/activitypub/{site-DVIcsohX.js → site-CtBUCPBz.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-1-9Yey9usF.js → step-1-D9jGWyAy.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-2-DdYmKRXV.js → step-2-Jz_enuo9.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-hdSkcjZU.js → step-3-KpUqpGWi.js} +12 -12
- package/core/built/admin/assets/activitypub/{tabs-CUqseYzQ.js → tabs-DvlsxTCI.js} +7 -7
- package/core/built/admin/assets/activitypub/{topic-filter-Coni6OXM.js → topic-filter-CIN1E2jg.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-MeHTPMQv.js → use-activity-pub-queries-CATFd6YV.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-B046AM38.js → use-navigate-with-base-path-iCkcbMGk.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-1AO-Xob6.js → use-reply-chain-data-DJ0vhA8V.js} +6 -6
- package/core/built/admin/assets/activitypub/{users-BLeiOTHc.js → users-sgJ6GNZX.js} +12 -11
- package/core/built/admin/assets/activitypub/{x-BRfRYd16.js → x-Cy2jUiYM.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BW23PDYs.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-M0ls8BKO.js +1 -0
- package/core/built/admin/assets/{add-recommendation-modal-D-3LOrdd.js → add-recommendation-modal-B0NeBMzl.js} +1 -1
- package/core/built/admin/assets/{analytics-DTIMRhWw.js → analytics-BC2oR8ay.js} +1 -1
- package/core/built/admin/assets/announcement-bar-modal-pXBqjQE_.js +1 -0
- package/core/built/admin/assets/{api-keys-_iCT4dxr.js → api-keys-BgKr003d.js} +1 -1
- package/core/built/admin/assets/{api-keys-Dx4NeNpK.js → api-keys-Df6uv1dT.js} +1 -1
- package/core/built/admin/assets/{audience-DkknNqhZ.js → audience-BY8od8If.js} +1 -1
- package/core/built/admin/assets/{automated-emails-PayPCT8L.js → automated-emails-28c2Lbb3.js} +1 -1
- package/core/built/admin/assets/{automations-DvSU7QhW.js → automations-DCs6Wady.js} +1 -1
- package/core/built/admin/assets/{automations-162q5HjL.js → automations-Dzp_QnON.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-yZ9A_jL8.js → bluesky-sharing-D0P42Cws.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-CjOH7LCU.js → breadcrumb-MTj_cKLb.js} +1 -1
- package/core/built/admin/assets/card-CiiE7QfM.js +1 -0
- package/core/built/admin/assets/{chart-helpers-DGxvWnBT.js → chart-helpers-DcO3k_vA.js} +1 -1
- package/core/built/admin/assets/{checkbox-BSUcnNq_.js → checkbox-LmClhqcS.js} +1 -1
- package/core/built/admin/assets/{chunk.424.4a27628efdade15c2dab.js → chunk.408.1a258af360eb8d905508.js} +40 -37
- package/core/built/admin/assets/{chunk.571.40f093ed6d883993714e.js → chunk.492.a9da46ca956b10116774.js} +1342 -1342
- package/core/built/admin/assets/{chunk.571.40f093ed6d883993714e.js.LICENSE.txt → chunk.492.a9da46ca956b10116774.js.LICENSE.txt} +1 -1
- package/core/built/admin/assets/chunk.524.4c693c3a00ac91df34fa.js +25 -0
- package/core/built/admin/assets/chunk.582.20b600e329e58d12d7c2.js +27 -0
- package/core/built/admin/assets/code-editor-view-DqTOUnbM.js +1 -0
- package/core/built/admin/assets/code-editor-view-bA0lfxUL.js +1 -0
- package/core/built/admin/assets/{debounce-Cx5B_9OI.js → color-picker-CBlJMv3y.js} +2 -2
- package/core/built/admin/assets/color-picker-field-Cv9GOJQZ.js +1 -0
- package/core/built/admin/assets/{combobox-5rdgDrDV.js → combobox-BbPJzjCp.js} +1 -1
- package/core/built/admin/assets/command-BrhXIlDr.js +1 -0
- package/core/built/admin/assets/comments-Cu4rD1aJ.js +1 -0
- package/core/built/admin/assets/confirmation-provider-BEdrnHns.js +1 -0
- package/core/built/admin/assets/{content-api-modal-BtW4Z1-1.js → content-api-modal-BOFWRUkC.js} +1 -1
- package/core/built/admin/assets/{copy-field-CEp_XieW.js → copy-field-8biQvNMD.js} +1 -1
- package/core/built/admin/assets/custom-field-type-option-CNhAgHbJ.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-CrGOI8IB.js +1 -0
- package/core/built/admin/assets/{date-range-select-B3Ij1Rqg.js → date-range-select-Bsh4gIek.js} +1 -1
- package/core/built/admin/assets/debounce-pscB46sW.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-DWZguwWX.js +23 -0
- package/core/built/admin/assets/{dirty-confirm-dialog-YE9L21gY.js → dirty-confirm-dialog-DmdMpgyN.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-uIzbWL7K.js → disabled-sources-indicator-BLnmyWgt.js} +1 -1
- package/core/built/admin/assets/dist-4m6pHdc0.js +23 -0
- package/core/built/admin/assets/{dist-DCk0UkGx.js → dist-B6bJuxL3.js} +1 -1
- package/core/built/admin/assets/{dist-CbHbGtQ-.js → dist-BtaTA7Ii.js} +1 -1
- package/core/built/admin/assets/{dist-Q1p9iXwH.js → dist-CZgIBXQN.js} +1 -1
- package/core/built/admin/assets/dist-CiHx7vA5.js +6 -0
- package/core/built/admin/assets/{dist-ARN6XTcR.js → dist-ECAUrdGc.js} +1 -1
- package/core/built/admin/assets/dist-Z97pCNqS.js +11 -0
- package/core/built/admin/assets/{dist-t6AXpf_4.js → dist-zwwGt8Yf.js} +1 -1
- package/core/built/admin/assets/{domain-DzrOAPY8.js → domain-Dp9TRrFp.js} +1 -1
- package/core/built/admin/assets/{dropzone-CzhP33wa.js → dropzone-B21Entqz.js} +1 -1
- package/core/built/admin/assets/{edit-profile-oufs4R4n.js → edit-profile-DG6lpMsr.js} +1 -1
- package/core/built/admin/assets/editor-CPQOrhrn.js +8 -0
- package/core/built/admin/assets/{editor-DtxX27Jz.css → editor-DLioOiRN.css} +1 -1
- package/core/built/admin/assets/{embed-signup-form-modal-Ch3s6lqA.js → embed-signup-form-modal-CgWniDha.js} +2 -2
- package/core/built/admin/assets/esm-CXo5SEPy.js +2 -0
- package/core/built/admin/assets/{explore-CA-R01Br.js → explore-C3rzONJv.js} +1 -1
- package/core/built/admin/assets/{feed-ClC8_Gld.js → feed-DpUwS1cU.js} +1 -1
- package/core/built/admin/assets/{field-B3OPenbP.js → field-BQqLTgHM.js} +1 -1
- package/core/built/admin/assets/{filter-sources-CiPLnG9A.js → filter-sources-FgqoUIL7.js} +1 -1
- package/core/built/admin/assets/{filters-yFvybRMe.js → filters-D0uIMD8n.js} +1 -1
- package/core/built/admin/assets/filters-DZJT29GW.js +1 -0
- package/core/built/admin/assets/{first-promoter-modal-BC0UpYCD.js → first-promoter-modal-DJhQQheG.js} +1 -1
- package/core/built/admin/assets/framework-provider-CFLwK5uS.js +4 -0
- package/core/built/admin/assets/{ghost-d38c30b866e59f2e06a357b5eb844fe5.css → ghost-68f447a1796b93a1f128b649bac8506d.css} +1 -1
- package/core/built/admin/assets/{ghost-d683fbeb945b13155ea669caa5d8db41.js → ghost-ba408063a5922b458b4a8f216cf23251.js} +124 -342
- package/core/built/admin/assets/{ghost-dark-1bffd5099ba7a749303133efacf8480b.css → ghost-dark-28ae4899c92022857f3584694bcb50ef.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-BKRuqo-k.js → gift-link-modal-sdFLjND9.js} +1 -1
- package/core/built/admin/assets/{gift-links-CVuqLhsZ.js → gift-links-D7BJ9qfe.js} +1 -1
- package/core/built/admin/assets/{global-data-provider-RzW6Bnlf.js → global-data-provider-CixmTX00.js} +1 -1
- package/core/built/admin/assets/{growth-ClNAldfL.js → growth-0YYkBEeS.js} +1 -1
- package/core/built/admin/assets/{growth-Dz8OSG06.js → growth-DpjAPeQT.js} +2 -2
- package/core/built/admin/assets/{history-modal-DIVuQ5Ct.js → history-modal-BWJ4-cJ0.js} +1 -1
- package/core/built/admin/assets/{hooks-D6EE7BIt.js → hooks-CJepWnbB.js} +1 -1
- package/core/built/admin/assets/{html-field-CVZ2sMxw.js → html-field-C-oa5DEl.js} +1 -1
- package/core/built/admin/assets/{iframe-buffering-e8vH4-Qn.js → iframe-buffering-Ba9sDpi2.js} +1 -1
- package/core/built/admin/assets/{images-Ok8FFJ3A.js → images-3cQgwfTD.js} +1 -1
- package/core/built/admin/assets/{inbox-CMUVI6qb.js → inbox-Dz_JblyJ.js} +6 -6
- package/core/built/admin/assets/index-BOJzlYiz.js +67 -0
- package/core/built/admin/assets/index-Bid6LAyc.css +1 -0
- package/core/built/admin/assets/{input-group-2XU-iuh_.js → input-group-DpogNUIt.js} +1 -1
- package/core/built/admin/assets/{integrations-BT6_iJvu.js → integrations-B9yYgci5.js} +1 -1
- package/core/built/admin/assets/{invite-user-modal-DrZ7xxrZ.js → invite-user-modal-DCiQSiuD.js} +1 -1
- package/core/built/admin/assets/{koenig-editor-base-BPj4S760.js → koenig-editor-base-BApy8Q7C.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +8328 -8294
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +84 -84
- package/core/built/admin/assets/{koenig-lexical-Cdjv-m3_.js → koenig-lexical-2KuqRwZI.js} +33 -33
- package/core/built/admin/assets/{kpi-card-J8Mj1sDX.js → kpi-card-Ck3tT2Wg.js} +1 -1
- package/core/built/admin/assets/{kpi-card-awos0BMH.js → kpi-card-DDIxptaS.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-SEhvH-8E.js → kpi-helpers-ssbuy-hD.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-R6BjsKhd.js → kpi-tabs-BqoIM6bI.js} +1 -1
- package/core/built/admin/assets/{label-BMC3dzLI.js → label-DodDsMzn.js} +1 -1
- package/core/built/admin/assets/label-picker-Cvvbmzb5.js +1 -0
- package/core/built/admin/assets/{labels-DAPjKPy2.js → labels-DqvSR6JF.js} +1 -1
- package/core/built/admin/assets/layout-DkkGyev2.js +1 -0
- package/core/built/admin/assets/limit-service-BhiEvqcP.js +20 -0
- package/core/built/admin/assets/{link-helpers-Bzkk68GD.js → link-helpers-kpbU8TWh.js} +1 -1
- package/core/built/admin/assets/luxon-D1RVjjpq.js +1 -0
- package/core/built/admin/assets/member-custom-fields-CvGcpaiW.js +1 -0
- package/core/built/admin/assets/member-detail-CMCMZ47_.js +1 -0
- package/core/built/admin/assets/members-sRl6fNfq.js +11 -0
- package/core/built/admin/assets/{moderation-mPjYHgZW.js → moderation-KhKwGqI1.js} +1 -1
- package/core/built/admin/assets/{multi-select-combobox-CsoyHAiX.js → multi-select-combobox-nhCff0_K.js} +1 -1
- package/core/built/admin/assets/{navigation-modal-D_c0badj.js → navigation-modal-MDJExWDJ.js} +1 -1
- package/core/built/admin/assets/newsletter-BIBHumfV.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-CfjBMH3v.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-B-OLfrvr.js → newsletter-emails-ClDL-7dA.js} +1 -1
- package/core/built/admin/assets/newsletters-CBe-IPzK.js +1 -0
- package/core/built/admin/assets/{newsletters-Du0lzskS.js → newsletters-ILsexy-5.js} +1 -1
- package/core/built/admin/assets/{note-CSVsUKBm.js → note-HnNUOt4d.js} +1 -1
- package/core/built/admin/assets/{notifications-DV9FRtDw.js → notifications-Cj5e-258.js} +1 -1
- package/core/built/admin/assets/nql-lang-CaWrLesS.js +19 -0
- package/core/built/admin/assets/{offers-Um6ab71O.js → offers-DMxebaoK.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BHwHtixC.js +1 -0
- package/core/built/admin/assets/{onboarding-route--jL5KLPD.js → onboarding-route-D_4jp7Nq.js} +1 -1
- package/core/built/admin/assets/overview-CezDdg-F.js +1 -0
- package/core/built/admin/assets/overview-D5mM51Lj.js +1 -0
- package/core/built/admin/assets/{page-header-DAvDYKO9.js → page-header-VLPJfj_d.js} +1 -1
- package/core/built/admin/assets/{pages-BVMnpI2O.js → pages-Gchxp5c7.js} +1 -1
- package/core/built/admin/assets/{pintura-modal-jiUCxX0Q.js → pintura-modal-BXXJ8Iaa.js} +1 -1
- package/core/built/admin/assets/{portal-frame-D4D4nEO6.js → portal-frame-BLCvF8f-.js} +1 -1
- package/core/built/admin/assets/portal-modal-DN80tAcy.js +1 -0
- package/core/built/admin/assets/{post-analytics-BhAtiFJp.js → post-analytics-Gv81eN_g.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-_ydSY8qO.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-DGg0FUqs.js → post-analytics-provider-O2YjZMhN.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DtFAC132.js → post-share-modal-CzYak_a5.js} +1 -1
- package/core/built/admin/assets/{posts-CxG7v0-M.js → posts-DgcUi2rc.js} +1 -1
- package/core/built/admin/assets/preferences-gneLb6LV.js +5 -0
- package/core/built/admin/assets/{preview-modal-DctYx20m.js → preview-modal-Bkq-tgVm.js} +1 -1
- package/core/built/admin/assets/profile-CzXmCNTC.js +1 -0
- package/core/built/admin/assets/{radio-group-CI5_Kf_d.js → radio-group-Dsq5ZEbV.js} +1 -1
- package/core/built/admin/assets/{referrers-B9QDAfYm.js → referrers-C3a2K8qB.js} +1 -1
- package/core/built/admin/assets/{router-provider-DSQfprxH.js → router-provider-CkB_ArLF.js} +1 -1
- package/core/built/admin/assets/{select-CkXqNJDJ.js → select-xuZAuDyS.js} +1 -1
- package/core/built/admin/assets/{settings-ZCj-W56o.js → settings-Ct8pabhr.js} +1 -1
- package/core/built/admin/assets/settings-KSWWQanQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-DIq31FwA.js → settings-breadcrumbs-DbPGu-Tu.js} +1 -1
- package/core/built/admin/assets/{share-modal-BNj14g79.js → share-modal-CdiUx9WM.js} +1 -1
- package/core/built/admin/assets/{slack-modal-C6SpMQ-q.js → slack-modal-BP--Z3jE.js} +1 -1
- package/core/built/admin/assets/{sort-button-B-k54ow2.js → sort-button-CjfKdlsL.js} +1 -1
- package/core/built/admin/assets/{source-icon-Cej3sZM6.js → source-icon-DHj1h6Ts.js} +1 -1
- package/core/built/admin/assets/{stats-B2KW8E2y.js → stats-C615kulJ.js} +1 -1
- package/core/built/admin/assets/{stats-view-CVzU2-p5.js → stats-view-BSMRdYiN.js} +1 -1
- package/core/built/admin/assets/{step-1-Dbn1iIMU.js → step-1-pSdGYtIF.js} +1 -1
- package/core/built/admin/assets/{step-2-BahnSuN6.js → step-2-T9Uy4eBD.js} +1 -1
- package/core/built/admin/assets/{step-3-CW04e2ZJ.js → step-3-D4uXe_XG.js} +2 -2
- package/core/built/admin/assets/{stripe-button-BkBZqaGG.js → stripe-button-Bh4p6Wud.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-C2vTvepi.js +1 -0
- package/core/built/admin/assets/{table-BtLcEi1M.js → table-DghJxZMH.js} +1 -1
- package/core/built/admin/assets/{tabs-BaWRfg8j.js → tabs-l9c7FjzI.js} +1 -1
- package/core/built/admin/assets/tag-detail-DBw_M2-p.js +2 -0
- package/core/built/admin/assets/{tags-BbygUvNB.js → tags-DoWsAG6l.js} +1 -1
- package/core/built/admin/assets/{tags-BwCgmr5v.js → tags-EmEvJN75.js} +1 -1
- package/core/built/admin/assets/{testimonials-modal--bdzppyh.js → testimonials-modal-BZKePE2T.js} +1 -1
- package/core/built/admin/assets/tier-detail-modal-D65pVWsN.js +1 -0
- package/core/built/admin/assets/{tiers-Da6kxHQP.js → tiers-8iTFjJWg.js} +1 -1
- package/core/built/admin/assets/{toggle-group-CVl-Xigr.js → toggle-group-uJnl42w3.js} +1 -1
- package/core/built/admin/assets/{topic-filter-BoRHRbL1.js → topic-filter-BM7hz0kW.js} +1 -1
- package/core/built/admin/assets/transistor-modal-VZOGN3od.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-CixoRAO2.js → unsplash-modal-CKGfACxF.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-BiWvcf1u.js → use-analytics-data-IxJcSjc9.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-CGp3CGT5.js +1 -0
- package/core/built/admin/assets/use-feature-flag-CSq2Lwzt.js +1 -0
- package/core/built/admin/assets/use-filter-params-0hcJJuEJ.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-iMpDZBxD.js → use-filterable-api-CQBtVG-l.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-Dom8xVzo.js → use-growth-stats-1NW-8eEZ.js} +1 -1
- package/core/built/admin/assets/use-limiter-ffwpsZPp.js +2 -0
- package/core/built/admin/assets/{use-pintura-config-Bj4QmiVk.js → use-pintura-config-BjFChXV1.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-DvrslmPE.js → use-pintura-editor-Cb41oJyL.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-pxIHTXFs.js → use-post-referrers-Dp1BnTAK.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-CmFe0N6F.js → use-reply-chain-data-C0ZUTkfk.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-Cmp_sy-d.js → use-setting-group-Bx8M6FL2.js} +1 -1
- package/core/built/admin/assets/{use-settings-navigation-BmROPrls.js → use-settings-navigation-BOZcat_V.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-D5rL3YwO.js → use-simple-pagination-DsEGSJhe.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-DP2dMaq7.js → use-tinybird-query-Ci_DiPkX.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-DrK_skPQ.js → use-tinybird-token-CsUwc722.js} +1 -1
- package/core/built/admin/assets/use-upgrade-route-B3_DUiuT.js +1 -0
- package/core/built/admin/assets/{use-url-input-ChrUZBFf.js → use-url-input-BEZJpVgN.js} +2 -2
- package/core/built/admin/assets/user-detail-modal-595z1N5r.js +1 -0
- package/core/built/admin/assets/{users-B6hIds6n.js → users-DmGMPfw7.js} +1 -1
- package/core/built/admin/assets/virtual-list-D5ugXyZY.js +1 -0
- package/core/built/admin/assets/{web-D71mjB2r.js → web-CAta3lRE.js} +1 -1
- package/core/built/admin/assets/web-Ds9_wWJK.js +2 -0
- package/core/built/admin/assets/zapier-modal-CcfDZY53.js +1 -0
- package/core/built/admin/index.html +12 -12
- package/core/frontend/apps/private-blogging/lib/middleware.js +5 -58
- package/core/frontend/helpers/ghost_head.js +52 -0
- package/core/frontend/meta/asset-url.js +20 -9
- package/core/frontend/public/cards.manifest.json +1 -0
- package/core/frontend/public/ghost-stats.min.js +1 -1
- package/core/frontend/services/assets-minification/card-assets.js +121 -46
- package/core/frontend/services/assets-minification/index.js +13 -4
- package/core/frontend/services/data/fetch-data.js +7 -14
- package/core/frontend/services/llms/service.js +27 -6
- package/core/frontend/services/proxy.js +1 -0
- package/core/frontend/services/routing/api-adapter.js +11 -5
- package/core/frontend/services/routing/collection-router.js +1 -2
- package/core/frontend/services/routing/controllers/entry/markdown.js +63 -5
- package/core/frontend/services/routing/controllers/entry.js +1 -1
- package/core/frontend/services/routing/controllers/previews.js +0 -3
- package/core/frontend/services/routing/events.js +2 -1
- package/core/frontend/services/routing/parent-router.js +0 -22
- package/core/frontend/services/routing/router-manager.js +2 -2
- package/core/frontend/services/routing/static-pages-router.js +0 -1
- package/core/frontend/services/sitemap/handler.js +3 -4
- package/core/frontend/services/sitemap/site-map-manager.js +1 -2
- package/core/frontend/services/theme-engine/i18n/i18n.js +0 -33
- package/core/frontend/web/middleware/llms-discovery.js +2 -2
- package/core/frontend/web/routers/serve-public-file.js +33 -3
- package/core/frontend/web/site.js +5 -4
- package/core/server/adapters/jobs/InMemoryJobsBackend.js +113 -0
- package/core/server/adapters/redirects/S3RedirectsStore.js +33 -13
- package/core/server/adapters/route-settings/S3RouteSettingsStore.js +33 -13
- package/core/server/adapters/scheduling/scheduling-default.js +6 -7
- package/core/server/adapters/storage/LocalStorageBase.js +1 -3
- package/core/server/api/endpoints/index.js +0 -4
- package/core/server/api/endpoints/member-custom-fields.js +15 -4
- package/core/server/api/endpoints/pages.js +3 -11
- package/core/server/api/endpoints/posts.js +3 -11
- package/core/server/api/endpoints/utils/api-filter-utils.js +15 -1
- package/core/server/api/endpoints/utils/serializers/input/settings.js +4 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/email-batches.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/snippets.js +0 -1
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +1 -0
- package/core/server/data/exporter/table-lists.js +4 -1
- package/core/server/data/migrations/utils/schema.js +5 -3
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +19 -7
- package/core/server/data/migrations/versions/6.58/2026-08-10-08-20-50-add-machine-payments-settings.js +41 -0
- package/core/server/data/migrations/versions/6.58/2026-08-10-08-21-00-add-machine-payment-events-table.js +18 -0
- package/core/server/data/migrations/versions/6.58/2026-08-10-15-24-09-reset-custom-field-definitions-for-underscored-keys.js +66 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-10-00-00-swap-custom-field-values-to-key-fk.js +65 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-10-34-25-remove-legacy-explore-integration.js +131 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-16-00-00-add-sort-order-to-members-custom-fields.js +13 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-21-22-48-rename-email-batches-provider-id.js +8 -0
- package/core/server/data/migrations/versions/6.58/2026-08-13-16-07-33-add-automation-runs-automation-id-created-at-index.js +28 -0
- package/core/server/data/schema/README.md +87 -0
- package/core/server/data/schema/commands.js +5 -2
- package/core/server/data/schema/default-settings/default-settings.json +27 -0
- package/core/server/data/schema/fixtures/fixtures.json +0 -21
- package/core/server/data/schema/schema.js +37 -9
- package/core/server/data/seeders/data-generator.js +3 -1
- package/core/server/data/seeders/importers/automation-action-edges-importer.js +38 -0
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +139 -0
- package/core/server/data/seeders/importers/automation-actions-importer.js +84 -0
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +134 -0
- package/core/server/data/seeders/importers/automation-runs-importer.js +97 -0
- package/core/server/data/seeders/importers/automations-importer.js +78 -0
- package/core/server/data/seeders/importers/benefits-importer.js +1 -1
- package/core/server/data/seeders/importers/comment-reports-importer.js +5 -5
- package/core/server/data/seeders/importers/comments-importer.js +6 -6
- package/core/server/data/seeders/importers/email-batches-importer.js +9 -8
- package/core/server/data/seeders/importers/email-recipient-failures-importer.js +1 -1
- package/core/server/data/seeders/importers/email-recipients-importer.js +12 -12
- package/core/server/data/seeders/importers/emails-importer.js +7 -7
- package/core/server/data/seeders/importers/index.js +6 -0
- package/core/server/data/seeders/importers/labels-importer.js +4 -4
- package/core/server/data/seeders/importers/members-click-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-created-events-importer.js +5 -5
- package/core/server/data/seeders/importers/members-feedback-importer.js +7 -7
- package/core/server/data/seeders/importers/members-importer.js +5 -5
- package/core/server/data/seeders/importers/members-labels-importer.js +1 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-products-importer.js +3 -3
- package/core/server/data/seeders/importers/members-status-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +3 -3
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +8 -8
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +3 -3
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +4 -4
- package/core/server/data/seeders/importers/newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +6 -6
- package/core/server/data/seeders/importers/offers-importer.js +4 -4
- package/core/server/data/seeders/importers/posts-authors-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-importer.js +5 -5
- package/core/server/data/seeders/importers/posts-products-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-tags-importer.js +1 -1
- package/core/server/data/seeders/importers/products-benefits-importer.js +1 -1
- package/core/server/data/seeders/importers/products-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendation-click-events-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendation-subscribe-events-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendations-importer.js +1 -1
- package/core/server/data/seeders/importers/redirects-importer.js +1 -1
- package/core/server/data/seeders/importers/roles-users-importer.js +1 -1
- package/core/server/data/seeders/importers/stripe-prices-importer.js +1 -1
- package/core/server/data/seeders/importers/stripe-products-importer.js +1 -1
- package/core/server/data/seeders/importers/table-importer.js +92 -91
- package/core/server/data/seeders/importers/tags-importer.js +3 -3
- package/core/server/data/seeders/importers/users-importer.js +3 -3
- package/core/server/data/seeders/importers/web-mentions-importer.js +3 -3
- package/core/server/data/seeders/utils/database-date.js +14 -18
- package/core/server/data/seeders/utils/json-importer.js +1 -1
- package/core/server/data/seeders/utils/random.js +8 -7
- package/core/server/data/seeders/utils/topological-sort.js +5 -3
- package/core/server/data/tinybird/ARCHITECTURE.md +1 -2
- package/core/server/models/base/plugins/crud.js +0 -5
- package/core/server/models/index.js +2 -0
- package/core/server/models/machine-payment-event.js +18 -0
- package/core/server/models/member.js +24 -0
- package/core/server/models/settings.js +32 -3
- package/core/server/models/user.js +1 -1
- package/core/server/services/README.md +30 -0
- package/core/server/services/activitypub/activity-pub-service.js +2 -3
- package/core/server/services/adapter-manager/base-classes.js +3 -1
- package/core/server/services/auth/passwordreset.js +47 -51
- package/core/server/services/automations/database-automations-repository.js +19 -3
- package/core/server/services/automations/poll.js +2 -8
- package/core/server/services/comments/comments-service.js +4 -16
- package/core/server/services/comments/index.js +1 -3
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +68 -119
- package/core/server/services/email-analytics/email-analytics-service.js +73 -229
- package/core/server/services/email-analytics/event-processing-result.js +0 -10
- package/core/server/services/email-analytics/fetch-mailgun-events.js +25 -0
- package/core/server/services/email-analytics/index.js +35 -67
- package/core/server/services/email-analytics/lib/queries.js +88 -51
- package/core/server/services/email-analytics/newsletter-email-analytics-batch-processor.js +4 -1
- package/core/server/services/email-service/batch-sending-service.js +1 -1
- package/core/server/services/email-service/email-event-processor.js +12 -12
- package/core/server/services/email-service/events/email-bounced-event.js +8 -46
- package/core/server/services/email-service/events/email-delivered-event.js +8 -36
- package/core/server/services/email-service/events/email-opened-event.js +8 -36
- package/core/server/services/email-service/events/email-temporary-bounced-event.js +8 -46
- package/core/server/services/email-service/events/email-unsubscribed-event.js +8 -31
- package/core/server/services/email-service/events/spam-complaint-event.js +8 -31
- package/core/server/services/email-service/newsletter-email-event-storage.js +8 -7
- package/core/server/services/email-service/sending-service.js +1 -1
- package/core/server/services/email-suppression-list/mailgun-email-suppression-list.js +2 -2
- package/core/server/services/email-suppression-list/service.js +1 -3
- package/core/server/services/explore-ping/explore-ping-service.js +1 -6
- package/core/server/services/explore-ping/index.js +0 -2
- package/core/server/services/gifts/index.js +109 -3
- package/core/server/services/gifts/jobs/clean-gifts-job.js +1 -1
- package/core/server/services/gifts/jobs/send-gift-reminders-job.js +1 -1
- package/core/server/services/jobs-service/index.js +36 -0
- package/core/server/services/jobs-service/job.js +6 -0
- package/core/server/services/jobs-service/jobs-service.js +91 -0
- package/core/server/services/jobs-service/register-job-handlers.js +6 -0
- package/core/server/services/lib/mailgun-client.js +1 -1
- package/core/server/services/machine-payments/README.md +23 -0
- package/core/server/services/machine-payments/adapters/mpp-adapter.js +195 -0
- package/core/server/services/machine-payments/content-loader.js +89 -0
- package/core/server/services/machine-payments/eligibility.js +6 -0
- package/core/server/services/machine-payments/events/machine-payment-event-repository.js +58 -0
- package/core/server/services/machine-payments/events/machine-payment-event.js +46 -0
- package/core/server/services/machine-payments/index.js +87 -0
- package/core/server/services/machine-payments/pricing.js +54 -0
- package/core/server/services/machine-payments/service.js +275 -0
- package/core/server/services/machine-payments/stripe/deposit-address-store.js +157 -0
- package/core/server/services/machine-payments/stripe/payment-recorder.js +85 -0
- package/core/server/services/machine-payments/stripe/stripe-client-options.js +25 -0
- package/core/server/services/machine-payments/types.js +2 -0
- package/core/server/services/members/import-export/csv/parse.js +9 -1
- package/core/server/services/members/import-export/import/completion-email.js +61 -27
- package/core/server/services/members/import-export/import/email-template.js +28 -15
- package/core/server/services/members/import-export/import/importer.js +99 -51
- package/core/server/services/members/import-export/import/spool.js +1 -1
- package/core/server/services/members/import-export/index.js +24 -1
- package/core/server/services/members/members-api/services/member-bread-service.js +2 -2
- package/core/server/services/members/members-config-provider.js +12 -2
- package/core/server/services/members-custom-fields/actions.js +2 -1
- package/core/server/services/members-custom-fields/definitions-service.js +109 -27
- package/core/server/services/members-custom-fields/filter.js +199 -0
- package/core/server/services/members-custom-fields/key.js +37 -0
- package/core/server/services/members-custom-fields/queries.js +15 -0
- package/core/server/services/members-custom-fields/schema.js +1 -1
- package/core/server/services/members-custom-fields/values-service.js +18 -14
- package/core/server/services/members-events/last-seen-at-updater.js +1 -1
- package/core/server/services/oembed/oembed-service.js +8 -2
- package/core/server/services/permissions/can-this.js +4 -1
- package/core/server/services/posts/posts-service.js +18 -16
- package/core/server/services/public-config/config.js +1 -1
- package/core/server/services/route-settings/dynamic-routing-service.js +2 -0
- package/core/server/services/settings-helpers/settings-helpers.js +0 -1
- package/core/server/services/stats/posts-stats-service.js +0 -2
- package/core/server/services/stripe/stripe-api.js +2 -9
- package/core/server/web/api/endpoints/admin/middleware.js +0 -1
- package/core/server/web/api/endpoints/admin/routes.js +2 -3
- package/core/server/web/comments/routes.js +31 -8
- package/core/shared/config/defaults.json +17 -2
- package/core/shared/config/loader.js +11 -2
- package/core/shared/config/overrides.json +2 -0
- package/core/shared/config/secrets.js +71 -0
- package/core/shared/labs.js +4 -13
- package/core/shared/machine-payments.js +30 -0
- package/core/shared/private-site-access/index.js +68 -0
- package/package.json +48 -43
- package/pnpm-lock.yaml +1412 -1583
- package/pnpm-workspace.yaml +85 -48
- package/bin/validate-adapters.ts +0 -87
- package/components/tryghost-adapter-base-scheduling-0.2.1.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.3.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.3.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.4.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.4.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.5.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-kg-markdown-html-renderer-7.2.3.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.3.tgz +0 -0
- package/content/themes/casper/assets/built/casper.js.map +0 -1
- package/content/themes/casper/assets/built/global.css.map +0 -1
- package/content/themes/casper/assets/built/screen.css.map +0 -1
- package/content/themes/source/assets/built/screen.css.map +0 -1
- package/content/themes/source/assets/built/source.js.map +0 -1
- package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +0 -1
- package/core/built/admin/assets/add-integration-modal-DU9ghGLS.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-DFxuQrYu.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-DGCcezB6.js +0 -1
- package/core/built/admin/assets/card-aUHX0hyR.js +0 -1
- package/core/built/admin/assets/chunk.524.dce1c51e0bc30fb2718e.js +0 -25
- package/core/built/admin/assets/chunk.582.3a430d5c6a744fd475b6.js +0 -27
- package/core/built/admin/assets/code-editor-view-BcyKABrG.js +0 -1
- package/core/built/admin/assets/color-picker-field-8NoSnhNI.js +0 -1
- package/core/built/admin/assets/command-BOYMbZa_.js +0 -1
- package/core/built/admin/assets/comments-BhNM5CYl.js +0 -1
- package/core/built/admin/assets/confirmation-modal-CYD7x3UQ.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-bk3lNEuQ.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-B2xYKS1I.js +0 -23
- package/core/built/admin/assets/dist-Bg8bSvLA.js +0 -6
- package/core/built/admin/assets/dist-BnFdlebk.js +0 -11
- package/core/built/admin/assets/dist-CsvXCJSe.js +0 -23
- package/core/built/admin/assets/editor-BBKrunMK.js +0 -8
- package/core/built/admin/assets/filters-Bz0HsRie.js +0 -1
- package/core/built/admin/assets/framework-provider-CmtnPXIT.js +0 -4
- package/core/built/admin/assets/index-DUXM0NlS.js +0 -67
- package/core/built/admin/assets/index-Ds2iUoF6.css +0 -1
- package/core/built/admin/assets/label-picker-Bnlru5tO.js +0 -1
- package/core/built/admin/assets/layout-yR9rlvEw.js +0 -1
- package/core/built/admin/assets/limit-modal-COmLevuY.js +0 -1
- package/core/built/admin/assets/limit-service-9fwD33lU.js +0 -20
- package/core/built/admin/assets/member-custom-fields-BvFKEpxJ.js +0 -1
- package/core/built/admin/assets/member-detail-CWdPAe3p.js +0 -1
- package/core/built/admin/assets/members-Bnt0ih2R.js +0 -11
- package/core/built/admin/assets/newsletter-DR49NyGS.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-B45pL1pb.js +0 -1
- package/core/built/admin/assets/newsletters-DGs5BaKH.js +0 -1
- package/core/built/admin/assets/nql-lang-B7faUf8D.js +0 -19
- package/core/built/admin/assets/offers-container-modal-B0hFS8nk.js +0 -1
- package/core/built/admin/assets/overview-B-ZPNqMo.js +0 -1
- package/core/built/admin/assets/overview-D96iwoJi.js +0 -1
- package/core/built/admin/assets/portal-modal-BLFdYeDH.js +0 -1
- package/core/built/admin/assets/post-analytics-header-DK27VjJm.js +0 -1
- package/core/built/admin/assets/preferences-CuWLISHa.js +0 -5
- package/core/built/admin/assets/profile-BsKYYtuL.js +0 -1
- package/core/built/admin/assets/settings-B4vM-rgV.js +0 -4
- package/core/built/admin/assets/stripe-connect-modal-CYDDrSJm.js +0 -1
- package/core/built/admin/assets/tag-detail-CmqiC32L.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BZmAsIy4.js +0 -1
- package/core/built/admin/assets/transistor-modal-CBPDqEGy.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B14lZukR.js +0 -2
- package/core/built/admin/assets/use-feature-flag-BmJoc5WN.js +0 -1
- package/core/built/admin/assets/use-filter-params-ChoV72Hw.js +0 -1
- package/core/built/admin/assets/use-limiter-Dwg8ivgQ.js +0 -2
- package/core/built/admin/assets/user-detail-modal-XODpOqNC.js +0 -1
- package/core/built/admin/assets/virtual-list-Dnoafqbk.js +0 -1
- package/core/built/admin/assets/web-z71lA1Y3.js +0 -2
- package/core/built/admin/assets/zapier-modal-Bp507Um7.js +0 -1
- package/core/frontend/meta/og-type.ts +0 -13
- package/core/frontend/services/routing/api-adapter.ts +0 -157
- package/core/frontend/services/routing/config.ts +0 -59
- package/core/frontend/services/routing/controllers/entry/canonical-url.ts +0 -14
- package/core/frontend/services/routing/controllers/entry/gift-links.ts +0 -56
- package/core/frontend/services/routing/controllers/entry/markdown.ts +0 -72
- package/core/frontend/services/routing/controllers/entry.ts +0 -120
- package/core/frontend/services/routing/controllers/rss.ts +0 -66
- package/core/frontend/services/routing/permalink-adapter.ts +0 -10
- package/core/frontend/src/utils/privacy.ts +0 -66
- package/core/server/adapters/redirects/FileStore.ts +0 -113
- package/core/server/adapters/redirects/S3RedirectsStore.ts +0 -185
- package/core/server/adapters/route-settings/FileStore.ts +0 -144
- package/core/server/adapters/route-settings/S3RouteSettingsStore.ts +0 -199
- package/core/server/adapters/route-settings/utils.ts +0 -17
- package/core/server/adapters/scheduling/deps.d.ts +0 -1
- package/core/server/adapters/scheduling/error-capture.ts +0 -91
- package/core/server/adapters/scheduling/scheduling-default.ts +0 -248
- package/core/server/adapters/scheduling/utils.ts +0 -36
- package/core/server/adapters/sso/DefaultSSOAdapter.ts +0 -15
- package/core/server/adapters/storage/LocalFilesStorage.ts +0 -24
- package/core/server/adapters/storage/LocalImagesStorage.ts +0 -24
- package/core/server/adapters/storage/LocalMediaStorage.ts +0 -24
- package/core/server/adapters/storage/LocalStorageBase.ts +0 -342
- package/core/server/adapters/storage/S3Storage.ts +0 -506
- package/core/server/adapters/storage/utils.ts +0 -60
- package/core/server/api/endpoints/automation-action-links.ts +0 -46
- package/core/server/api/endpoints/explore.js +0 -18
- package/core/server/api/endpoints/gift-links.ts +0 -87
- package/core/server/api/endpoints/libraries.d.ts +0 -2
- package/core/server/api/endpoints/member-commenting.ts +0 -92
- package/core/server/api/endpoints/member-custom-fields.ts +0 -106
- package/core/server/api/endpoints/utils/api-filter-utils.ts +0 -22
- package/core/server/api/endpoints/utils/gift-link-access.ts +0 -82
- package/core/server/api/endpoints/utils/public-cache-keys.ts +0 -47
- package/core/server/api/endpoints/utils/serializers/input/member-commenting.ts +0 -29
- package/core/server/api/endpoints/utils/serializers/output/explore.js +0 -11
- package/core/server/api/endpoints/utils/serializers/output/gift-links.ts +0 -21
- package/core/server/api/endpoints/utils/serializers/output/member-commenting.ts +0 -8
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.ts +0 -25
- package/core/server/api/endpoints/utils/serializers/output/members.d.ts +0 -20
- package/core/server/api/endpoints/utils/serializers/output/posts-csv-transform.ts +0 -46
- package/core/server/api/endpoints/utils/serializers/output/stream-csv-response.ts +0 -47
- package/core/server/api/endpoints/utils/validate-email-sender-fields.ts +0 -42
- package/core/server/data/importer/email-template.ts +0 -213
- package/core/server/data/seeders/utils/topological-sort.ts +0 -48
- package/core/server/lib/auto-filling-map.ts +0 -23
- package/core/server/lib/bootstrap-socket.ts +0 -114
- package/core/server/lib/case-keys.ts +0 -30
- package/core/server/lib/common/to-plain.ts +0 -25
- package/core/server/lib/db-date.ts +0 -8
- package/core/server/lib/get-inbox-links.ts +0 -276
- package/core/server/lib/post-revisions.ts +0 -152
- package/core/server/lib/soonest-timer.ts +0 -57
- package/core/server/models/base/plugins/bulk-filters.ts +0 -35
- package/core/server/models/role-utils.ts +0 -56
- package/core/server/services/activitypub/activity-pub-service.ts +0 -216
- package/core/server/services/adapter-manager/adapter-manager.ts +0 -253
- package/core/server/services/adapter-manager/adapter-paths.ts +0 -26
- package/core/server/services/adapter-manager/base-classes.ts +0 -22
- package/core/server/services/adapter-manager/index.ts +0 -16
- package/core/server/services/adapter-manager/types.ts +0 -45
- package/core/server/services/adapter-manager/utils.ts +0 -308
- package/core/server/services/auth/reset-authentication.ts +0 -72
- package/core/server/services/automations/automations-api.ts +0 -396
- package/core/server/services/automations/automations-repository.ts +0 -218
- package/core/server/services/automations/constants.ts +0 -4
- package/core/server/services/automations/database-automations-repository.ts +0 -1513
- package/core/server/services/automations/database-date.ts +0 -26
- package/core/server/services/automations/fake-wait-hours-multiplier.ts +0 -12
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +0 -8
- package/core/server/services/automations/mailgun-message-id.ts +0 -27
- package/core/server/services/automations/poll.ts +0 -325
- package/core/server/services/automations/scheduler-poll-time.ts +0 -38
- package/core/server/services/automations/service.ts +0 -131
- package/core/server/services/automations/stale-lock-cutoff.ts +0 -5
- package/core/server/services/comments/email-templates/new-comment-reply.txt.ts +0 -25
- package/core/server/services/comments/email-templates/new-comment.txt.ts +0 -25
- package/core/server/services/comments/email-templates/report.txt.ts +0 -34
- package/core/server/services/custom-redirects/redirect-config-parser.ts +0 -148
- package/core/server/services/custom-redirects/redirects-service.ts +0 -181
- package/core/server/services/custom-redirects/utils.ts +0 -7
- package/core/server/services/donations/donation-bookshelf-repository.ts +0 -63
- package/core/server/services/donations/donation-payment-event.ts +0 -51
- package/core/server/services/email-address/email-address-parser.d.ts +0 -12
- package/core/server/services/email-address/email-address-service.ts +0 -241
- package/core/server/services/email-analytics/automation-email-analytics-batch-processor.ts +0 -130
- package/core/server/services/email-analytics/batch-event-processor.ts +0 -18
- package/core/server/services/email-analytics/email-analytics-provider-mailgun.js +0 -57
- package/core/server/services/email-analytics/event-processing-result.ts +0 -66
- package/core/server/services/email-analytics/events/start-automation-email-analytics-job-event.ts +0 -14
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.ts +0 -15
- package/core/server/services/email-analytics/index.ts +0 -109
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.ts +0 -126
- package/core/server/services/email-analytics/lib/queries.ts +0 -341
- package/core/server/services/email-service/domain-warming-service.ts +0 -91
- package/core/server/services/explore/explore-service.js +0 -59
- package/core/server/services/explore/index.js +0 -18
- package/core/server/services/explore-ping/explore-ping-service.ts +0 -198
- package/core/server/services/explore-ping/index.ts +0 -42
- package/core/server/services/files/file-type-utils.ts +0 -56
- package/core/server/services/gift-links/actions.ts +0 -51
- package/core/server/services/gift-links/codec.ts +0 -16
- package/core/server/services/gift-links/index.ts +0 -20
- package/core/server/services/gift-links/models.ts +0 -22
- package/core/server/services/gift-links/schema.ts +0 -33
- package/core/server/services/gift-links/serializers.ts +0 -20
- package/core/server/services/gift-links/service.ts +0 -95
- package/core/server/services/gifts/constants.ts +0 -3
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.ts +0 -43
- package/core/server/services/gifts/email-templates/gift-reminder.ts +0 -50
- package/core/server/services/gifts/gift-bookshelf-repository.ts +0 -366
- package/core/server/services/gifts/gift-checkout-offer.ts +0 -127
- package/core/server/services/gifts/gift-codec.ts +0 -62
- package/core/server/services/gifts/gift-controller.ts +0 -51
- package/core/server/services/gifts/gift-email-renderer.ts +0 -97
- package/core/server/services/gifts/gift-email-service.ts +0 -158
- package/core/server/services/gifts/gift-reminder-scheduler.ts +0 -107
- package/core/server/services/gifts/gift-schema.ts +0 -39
- package/core/server/services/gifts/gift-service-wrapper.js +0 -142
- package/core/server/services/gifts/gift-service.ts +0 -1034
- package/core/server/services/gifts/gift.ts +0 -234
- package/core/server/services/identity-tokens/identity-token-service.ts +0 -36
- package/core/server/services/indexnow-ping/constants.ts +0 -32
- package/core/server/services/indexnow-ping/index.ts +0 -35
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +0 -311
- package/core/server/services/internal-keys/index.ts +0 -53
- package/core/server/services/lib/dynamic-redirect-manager.d.ts +0 -20
- package/core/server/services/lib/in-memory-repository.ts +0 -78
- package/core/server/services/lib/member-signup-contexts.ts +0 -22
- package/core/server/services/link-redirection/link-redirect.ts +0 -24
- package/core/server/services/member-welcome-emails/constants.ts +0 -35
- package/core/server/services/members/commenting/index.ts +0 -2
- package/core/server/services/members/commenting/member-commenting-codec.ts +0 -73
- package/core/server/services/members/commenting/member-commenting.ts +0 -69
- package/core/server/services/members/create-paid-member-shim.ts +0 -40
- package/core/server/services/members/import-export/config.ts +0 -34
- package/core/server/services/members/import-export/csv/formula.ts +0 -14
- package/core/server/services/members/import-export/csv/index.ts +0 -3
- package/core/server/services/members/import-export/csv/parse.ts +0 -69
- package/core/server/services/members/import-export/csv/serialize.ts +0 -18
- package/core/server/services/members/import-export/export/exporter.ts +0 -325
- package/core/server/services/members/import-export/import/completion-email.ts +0 -146
- package/core/server/services/members/import-export/import/importer.ts +0 -489
- package/core/server/services/members/import-export/import/reader.ts +0 -33
- package/core/server/services/members/import-export/import/row.ts +0 -56
- package/core/server/services/members/import-export/import/spool.ts +0 -38
- package/core/server/services/members/import-export/index.ts +0 -143
- package/core/server/services/members/index.d.ts +0 -32
- package/core/server/services/members-custom-fields/actions.ts +0 -62
- package/core/server/services/members-custom-fields/codec.ts +0 -13
- package/core/server/services/members-custom-fields/config.ts +0 -38
- package/core/server/services/members-custom-fields/definitions-service.ts +0 -413
- package/core/server/services/members-custom-fields/index.ts +0 -50
- package/core/server/services/members-custom-fields/models.ts +0 -15
- package/core/server/services/members-custom-fields/queries.ts +0 -13
- package/core/server/services/members-custom-fields/schema.ts +0 -71
- package/core/server/services/members-custom-fields/serializers.ts +0 -22
- package/core/server/services/members-custom-fields/storage.ts +0 -122
- package/core/server/services/members-custom-fields/values-service.ts +0 -269
- package/core/server/services/notifications/notification-email.ts +0 -66
- package/core/server/services/notifications/sanitize-email-html.ts +0 -35
- package/core/server/services/post-scheduling/index.ts +0 -13
- package/core/server/services/post-scheduling/post-scheduling.ts +0 -133
- package/core/server/services/recommendations/service/bookshelf-click-event-repository.ts +0 -49
- package/core/server/services/recommendations/service/bookshelf-recommendation-repository.ts +0 -117
- package/core/server/services/recommendations/service/bookshelf-repository.ts +0 -192
- package/core/server/services/recommendations/service/bookshelf-subscribe-event-repository.ts +0 -49
- package/core/server/services/recommendations/service/click-event.ts +0 -32
- package/core/server/services/recommendations/service/in-memory-recommendation-repository.ts +0 -20
- package/core/server/services/recommendations/service/incoming-recommendation-controller.ts +0 -51
- package/core/server/services/recommendations/service/incoming-recommendation-email-renderer.ts +0 -37
- package/core/server/services/recommendations/service/incoming-recommendation-service.ts +0 -159
- package/core/server/services/recommendations/service/index.ts +0 -15
- package/core/server/services/recommendations/service/libraries.d.ts +0 -2
- package/core/server/services/recommendations/service/recommendation-controller.ts +0 -257
- package/core/server/services/recommendations/service/recommendation-metadata-service.ts +0 -132
- package/core/server/services/recommendations/service/recommendation-repository.ts +0 -13
- package/core/server/services/recommendations/service/recommendation-service.ts +0 -285
- package/core/server/services/recommendations/service/recommendation.ts +0 -200
- package/core/server/services/recommendations/service/subscribe-event.ts +0 -32
- package/core/server/services/recommendations/service/unsafe-data.ts +0 -217
- package/core/server/services/recommendations/service/well-known-service.ts +0 -47
- package/core/server/services/remote-flags/index.ts +0 -101
- package/core/server/services/remote-flags/remote-flags-service.ts +0 -236
- package/core/server/services/remote-flags/resolve.ts +0 -108
- package/core/server/services/route-settings/route-settings-parser.ts +0 -455
- package/core/server/services/route-settings/validation-errors.ts +0 -271
- package/core/server/services/route-settings/yaml-parser.d.ts +0 -4
- package/core/server/services/slack-ping/constants.ts +0 -15
- package/core/server/services/slack-ping/index.ts +0 -35
- package/core/server/services/slack-ping/slack-ping-service.ts +0 -295
- package/core/server/services/staff/email-templates/donation.txt.ts +0 -26
- package/core/server/services/staff/email-templates/gift.txt.ts +0 -27
- package/core/server/services/staff/email-templates/new-free-signup.txt.ts +0 -15
- package/core/server/services/staff/email-templates/new-gift-subscription.txt.ts +0 -26
- package/core/server/services/staff/email-templates/new-milestone-received.txt.ts +0 -20
- package/core/server/services/staff/email-templates/new-paid-cancellation.txt.ts +0 -15
- package/core/server/services/staff/email-templates/new-paid-started.txt.ts +0 -15
- package/core/server/services/staff/email-templates/recommendation-received.txt.ts +0 -23
- package/core/server/services/staff/email-templates/types.ts +0 -13
- package/core/server/services/url/lazy-find-resource.ts +0 -104
- package/core/server/services/url/lazy-url-service.ts +0 -647
- package/core/server/services/url/permalink-matcher.ts +0 -119
- package/core/server/services/url/router-filter.ts +0 -80
- package/core/server/services/verification/verification-webhook-service.ts +0 -137
- package/core/shared/config/helpers.ts +0 -133
- package/core/shared/config/index.ts +0 -12
- package/core/shared/config/loader.ts +0 -87
- package/core/shared/config/utils.ts +0 -104
- package/core/shared/errify.ts +0 -31
- package/core/shared/events-ts/index.ts +0 -8
- package/core/shared/events-ts/post-deleted-event.ts +0 -15
- package/core/shared/events-ts/posts-bulk-add-tags-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-destroyed-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-featured-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unfeatured-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unpublished-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unscheduled-event.ts +0 -13
- package/core/shared/labs-flag-overrides.ts +0 -35
- package/core/shared/one-at-a-time.ts +0 -47
- package/core/shared/url-utils.ts +0 -20
|
@@ -1,647 +0,0 @@
|
|
|
1
|
-
const debug = require('@tryghost/debug')('services:url:lazy');
|
|
2
|
-
const errors = require('@tryghost/errors');
|
|
3
|
-
const logging = require('@tryghost/logging');
|
|
4
|
-
const localUtils = require('../../../shared/url-utils').default;
|
|
5
|
-
const {matchPermalink, toLookupParams} = require('./permalink-matcher');
|
|
6
|
-
const {buildFilter, filterMatches, routerTypeOf} = require('./router-filter');
|
|
7
|
-
const {fetchRoutableResources: defaultFetchRoutableResources} = require('./routable-resources');
|
|
8
|
-
const urlConfig = require('./config');
|
|
9
|
-
|
|
10
|
-
import type {CompiledFilter} from './router-filter';
|
|
11
|
-
import type {PermalinkParams} from './permalink-matcher';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Routing-level resource. `type` is one of the plural router keys
|
|
15
|
-
* ('posts', 'pages', 'tags', 'authors'). Concrete records carry additional
|
|
16
|
-
* fields (slug, published_at, primary_tag, ...) read by the permalink
|
|
17
|
-
* templates and router filters.
|
|
18
|
-
*/
|
|
19
|
-
export interface Resource {
|
|
20
|
-
type: string;
|
|
21
|
-
id: string;
|
|
22
|
-
[key: string]: unknown;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface UrlOptions {
|
|
26
|
-
absolute?: boolean;
|
|
27
|
-
withSubdirectory?: boolean;
|
|
28
|
-
// Diagnostic only: the producing api endpoint and the fetch shape its
|
|
29
|
-
// input serializer settled on, threaded from the output serializer so a
|
|
30
|
-
// thin-resource report names what under-fetched. Ignored by URL
|
|
31
|
-
// generation; read only into that report.
|
|
32
|
-
serializerContext?: {
|
|
33
|
-
apiType?: string;
|
|
34
|
-
docName?: string;
|
|
35
|
-
method?: string;
|
|
36
|
-
withRelated?: unknown;
|
|
37
|
-
columns?: unknown;
|
|
38
|
-
forcedUrlRelations?: unknown;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* On-demand enumeration of the routable rows of a type. Injectable so tests
|
|
44
|
-
* can drive `getRoutableResources` without a database.
|
|
45
|
-
*/
|
|
46
|
-
export type FetchRoutableResources = (
|
|
47
|
-
type: string,
|
|
48
|
-
options: {columns?: string[]; requiredFields?: string[]; requiredRelations?: string[]}
|
|
49
|
-
) => Promise<Array<Record<string, unknown>>>;
|
|
50
|
-
|
|
51
|
-
export type ResourceLookupParams = {id: string} | {slug: string};
|
|
52
|
-
|
|
53
|
-
export type FindResource = (
|
|
54
|
-
routerType: string,
|
|
55
|
-
params: ResourceLookupParams
|
|
56
|
-
) => Promise<Record<string, unknown> | null>;
|
|
57
|
-
|
|
58
|
-
interface RouterConfig {
|
|
59
|
-
identifier: string;
|
|
60
|
-
filter: string | null;
|
|
61
|
-
resourceType: string;
|
|
62
|
-
permalink: string;
|
|
63
|
-
compiledFilter: CompiledFilter | null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Per-resource-type gate on which rows get a URL at all — the forward-lookup
|
|
67
|
-
// counterpart of the fetch filters in routable-resources.js. `fields` lists
|
|
68
|
-
// the record columns each filter reads; a resource that reaches URL generation
|
|
69
|
-
// must carry them (see _missingBaseFields).
|
|
70
|
-
//
|
|
71
|
-
// Authors are intentionally absent: users.visibility is schema-pinned to
|
|
72
|
-
// 'public' (isIn: [['public']]), so the visibility:public author filter never
|
|
73
|
-
// excludes anyone — every author is routable, and serialized authors drop
|
|
74
|
-
// visibility anyway (#10438).
|
|
75
|
-
const BASE_FILTERS: Record<string, {filter: string; fields: string[]}> = {
|
|
76
|
-
posts: {filter: 'status:published+type:post', fields: ['status', 'type']},
|
|
77
|
-
pages: {filter: 'status:published+type:page', fields: ['status', 'type']},
|
|
78
|
-
tags: {filter: 'visibility:public', fields: ['visibility']}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
interface BaseFilter {
|
|
82
|
-
filter: string;
|
|
83
|
-
compiledFilter: CompiledFilter;
|
|
84
|
-
fields: string[];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Why a resource could not be routed: the columns it was missing, and which
|
|
88
|
-
// filter needed them. Reported, then degraded to /404/.
|
|
89
|
-
interface ThinResource {
|
|
90
|
-
resourceType: string;
|
|
91
|
-
missing: string[];
|
|
92
|
-
baseFilter?: string;
|
|
93
|
-
routerIdentifier?: string;
|
|
94
|
-
filter?: string | null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function buildBaseFilters(): Map<string, BaseFilter> {
|
|
98
|
-
const baseFilters = new Map<string, BaseFilter>();
|
|
99
|
-
for (const [type, {filter, fields}] of Object.entries(BASE_FILTERS)) {
|
|
100
|
-
const compiledFilter = buildFilter(filter);
|
|
101
|
-
if (compiledFilter) {
|
|
102
|
-
baseFilters.set(type, {filter, compiledFilter, fields});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return baseFilters;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Columns a router filter must not see (the `exclude` lists in
|
|
109
|
-
// services/url/config.js), keyed by resourceType. Ghost previously answered
|
|
110
|
-
// URLs from a precomputed cache that dropped these columns, so a filter
|
|
111
|
-
// referencing one matched it as absent — NQL reads absent as null. This
|
|
112
|
-
// service loads full records and would see the real value, so it strips them
|
|
113
|
-
// before evaluating router filters (and neither requires nor force-loads
|
|
114
|
-
// them), keeping routes.yaml files that rely on that behaviour working. The
|
|
115
|
-
// base filter above is unaffected — it runs against the full record.
|
|
116
|
-
function buildExcludedFilterFields(): Map<string, Set<string>> {
|
|
117
|
-
const excluded = new Map<string, Set<string>>();
|
|
118
|
-
for (const entry of urlConfig) {
|
|
119
|
-
const exclude = entry?.exclude;
|
|
120
|
-
if (Array.isArray(exclude)) {
|
|
121
|
-
excluded.set(entry.type, new Set(exclude));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return excluded;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const EMPTY_FIELD_SET: ReadonlySet<string> = new Set();
|
|
128
|
-
|
|
129
|
-
// 1, 10, 100, ... — exact for integers, unlike a log10 comparison.
|
|
130
|
-
function isPowerOfTen(n: number): boolean {
|
|
131
|
-
let power = 1;
|
|
132
|
-
while (power < n) {
|
|
133
|
-
power *= 10;
|
|
134
|
-
}
|
|
135
|
-
return power === n;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Relation roots are loaded via getRequiredRelations (as withRelated), not as
|
|
139
|
-
// scalar columns; `page`/`type` are the router-type discriminator, always set
|
|
140
|
-
// on the resource. Everything else a router filter references is a scalar
|
|
141
|
-
// own-column the resource must carry to be routed.
|
|
142
|
-
const FILTER_NON_SCALAR_FIELDS = new Set([
|
|
143
|
-
'tag', 'tags', 'author', 'authors', 'primary_tag', 'primary_author', 'page', 'type'
|
|
144
|
-
]);
|
|
145
|
-
|
|
146
|
-
// Scalar (own-column) fields a router filter reads, e.g. 'featured' from
|
|
147
|
-
// 'featured:true'. Dotted clauses (e.g. tags.visibility) are relation
|
|
148
|
-
// sub-fields and skipped — getRequiredRelations loads those relations.
|
|
149
|
-
//
|
|
150
|
-
// Only field names at an NQL expression boundary (start of the filter, or after
|
|
151
|
-
// a `+`/`,`/`(` combinator) are matched, so colon-bearing values — URLs,
|
|
152
|
-
// timestamps like 2020-01-01T00:00:00 — aren't mistaken for fields.
|
|
153
|
-
function filterScalarFields(filter: string | null): string[] {
|
|
154
|
-
if (!filter) {
|
|
155
|
-
return [];
|
|
156
|
-
}
|
|
157
|
-
const fields = new Set<string>();
|
|
158
|
-
const matcher = /(?:^|[+,(])\s*(\w+)(\.\w+)?:/g;
|
|
159
|
-
let match;
|
|
160
|
-
while ((match = matcher.exec(filter)) !== null) {
|
|
161
|
-
const [, root, sub] = match;
|
|
162
|
-
if (sub || FILTER_NON_SCALAR_FIELDS.has(root)) {
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
fields.add(root);
|
|
166
|
-
}
|
|
167
|
-
return [...fields];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
interface LazyUrlServiceDeps {
|
|
171
|
-
urlUtils?: typeof localUtils;
|
|
172
|
-
findResource: FindResource;
|
|
173
|
-
fetchRoutableResources?: FetchRoutableResources;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const ROUTER_TYPE_TO_DB_TYPE: Record<string, string> = {posts: 'post', pages: 'page'};
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Ghost's URL service: computes URLs and ownership per call from the
|
|
180
|
-
* registered router configs. Forward lookups are pure; resolveUrl and
|
|
181
|
-
* getRoutableResources are the only DB-touching paths, and only through the
|
|
182
|
-
* injected findResource / fetchRoutableResources hooks.
|
|
183
|
-
*/
|
|
184
|
-
export class LazyUrlService {
|
|
185
|
-
private urlUtils: typeof localUtils;
|
|
186
|
-
private findResource: FindResource;
|
|
187
|
-
private fetchRoutableResources: FetchRoutableResources;
|
|
188
|
-
// Router configs in registration order, which is also their priority.
|
|
189
|
-
private routerConfigs: RouterConfig[];
|
|
190
|
-
private requiredRelations: string[] | null;
|
|
191
|
-
private baseFilters: Map<string, BaseFilter>;
|
|
192
|
-
private excludedFilterFields: Map<string, Set<string>>;
|
|
193
|
-
// True once routers have been registered. Cleared by reset() so the
|
|
194
|
-
// route-reload window re-gates the maintenance middleware.
|
|
195
|
-
private routersReady: boolean;
|
|
196
|
-
// How many times each thin-resource cause has been seen — see
|
|
197
|
-
// _degradeThinResource.
|
|
198
|
-
private reportedThinResources: Map<string, number>;
|
|
199
|
-
|
|
200
|
-
constructor({
|
|
201
|
-
urlUtils = localUtils,
|
|
202
|
-
findResource,
|
|
203
|
-
fetchRoutableResources = defaultFetchRoutableResources
|
|
204
|
-
}: LazyUrlServiceDeps) {
|
|
205
|
-
if (typeof findResource !== 'function') {
|
|
206
|
-
throw new errors.IncorrectUsageError({
|
|
207
|
-
message: 'LazyUrlService requires a findResource function'
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
this.urlUtils = urlUtils;
|
|
211
|
-
this.findResource = findResource;
|
|
212
|
-
this.fetchRoutableResources = fetchRoutableResources;
|
|
213
|
-
this.routerConfigs = [];
|
|
214
|
-
this.requiredRelations = null;
|
|
215
|
-
this.baseFilters = buildBaseFilters();
|
|
216
|
-
this.excludedFilterFields = buildExcludedFilterFields();
|
|
217
|
-
this.routersReady = false;
|
|
218
|
-
this.reportedThinResources = new Map();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
onRouterAddedType(identifier: string, filter: string | null, resourceType: string, permalink: string): void {
|
|
222
|
-
debug('onRouterAddedType', identifier, resourceType, permalink, filter);
|
|
223
|
-
this.routerConfigs.push({
|
|
224
|
-
identifier,
|
|
225
|
-
filter,
|
|
226
|
-
resourceType,
|
|
227
|
-
permalink,
|
|
228
|
-
compiledFilter: buildFilter(filter)
|
|
229
|
-
});
|
|
230
|
-
this.requiredRelations = null;
|
|
231
|
-
this.routersReady = true;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
onRouterUpdated(): void {
|
|
235
|
-
// Defensive: a router update could change a filter the cache derived
|
|
236
|
-
// from, so drop it and recompute lazily on next read.
|
|
237
|
-
this.requiredRelations = null;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
reset(): void {
|
|
241
|
-
this.routerConfigs = [];
|
|
242
|
-
this.requiredRelations = null;
|
|
243
|
-
this.routersReady = false;
|
|
244
|
-
this.reportedThinResources.clear();
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
getRequiredRelations(): string[] {
|
|
248
|
-
if (this.requiredRelations !== null) {
|
|
249
|
-
return [...this.requiredRelations];
|
|
250
|
-
}
|
|
251
|
-
const required = new Set<string>();
|
|
252
|
-
for (const config of this.routerConfigs) {
|
|
253
|
-
if (config.filter) {
|
|
254
|
-
if (/\btags?\b/.test(config.filter) || /\bprimary_tag\b/.test(config.filter)) {
|
|
255
|
-
required.add('tags');
|
|
256
|
-
}
|
|
257
|
-
if (/\bauthors?\b/.test(config.filter) || /\bprimary_author\b/.test(config.filter)) {
|
|
258
|
-
required.add('authors');
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (config.permalink) {
|
|
262
|
-
if (/\bprimary_tag\b/.test(config.permalink)) {
|
|
263
|
-
required.add('tags');
|
|
264
|
-
}
|
|
265
|
-
if (/\bprimary_author\b/.test(config.permalink)) {
|
|
266
|
-
required.add('authors');
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
this.requiredRelations = [...required];
|
|
271
|
-
return [...this.requiredRelations];
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Columns a resource of this type must carry for the service to build
|
|
275
|
-
// its URL: its base-filter columns plus the scalar columns its routers'
|
|
276
|
-
// permalinks substitute and filters read. Relations are covered separately
|
|
277
|
-
// by getRequiredRelations.
|
|
278
|
-
getRequiredFields(routerType: string): string[] {
|
|
279
|
-
const fields = new Set<string>();
|
|
280
|
-
const excluded = this._excludedFilterFieldsFor(routerType);
|
|
281
|
-
const base = this.baseFilters.get(routerType);
|
|
282
|
-
if (base) {
|
|
283
|
-
base.fields.forEach(field => fields.add(field));
|
|
284
|
-
}
|
|
285
|
-
for (const config of this.routerConfigs) {
|
|
286
|
-
if (config.resourceType !== routerType) {
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
if (/\bslug\b/.test(config.permalink)) {
|
|
290
|
-
fields.add('slug');
|
|
291
|
-
}
|
|
292
|
-
if (/\bid\b/.test(config.permalink)) {
|
|
293
|
-
fields.add('id');
|
|
294
|
-
}
|
|
295
|
-
if (/\b(year|month|day)\b/.test(config.permalink)) {
|
|
296
|
-
fields.add('published_at');
|
|
297
|
-
}
|
|
298
|
-
// primary_tag/primary_author are computed attributes the model
|
|
299
|
-
// only attaches when `options.columns` names them, so they must
|
|
300
|
-
// be forced like scalar columns — the tags/authors relations
|
|
301
|
-
// (via getRequiredRelations) alone don't surface them on a
|
|
302
|
-
// `?fields=url` query.
|
|
303
|
-
for (const computed of ['primary_tag', 'primary_author'] as const) {
|
|
304
|
-
if (new RegExp(`\\b${computed}\\b`).test(config.permalink) ||
|
|
305
|
-
(config.filter && new RegExp(`\\b${computed}\\b`).test(config.filter))) {
|
|
306
|
-
fields.add(computed);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
// Skip the columns a router filter must not see: their filters are
|
|
310
|
-
// evaluated against an absent value, so requiring them would
|
|
311
|
-
// force-load a column that can't affect the result (see
|
|
312
|
-
// buildExcludedFilterFields).
|
|
313
|
-
filterScalarFields(config.filter).forEach((field) => {
|
|
314
|
-
if (!excluded.has(field)) {
|
|
315
|
-
fields.add(field);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
return [...fields];
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
hasFinished(): boolean {
|
|
323
|
-
return this.routersReady;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* All routable rows of a type. The columns URL computation needs come
|
|
328
|
-
* from the active routing config, so the rows are never thin for it;
|
|
329
|
-
* callers name any extra columns they want back.
|
|
330
|
-
*/
|
|
331
|
-
async getRoutableResources(type: string, {columns = []}: {columns?: string[]} = {}): Promise<Array<Record<string, unknown>>> {
|
|
332
|
-
return this.fetchRoutableResources(type, {
|
|
333
|
-
columns,
|
|
334
|
-
requiredFields: this.getRequiredFields(type),
|
|
335
|
-
requiredRelations: this.getRequiredRelations()
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* A thin resource — one missing a column its base filter or a router
|
|
341
|
-
* filter reads — is a caller bug: we cannot tell whether it routes. It is
|
|
342
|
-
* reported and degraded to /404/ rather than thrown, because 500ing a page
|
|
343
|
-
* that does route is worse than a 404 on one that may not.
|
|
344
|
-
*/
|
|
345
|
-
getUrlForResource(resource: Resource, options: UrlOptions = {}): string {
|
|
346
|
-
const routerType = routerTypeOf(resource);
|
|
347
|
-
if (!routerType) {
|
|
348
|
-
return this.notFoundUrl(options);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const record = this._recordForFilter(resource);
|
|
352
|
-
// The router (collection) filter is evaluated against the reduced
|
|
353
|
-
// column set (see buildExcludedFilterFields); the base filter keeps the
|
|
354
|
-
// full record, since it reads status / type / visibility.
|
|
355
|
-
const filterRecord = this._recordForRouterFilter(record, routerType);
|
|
356
|
-
|
|
357
|
-
// Only resources that pass the per-type base filter (visibility:public
|
|
358
|
-
// tags, status:published posts) get a URL. Checked only when a router
|
|
359
|
-
// for the type exists, since otherwise the resource 404s regardless.
|
|
360
|
-
if (this._hasRouterForType(routerType)) {
|
|
361
|
-
const missing = this._missingBaseFields(routerType, resource);
|
|
362
|
-
if (missing) {
|
|
363
|
-
return this._degradeThinResource(resource, missing, options);
|
|
364
|
-
}
|
|
365
|
-
if (!this._baseFilterMatches(routerType, record)) {
|
|
366
|
-
return this.notFoundUrl(options);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
for (const config of this.routerConfigs) {
|
|
371
|
-
if (config.resourceType !== routerType) {
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
const missing = this._missingRouterFields(config, resource, routerType);
|
|
375
|
-
if (missing) {
|
|
376
|
-
return this._degradeThinResource(resource, missing, options);
|
|
377
|
-
}
|
|
378
|
-
if (filterMatches(config.compiledFilter, filterRecord)) {
|
|
379
|
-
const path = this.urlUtils.replacePermalink(config.permalink, resource);
|
|
380
|
-
return this._formatPath(path, options);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return this.notFoundUrl(options);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
ownsResource(routerIdentifier: string, resource: Resource | null): boolean {
|
|
387
|
-
if (!resource) {
|
|
388
|
-
return false;
|
|
389
|
-
}
|
|
390
|
-
const routerType = routerTypeOf(resource);
|
|
391
|
-
if (!routerType) {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
// A resource failing its base filter has no URL, so no router owns it.
|
|
395
|
-
// Mirrors the base-filter gate in getUrlForResource.
|
|
396
|
-
const record = this._recordForFilter(resource);
|
|
397
|
-
if (!this._baseFilterMatches(routerType, record)) {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
const filterRecord = this._recordForRouterFilter(record, routerType);
|
|
401
|
-
// Ownership is exclusive: only the first matching router of the type
|
|
402
|
-
// owns the resource.
|
|
403
|
-
const owner = this.routerConfigs.find(
|
|
404
|
-
c => c.resourceType === routerType && filterMatches(c.compiledFilter, filterRecord)
|
|
405
|
-
);
|
|
406
|
-
return !!owner && owner.identifier === routerIdentifier;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
async resolveUrl(urlPath: string): Promise<Resource | null> {
|
|
410
|
-
// Memoize per call so routers sharing a resourceType+permalink shape (or
|
|
411
|
-
// fallthrough across filters) don't repeat the same DB lookup.
|
|
412
|
-
const lookupCache = new Map<string, Record<string, unknown> | null>();
|
|
413
|
-
for (const config of this.routerConfigs) {
|
|
414
|
-
const params = matchPermalink(config.permalink, urlPath);
|
|
415
|
-
if (!params) {
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
// matchPermalink only matches permalinks that capture a queryable
|
|
419
|
-
// column, so this always yields a usable lookup.
|
|
420
|
-
const lookupParams = toLookupParams(params);
|
|
421
|
-
const cacheKey = `${config.resourceType}:${JSON.stringify(lookupParams)}`;
|
|
422
|
-
let resource: Record<string, unknown> | null;
|
|
423
|
-
if (lookupCache.has(cacheKey)) {
|
|
424
|
-
resource = lookupCache.get(cacheKey) ?? null;
|
|
425
|
-
} else {
|
|
426
|
-
resource = await this.findResource(config.resourceType, lookupParams);
|
|
427
|
-
lookupCache.set(cacheKey, resource);
|
|
428
|
-
}
|
|
429
|
-
if (!resource) {
|
|
430
|
-
continue;
|
|
431
|
-
}
|
|
432
|
-
// Normalize the same way the forward paths do so page: filters are
|
|
433
|
-
// evaluated against an identical shape regardless of findResource.
|
|
434
|
-
// The base filter is enforced upstream by findResource's query
|
|
435
|
-
// scoping (visibility:public / status:published), so only the
|
|
436
|
-
// router filter needs re-checking here.
|
|
437
|
-
const record = this._recordForRouterFilter(
|
|
438
|
-
this._recordForFilter(resource as Resource),
|
|
439
|
-
config.resourceType
|
|
440
|
-
);
|
|
441
|
-
if (!filterMatches(config.compiledFilter, record)) {
|
|
442
|
-
continue;
|
|
443
|
-
}
|
|
444
|
-
if (!this._matchesCanonicalUrl(config, params, resource)) {
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
|
-
return Object.assign({}, resource, {type: config.resourceType}) as Resource;
|
|
448
|
-
}
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Only a URL that equals the resource's own generated (canonical) URL
|
|
453
|
-
// resolves, so we regenerate the record's URL for this permalink and
|
|
454
|
-
// confirm the captured params match it. Without this, derived/relation
|
|
455
|
-
// segments the query can't filter on (year/month, primary_tag) would
|
|
456
|
-
// resolve any slug, 200-ing a URL that has no page.
|
|
457
|
-
private _matchesCanonicalUrl(config: RouterConfig, params: PermalinkParams, resource: Record<string, unknown>): boolean {
|
|
458
|
-
const canonicalPath = this.urlUtils.replacePermalink(config.permalink, resource);
|
|
459
|
-
const canonicalParams = matchPermalink(config.permalink, canonicalPath);
|
|
460
|
-
if (!canonicalParams) {
|
|
461
|
-
return false;
|
|
462
|
-
}
|
|
463
|
-
const captured = params as Record<string, string>;
|
|
464
|
-
const canonical = canonicalParams as Record<string, string>;
|
|
465
|
-
return Object.keys(captured).every(key => canonical[key] === captured[key]);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Reports which columns were missing alongside what the caller handed over
|
|
469
|
-
// — the resource's shape, and what the active routing config needs — so
|
|
470
|
-
// the fetch that under-fetched can be found from the log line alone.
|
|
471
|
-
//
|
|
472
|
-
// Reported on the first occurrence of each distinct cause, then at each
|
|
473
|
-
// order of magnitude. A caller that under-fetches does so for every row it
|
|
474
|
-
// serializes, so a browse over a large collection would otherwise emit one
|
|
475
|
-
// error per row — but reporting only once would hide that the problem is
|
|
476
|
-
// still happening, and how widely. `occurrences` carries that instead.
|
|
477
|
-
// Cleared on reset(), since a new routing config can make a previously
|
|
478
|
-
// thin resource fine (or newly broken).
|
|
479
|
-
private _degradeThinResource(resource: Resource, thin: ThinResource, options: UrlOptions): string {
|
|
480
|
-
const producedBy = options.serializerContext;
|
|
481
|
-
// The producer is part of the key: two endpoints under-fetching the
|
|
482
|
-
// same way are two bugs, and the endpoint set is small enough that
|
|
483
|
-
// this cannot flood.
|
|
484
|
-
const key = [
|
|
485
|
-
thin.resourceType,
|
|
486
|
-
thin.routerIdentifier ?? '',
|
|
487
|
-
thin.missing.join(','),
|
|
488
|
-
producedBy ? `${producedBy.apiType}:${producedBy.docName}:${producedBy.method}` : ''
|
|
489
|
-
].join('|');
|
|
490
|
-
const occurrences = (this.reportedThinResources.get(key) ?? 0) + 1;
|
|
491
|
-
this.reportedThinResources.set(key, occurrences);
|
|
492
|
-
if (!isPowerOfTen(occurrences)) {
|
|
493
|
-
return this.notFoundUrl(options);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
logging.error(new errors.InternalServerError({
|
|
497
|
-
message: 'URL service could not build a URL, degraded to /404/',
|
|
498
|
-
code: 'LAZY_URL_RESOLUTION_ERROR',
|
|
499
|
-
errorDetails: {
|
|
500
|
-
method: 'getUrlForResource',
|
|
501
|
-
type: resource.type,
|
|
502
|
-
id: resource.id,
|
|
503
|
-
status: (resource as Record<string, unknown>).status,
|
|
504
|
-
resourceKeys: Object.keys(resource),
|
|
505
|
-
requiredRelations: this.getRequiredRelations(),
|
|
506
|
-
occurrences,
|
|
507
|
-
...(producedBy ? {serializer: producedBy} : {}),
|
|
508
|
-
...thin
|
|
509
|
-
}
|
|
510
|
-
}));
|
|
511
|
-
return this.notFoundUrl(options);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
private _hasRouterForType(routerType: string): boolean {
|
|
515
|
-
return this.routerConfigs.some(config => config.resourceType === routerType);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
private _baseFilterMatches(routerType: string, record: Record<string, unknown>): boolean {
|
|
519
|
-
const base = this.baseFilters.get(routerType);
|
|
520
|
-
if (!base) {
|
|
521
|
-
return true;
|
|
522
|
-
}
|
|
523
|
-
return filterMatches(base.compiledFilter, record);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// A resource that reaches URL generation must carry the columns its base
|
|
527
|
-
// filter reads (status for posts/pages, visibility for tags) — production
|
|
528
|
-
// callers always do, via full models or the serializers' force-load.
|
|
529
|
-
// Without them the filter can't be evaluated at all, so we cannot tell a
|
|
530
|
-
// genuine miss from a caller that under-fetched.
|
|
531
|
-
private _missingBaseFields(routerType: string, resource: Resource): ThinResource | null {
|
|
532
|
-
const base = this.baseFilters.get(routerType);
|
|
533
|
-
if (!base) {
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
const r = resource as Record<string, unknown>;
|
|
537
|
-
const missing = base.fields.filter(field => r[field] === undefined);
|
|
538
|
-
if (missing.length === 0) {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
return {resourceType: routerType, baseFilter: base.filter, missing};
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Normalizes the plural router type to the singular DB value for filter
|
|
545
|
-
// evaluation only, so `page:` filters in routes.yaml match.
|
|
546
|
-
private _recordForFilter(resource: Resource): Record<string, unknown> {
|
|
547
|
-
const record = resource as Record<string, unknown>;
|
|
548
|
-
const dbType = ROUTER_TYPE_TO_DB_TYPE[record.type as string];
|
|
549
|
-
return dbType ? {...record, type: dbType} : record;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
private _excludedFilterFieldsFor(routerType: string): ReadonlySet<string> {
|
|
553
|
-
return this.excludedFilterFields.get(routerType) ?? EMPTY_FIELD_SET;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Strips the columns a router/collection filter must not see, so an
|
|
557
|
-
// excluded column reads as absent (→ null in NQL) rather than its real
|
|
558
|
-
// value. Only affects filters that reference an excluded column; every
|
|
559
|
-
// other filter sees an identical record.
|
|
560
|
-
private _recordForRouterFilter(record: Record<string, unknown>, routerType: string): Record<string, unknown> {
|
|
561
|
-
const excluded = this._excludedFilterFieldsFor(routerType);
|
|
562
|
-
if (excluded.size === 0) {
|
|
563
|
-
return record;
|
|
564
|
-
}
|
|
565
|
-
const stripped: Record<string, unknown> = {};
|
|
566
|
-
for (const key of Object.keys(record)) {
|
|
567
|
-
if (!excluded.has(key)) {
|
|
568
|
-
stripped[key] = record[key];
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return stripped;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
private _formatPath(path: string, options: UrlOptions): string {
|
|
575
|
-
if (options.absolute) {
|
|
576
|
-
return this.urlUtils.createUrl(path, options.absolute);
|
|
577
|
-
}
|
|
578
|
-
if (options.withSubdirectory) {
|
|
579
|
-
return this.urlUtils.createUrl(path, false, true);
|
|
580
|
-
}
|
|
581
|
-
return path;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* The /404/ a resource gets when nothing routes it, formatted for the given
|
|
586
|
-
* options. Deliberately omits the argument `_formatPath` passes.
|
|
587
|
-
*
|
|
588
|
-
* The subdirectory is not lost by that omission: `createUrl` takes it from
|
|
589
|
-
* its own base whenever the url is relative, so a subdirectory install gets
|
|
590
|
-
* `/blog/404/` from here either way. The third argument `_formatPath`
|
|
591
|
-
* passes is `trailingSlash`, and `/404/` already ends in one.
|
|
592
|
-
*/
|
|
593
|
-
private notFoundUrl(options: UrlOptions = {}): string {
|
|
594
|
-
if (options.absolute) {
|
|
595
|
-
return this.urlUtils.createUrl('/404/', options.absolute);
|
|
596
|
-
}
|
|
597
|
-
if (options.withSubdirectory) {
|
|
598
|
-
return this.urlUtils.createUrl('/404/', false);
|
|
599
|
-
}
|
|
600
|
-
return '/404/';
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// A filtered router that references a relation the resource doesn't carry
|
|
604
|
-
// can't be evaluated, so the resource would 404 for a reason that has
|
|
605
|
-
// nothing to do with the routing config. Callers must hand over
|
|
606
|
-
// fully-inflated resources; a thin one is reported rather than silently
|
|
607
|
-
// 404'd (see getUrlForResource).
|
|
608
|
-
private _missingRouterFields(config: RouterConfig, resource: Resource, routerType: string): ThinResource | null {
|
|
609
|
-
if (!config.filter) {
|
|
610
|
-
return null;
|
|
611
|
-
}
|
|
612
|
-
const r = resource as Record<string, unknown>;
|
|
613
|
-
const missing: string[] = [];
|
|
614
|
-
if (/\btags?\b/.test(config.filter) && !Array.isArray(r.tags)) {
|
|
615
|
-
missing.push('tags');
|
|
616
|
-
}
|
|
617
|
-
if (/\bauthors?\b/.test(config.filter) && !Array.isArray(r.authors)) {
|
|
618
|
-
missing.push('authors');
|
|
619
|
-
}
|
|
620
|
-
if (/\bprimary_tag\b/.test(config.filter) && r.primary_tag === undefined) {
|
|
621
|
-
missing.push('primary_tag');
|
|
622
|
-
}
|
|
623
|
-
if (/\bprimary_author\b/.test(config.filter) && r.primary_author === undefined) {
|
|
624
|
-
missing.push('primary_author');
|
|
625
|
-
}
|
|
626
|
-
// Excluded columns are never required — their filters match them as
|
|
627
|
-
// absent, so a resource lacking one is not thin here.
|
|
628
|
-
const excluded = this._excludedFilterFieldsFor(routerType);
|
|
629
|
-
for (const field of filterScalarFields(config.filter)) {
|
|
630
|
-
if (!excluded.has(field) && r[field] === undefined) {
|
|
631
|
-
missing.push(field);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (missing.length === 0) {
|
|
635
|
-
return null;
|
|
636
|
-
}
|
|
637
|
-
return {
|
|
638
|
-
resourceType: routerType,
|
|
639
|
-
routerIdentifier: config.identifier,
|
|
640
|
-
filter: config.filter,
|
|
641
|
-
missing
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
module.exports = LazyUrlService;
|
|
647
|
-
module.exports.LazyUrlService = LazyUrlService;
|