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,28 +3,35 @@ import errors from '@tryghost/errors';
|
|
|
3
3
|
import logging from '@tryghost/logging';
|
|
4
4
|
import type {Knex} from 'knex';
|
|
5
5
|
import {z} from 'zod';
|
|
6
|
-
import {FIELD_TYPES, type FieldType} from '@tryghost/custom-field-types';
|
|
7
|
-
import {
|
|
6
|
+
import {FIELD_TYPES, subFieldsOf, type FieldType} from '@tryghost/custom-field-types';
|
|
7
|
+
import {DbCustomFieldLeaf, DbCustomFieldValue, FIELD_STATUS} from './schema';
|
|
8
8
|
import {activeFields} from './queries';
|
|
9
|
-
import {
|
|
9
|
+
import {leavesToWrite, valuesFromLeaves, type StoredLeaf} from './storage';
|
|
10
10
|
|
|
11
11
|
const FIELDS_TABLE = 'members_custom_fields';
|
|
12
12
|
const VALUES_TABLE = 'members_custom_field_values';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
// could actually have minted is ever refused by it.
|
|
14
|
+
/** Matches the `members_custom_fields.key` column, so no key a site could hold is refused. */
|
|
16
15
|
const MAX_KEY_LENGTH = 191;
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Rows per insert statement, bounded by knex rather than by either database. SQLite takes
|
|
19
|
+
* a multi-row `VALUES` perfectly well; knex's SQLite dialect emits that form only for a
|
|
20
|
+
* single row and compiles anything longer into `INSERT ... SELECT ? UNION ALL SELECT ?`,
|
|
21
|
+
* which SQLite refuses past 500 terms.
|
|
22
|
+
*
|
|
23
|
+
* Open as knex#721 since 2015, with an approved but unmerged fix in knex#5780. This can
|
|
24
|
+
* go when that lands; until then the alternative is hand-written upsert SQL per engine.
|
|
25
|
+
*/
|
|
26
|
+
const UPSERT_CHUNK = 400;
|
|
27
|
+
|
|
28
|
+
/** Derived, not restated, so a column changing shape in `schema.ts` changes here too. */
|
|
29
|
+
type DbLeafRow = z.infer<typeof DbCustomFieldValue>;
|
|
30
|
+
|
|
31
|
+
// Values stay `unknown`: each is validated by its own field type, which is not known
|
|
32
|
+
// until the key is resolved to a definition.
|
|
21
33
|
const ValuesInput = z.record(z.string().max(MAX_KEY_LENGTH), z.unknown());
|
|
22
34
|
|
|
23
|
-
// The field facts the value path needs: the id to write the FK, the key to match
|
|
24
|
-
// input against, the name for error messages, and the type to pick the validator
|
|
25
|
-
// and storage column. All columns on members_custom_fields, so a value operation
|
|
26
|
-
// resolves them with a direct query rather than routing through the definitions
|
|
27
|
-
// service — the same way the read path already joins the table directly.
|
|
28
35
|
interface ActiveField {
|
|
29
36
|
id: string;
|
|
30
37
|
key: string;
|
|
@@ -32,30 +39,20 @@ interface ActiveField {
|
|
|
32
39
|
type: FieldType;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
/** An absent `value` means clear the field. */
|
|
36
43
|
interface PlannedWrite {
|
|
37
44
|
field: ActiveField;
|
|
38
45
|
value?: unknown;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* aggregate boundary, not a technical layer, is why this is a separate service.
|
|
46
|
-
*
|
|
47
|
-
* It owns the `members_custom_field_values` table and reads `members_custom_fields`
|
|
48
|
-
* directly for the reference data it needs (which fields are active, and how they
|
|
49
|
-
* validate), rather than calling the definitions service — the same way the other
|
|
50
|
-
* knex services here talk to the database.
|
|
49
|
+
* What a member holds for each defined field. Separate from the definitions service
|
|
50
|
+
* because a value belongs to the member and a definition belongs to the site's settings,
|
|
51
|
+
* which are different aggregates rather than different layers.
|
|
51
52
|
*/
|
|
52
53
|
export class CustomFieldValuesService {
|
|
53
54
|
private knex: Knex;
|
|
54
|
-
/**
|
|
55
|
-
* @private
|
|
56
|
-
* A getter rather than a number: the ceiling is an operator setting that can
|
|
57
|
-
* change between requests.
|
|
58
|
-
*/
|
|
55
|
+
/** A getter, not a number: the ceiling is an operator setting that changes between requests. */
|
|
59
56
|
private getMaxDefinitions: () => number;
|
|
60
57
|
|
|
61
58
|
constructor({knex, getMaxDefinitions}: {knex: Knex, getMaxDefinitions: () => number}) {
|
|
@@ -63,11 +60,6 @@ export class CustomFieldValuesService {
|
|
|
63
60
|
this.getMaxDefinitions = getMaxDefinitions;
|
|
64
61
|
}
|
|
65
62
|
|
|
66
|
-
/**
|
|
67
|
-
* The active fields for a set of keys, keyed by key, for resolving a write.
|
|
68
|
-
* Scoped to the keys the caller is actually writing rather than every active
|
|
69
|
-
* field, since a publisher may have defined many and an edit touches few.
|
|
70
|
-
*/
|
|
71
63
|
private async activeFieldsByKey(keys: string[]): Promise<Map<string, ActiveField>> {
|
|
72
64
|
if (keys.length === 0) {
|
|
73
65
|
return new Map();
|
|
@@ -79,63 +71,39 @@ export class CustomFieldValuesService {
|
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
/**
|
|
82
|
-
* Members' values
|
|
83
|
-
* key. A member with no values is absent from the outer map; a field a member
|
|
84
|
-
* has no value for is absent from the inner one — "not set" is the absence of
|
|
85
|
-
* a key, told apart from a value by not being there. Archived fields are
|
|
86
|
-
* excluded to match the definitions `browse`: their values stay in the
|
|
87
|
-
* database, they just stop being addressable.
|
|
88
|
-
*
|
|
89
|
-
* One query for the whole set, so a member list can't turn into an N+1.
|
|
74
|
+
* Members' values, keyed by member id then field key; anything absent is unset.
|
|
90
75
|
*
|
|
91
|
-
*
|
|
92
|
-
* the
|
|
93
|
-
*
|
|
94
|
-
* take down a member's payload.
|
|
76
|
+
* Archived fields are excluded to match the definitions browse: their values stay in
|
|
77
|
+
* the database and stop being addressable. A row that will not parse is dropped and
|
|
78
|
+
* logged rather than failing the read, so one stale row cannot take down a member.
|
|
95
79
|
*/
|
|
96
80
|
async getValuesForMembers(memberIds: string[]): Promise<Map<string, Record<string, unknown>>> {
|
|
97
|
-
const byMember = new Map<string, Record<string, unknown>>();
|
|
98
81
|
if (memberIds.length === 0) {
|
|
99
|
-
return
|
|
82
|
+
return new Map();
|
|
100
83
|
}
|
|
101
84
|
|
|
102
85
|
const rows = await this.knex(VALUES_TABLE)
|
|
103
86
|
.join(FIELDS_TABLE, `${VALUES_TABLE}.custom_field_id`, `${FIELDS_TABLE}.id`)
|
|
104
87
|
.whereIn(`${VALUES_TABLE}.member_id`, memberIds)
|
|
105
|
-
// Read join, so the status filter is qualified and lives inline — it
|
|
106
|
-
// shares the invariant's vocabulary with queries.ts, not its builder.
|
|
107
88
|
.where(`${FIELDS_TABLE}.status`, FIELD_STATUS.active)
|
|
108
89
|
.orderBy(`${FIELDS_TABLE}.created_at`, 'asc')
|
|
109
90
|
.orderBy(`${FIELDS_TABLE}.id`, 'asc')
|
|
110
|
-
.
|
|
91
|
+
.orderBy(`${VALUES_TABLE}.path`, 'asc')
|
|
92
|
+
.select(`${VALUES_TABLE}.member_id`, `${FIELDS_TABLE}.key`, `${FIELDS_TABLE}.type`, `${VALUES_TABLE}.path`, `${VALUES_TABLE}.value_text`);
|
|
111
93
|
|
|
94
|
+
const leaves: StoredLeaf[] = [];
|
|
112
95
|
for (const row of rows) {
|
|
113
96
|
try {
|
|
114
|
-
|
|
115
|
-
const codec = storageCodecFor(type);
|
|
116
|
-
const raw = stored[codec.column];
|
|
117
|
-
// The column the type routes to is the only one that can hold this
|
|
118
|
-
// field's value; a null there means the row is not carrying one.
|
|
119
|
-
if (raw === null) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
const values = byMember.get(memberId) ?? {};
|
|
123
|
-
values[key] = codec.decode(raw);
|
|
124
|
-
byMember.set(memberId, values);
|
|
97
|
+
leaves.push(DbCustomFieldLeaf.parse(row));
|
|
125
98
|
} catch (err) {
|
|
126
|
-
logging.warn(`Skipping
|
|
99
|
+
logging.warn(`Skipping unreadable custom field value (field '${row.key}', path '${row.path}'): ${err instanceof Error ? err.message : String(err)}`);
|
|
127
100
|
}
|
|
128
101
|
}
|
|
129
102
|
|
|
130
|
-
return
|
|
103
|
+
return valuesFromLeaves(leaves);
|
|
131
104
|
}
|
|
132
105
|
|
|
133
|
-
/**
|
|
134
|
-
* @private
|
|
135
|
-
* Input as the values object it claims to be, rejecting anything that isn't
|
|
136
|
-
* one. Shared by every caller so they cannot disagree on what a values object
|
|
137
|
-
* is, or on the error when it isn't one.
|
|
138
|
-
*/
|
|
106
|
+
/** Shared by every caller so they cannot disagree on what a values object is. */
|
|
139
107
|
private parseValues(input: unknown): Record<string, unknown> {
|
|
140
108
|
const parsed = ValuesInput.safeParse(input);
|
|
141
109
|
if (!parsed.success) {
|
|
@@ -146,12 +114,8 @@ export class CustomFieldValuesService {
|
|
|
146
114
|
}
|
|
147
115
|
|
|
148
116
|
/**
|
|
149
|
-
* Whether input names any values.
|
|
150
|
-
*
|
|
151
|
-
* have raised.
|
|
152
|
-
*
|
|
153
|
-
* Answers the shape question alone, with no catalog lookup, so it can be asked
|
|
154
|
-
* before a write is known to be permitted.
|
|
117
|
+
* Whether input names any values. Asks the shape question alone, with no catalog
|
|
118
|
+
* lookup, so it can be asked before a write is known to be permitted.
|
|
155
119
|
*/
|
|
156
120
|
namesValues(input: unknown): boolean {
|
|
157
121
|
if (input === undefined) {
|
|
@@ -162,20 +126,16 @@ export class CustomFieldValuesService {
|
|
|
162
126
|
}
|
|
163
127
|
|
|
164
128
|
/**
|
|
165
|
-
* Resolve input into the writes it implies,
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* up front, before opening a transaction), then apply the same plan without
|
|
169
|
-
* re-resolving or re-validating.
|
|
129
|
+
* Resolve input into the writes it implies, writing nothing. Returned so a caller can
|
|
130
|
+
* validate before opening a transaction it would otherwise have to unwind, then apply
|
|
131
|
+
* the same plan without re-resolving it.
|
|
170
132
|
*/
|
|
171
133
|
async planWrite(input: unknown): Promise<PlannedWrite[]> {
|
|
172
134
|
const values = this.parseValues(input);
|
|
173
135
|
const keys = Object.keys(values);
|
|
174
136
|
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
// database driver's bound-parameter limit, which one key per parameter would
|
|
178
|
-
// otherwise exceed.
|
|
137
|
+
// Bounded by the definitions ceiling, which also holds the lookup below inside
|
|
138
|
+
// the driver's bound-parameter limit.
|
|
179
139
|
const maxKeys = this.getMaxDefinitions();
|
|
180
140
|
if (keys.length > maxKeys) {
|
|
181
141
|
throw new errors.ValidationError({
|
|
@@ -190,29 +150,29 @@ export class CustomFieldValuesService {
|
|
|
190
150
|
for (const [key, raw] of Object.entries(values)) {
|
|
191
151
|
const field = byKey.get(key);
|
|
192
152
|
if (!field) {
|
|
193
|
-
// Unknown
|
|
194
|
-
//
|
|
153
|
+
// Unknown or archived. Refused rather than ignored: a typo that silently
|
|
154
|
+
// drops what somebody typed is worse than a save that fails. The catalog
|
|
155
|
+
// applies the same rule to the parts of a composite value.
|
|
195
156
|
throw new errors.ValidationError({message: `Unknown custom field: ${key}`, property: `custom_fields.${key}`});
|
|
196
157
|
}
|
|
197
158
|
|
|
198
|
-
// `null` clears any field
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
if (raw === null || (raw === '' && FIELD_TYPES[field.type].storageType === 'text')) {
|
|
159
|
+
// `null` clears any field, and `''` clears one with no parts. For a value
|
|
160
|
+
// with parts `''` names nothing, so it is left to fail validation rather than
|
|
161
|
+
// being read as a silent delete.
|
|
162
|
+
if (raw === null || (raw === '' && subFieldsOf(field.type) === null)) {
|
|
203
163
|
writes.push({field});
|
|
204
164
|
continue;
|
|
205
165
|
}
|
|
206
166
|
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
167
|
+
// Message only, no `context`: the API error handler moves a message into
|
|
168
|
+
// `context` when `context` is empty and prepends it when it is not, so
|
|
169
|
+
// anything added here reaches the client glued to the front of the reason.
|
|
170
|
+
// Which field failed rides in `property`.
|
|
210
171
|
const value = FIELD_TYPES[field.type].value.safeParse(raw);
|
|
211
172
|
if (!value.success) {
|
|
212
173
|
const issue = value.error.issues[0];
|
|
213
174
|
throw new errors.ValidationError({
|
|
214
|
-
message:
|
|
215
|
-
context: issue.message,
|
|
175
|
+
message: issue.message,
|
|
216
176
|
property: [`custom_fields.${key}`, ...issue.path].join('.')
|
|
217
177
|
});
|
|
218
178
|
}
|
|
@@ -225,39 +185,81 @@ export class CustomFieldValuesService {
|
|
|
225
185
|
/**
|
|
226
186
|
* Apply a plan from `planWrite`.
|
|
227
187
|
*
|
|
228
|
-
*
|
|
229
|
-
* that
|
|
188
|
+
* A write touches the paths it names and nothing else, at every level: naming a path
|
|
189
|
+
* with an empty value clears that part, naming the field with `null` clears all of
|
|
190
|
+
* them, and saying nothing about a path leaves it alone. There is no whole-value
|
|
191
|
+
* replace, so a caller that does not know about a field cannot erase it.
|
|
230
192
|
*
|
|
231
|
-
* Always transactional
|
|
232
|
-
*
|
|
233
|
-
* failed value write takes the member with it; passed nothing it opens its own.
|
|
193
|
+
* Always transactional. Given an executor it joins that transaction, so the importer's
|
|
194
|
+
* failed value write takes its member with it; given none it opens its own.
|
|
234
195
|
*/
|
|
235
|
-
async applyWrite(
|
|
196
|
+
async applyWrite(
|
|
197
|
+
memberId: string,
|
|
198
|
+
writes: PlannedWrite[],
|
|
199
|
+
{executor = this.knex}: {executor?: Knex} = {}
|
|
200
|
+
): Promise<void> {
|
|
236
201
|
if (writes.length === 0) {
|
|
237
202
|
return;
|
|
238
203
|
}
|
|
239
204
|
|
|
240
205
|
const apply = async (trx: Knex) => {
|
|
241
|
-
|
|
242
|
-
|
|
206
|
+
// Built first, then sent as whole statements: a handful per member rather
|
|
207
|
+
// than one per part, under one timestamp, because a write happened once
|
|
208
|
+
// however many rows record it.
|
|
209
|
+
const now = new Date();
|
|
210
|
+
const clearedFields: string[] = [];
|
|
211
|
+
const clearedPaths: Array<{fieldId: string, paths: string[]}> = [];
|
|
212
|
+
const rows: DbLeafRow[] = [];
|
|
243
213
|
|
|
214
|
+
for (const {field, value} of writes) {
|
|
244
215
|
if (value === undefined) {
|
|
245
|
-
|
|
216
|
+
clearedFields.push(field.id);
|
|
246
217
|
continue;
|
|
247
218
|
}
|
|
248
219
|
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
220
|
+
const {set, cleared} = leavesToWrite(value);
|
|
221
|
+
if (cleared.length > 0) {
|
|
222
|
+
clearedPaths.push({fieldId: field.id, paths: cleared});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
rows.push(...set.map(leaf => ({
|
|
226
|
+
id: new ObjectID().toHexString(),
|
|
227
|
+
member_id: memberId,
|
|
228
|
+
custom_field_id: field.id,
|
|
229
|
+
path: leaf.path,
|
|
230
|
+
value_text: leaf.value_text,
|
|
231
|
+
created_at: now,
|
|
232
|
+
updated_at: now
|
|
233
|
+
})));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (clearedFields.length > 0) {
|
|
237
|
+
await trx(VALUES_TABLE).where('member_id', memberId).whereIn('custom_field_id', clearedFields).del();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (clearedPaths.length > 0) {
|
|
241
|
+
// One statement with a group per field, rather than a statement per field.
|
|
242
|
+
await trx(VALUES_TABLE).where('member_id', memberId).where((builder) => {
|
|
243
|
+
for (const {fieldId, paths} of clearedPaths) {
|
|
244
|
+
builder.orWhere(pair => pair.where('custom_field_id', fieldId).whereIn('path', paths));
|
|
245
|
+
}
|
|
246
|
+
}).del();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (let from = 0; from < rows.length; from += UPSERT_CHUNK) {
|
|
250
|
+
// Typed as the plain row because `merge` takes its columns as `keyof` the
|
|
251
|
+
// builder's record, which for a composite table registration is the scope
|
|
252
|
+
// names rather than the columns.
|
|
253
|
+
await trx<DbLeafRow>(VALUES_TABLE)
|
|
254
|
+
.insert(rows.slice(from, from + UPSERT_CHUNK))
|
|
255
|
+
// Naming the columns rather than giving values takes each from the row
|
|
256
|
+
// that lost the conflict, so every part updates to its own value.
|
|
257
|
+
.onConflict(['member_id', 'custom_field_id', 'path'])
|
|
258
|
+
.merge(['value_text', 'updated_at']);
|
|
256
259
|
}
|
|
257
260
|
};
|
|
258
261
|
|
|
259
|
-
//
|
|
260
|
-
// rather than nesting a savepoint under it, otherwise open one.
|
|
262
|
+
// knex's marker for a transactor: join it rather than nesting a savepoint under it.
|
|
261
263
|
if (executor.isTransaction) {
|
|
262
264
|
await apply(executor);
|
|
263
265
|
} else {
|
|
@@ -30,7 +30,7 @@ module.exports = class ResourceService {
|
|
|
30
30
|
*/
|
|
31
31
|
async getByURL(url) {
|
|
32
32
|
const path = this.#urlUtils.absoluteToRelative(url.href, {withoutSubdirectory: true});
|
|
33
|
-
const resource = await this.#urlService.
|
|
33
|
+
const resource = await this.#urlService.resolveUrl(path);
|
|
34
34
|
if (resource?.type === 'posts') {
|
|
35
35
|
return {
|
|
36
36
|
type: 'post',
|
|
@@ -20,7 +20,7 @@ const jobsService = require('../mentions-jobs');
|
|
|
20
20
|
// relations it reads for filtered collections (event-emitted models don't
|
|
21
21
|
// reliably carry them).
|
|
22
22
|
async function getPostData(post) {
|
|
23
|
-
const missing = urlService.
|
|
23
|
+
const missing = urlService.getRequiredRelations().filter(relation => !post.relations[relation]);
|
|
24
24
|
if (missing.length) {
|
|
25
25
|
await post.load(missing);
|
|
26
26
|
}
|
|
@@ -31,7 +31,7 @@ function getPostUrl(id, postData) {
|
|
|
31
31
|
const jsonModel = {...postData};
|
|
32
32
|
// The URL service routes by resource type. Pages and posts share the Post
|
|
33
33
|
// model, so the page's own type must reach forPost — otherwise it defaults
|
|
34
|
-
// to 'posts', matches no post collection, and 404s
|
|
34
|
+
// to 'posts', matches no post collection, and 404s.
|
|
35
35
|
const type = postData.type === 'page' ? 'pages' : 'posts';
|
|
36
36
|
outputSerializerUrlUtil.forPost(id, jsonModel, {options: {}}, type);
|
|
37
37
|
return jsonModel.url;
|
|
@@ -9,6 +9,7 @@ const charset = require('charset');
|
|
|
9
9
|
const iconv = require('iconv-lite');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const crypto = require('crypto');
|
|
12
|
+
const imageTransform = require('@tryghost/image-transform');
|
|
12
13
|
|
|
13
14
|
// Some sites block non-standard user agents so we need to mimic a typical browser
|
|
14
15
|
// Note: the Ghost/5.0 string _may_ be in use by 3rd parties so use caution when updating across majors
|
|
@@ -39,7 +40,26 @@ const messages = {
|
|
|
39
40
|
insufficientMetadata: 'URL contains insufficient metadata.',
|
|
40
41
|
unknownProvider: 'No provider found for supplied URL.',
|
|
41
42
|
unableToFetchOembed: 'Unable to fetch requested embed.',
|
|
42
|
-
unauthorized: 'URL contains a private resource.'
|
|
43
|
+
unauthorized: 'URL contains a private resource.',
|
|
44
|
+
unconvertibleSvg: 'SVG image is too large or compressed to convert.'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const SVG_RASTER_SIZE = 256;
|
|
48
|
+
const SVG_RASTER_TIMEOUT_SECONDS = 10;
|
|
49
|
+
const MAX_SVG_BYTES = 32 * 1024;
|
|
50
|
+
const GZIP_MAGIC = [0x1f, 0x8b];
|
|
51
|
+
|
|
52
|
+
const SVG_EXTENSIONS = new Set(['.svg', '.svgz']);
|
|
53
|
+
const SVG_SNIFF_BYTES = 1024;
|
|
54
|
+
|
|
55
|
+
const shouldRasterize = (buffer, ext) => {
|
|
56
|
+
if (SVG_EXTENSIONS.has(ext.toLowerCase())) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const head = buffer.subarray(0, SVG_SNIFF_BYTES).toString('utf8').trimStart();
|
|
61
|
+
|
|
62
|
+
return head.startsWith('<') && /<svg[\s:>]/i.test(head);
|
|
43
63
|
};
|
|
44
64
|
|
|
45
65
|
/**
|
|
@@ -175,14 +195,35 @@ class OEmbedService {
|
|
|
175
195
|
}
|
|
176
196
|
|
|
177
197
|
// Fetch image buffer from the URL
|
|
178
|
-
|
|
198
|
+
let imageBuffer = await this.fetchImageBuffer(imageUrl);
|
|
179
199
|
|
|
180
200
|
// Extract file name from URL
|
|
181
201
|
const fileName = path.basename(new URL(imageUrl).pathname);
|
|
182
|
-
|
|
202
|
+
let ext = path.extname(fileName);
|
|
183
203
|
const baseName = ext ? path.basename(fileName, ext) : fileName;
|
|
184
204
|
const name = this.imageStore.getSanitizedFileName(baseName);
|
|
185
205
|
|
|
206
|
+
if (shouldRasterize(imageBuffer, ext)) {
|
|
207
|
+
if (imageBuffer.length > MAX_SVG_BYTES || GZIP_MAGIC.every((byte, index) => imageBuffer[index] === byte)) {
|
|
208
|
+
throw new errors.ValidationError({
|
|
209
|
+
message: tpl(messages.unconvertibleSvg),
|
|
210
|
+
context: imageUrl
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
imageBuffer = await imageTransform.resizeFromBuffer(imageBuffer, {
|
|
215
|
+
// without `format` this returns the original bytes whenever they
|
|
216
|
+
// are smaller, storing the SVG under a .png name
|
|
217
|
+
format: 'png',
|
|
218
|
+
width: SVG_RASTER_SIZE,
|
|
219
|
+
height: SVG_RASTER_SIZE,
|
|
220
|
+
withoutEnlargement: false,
|
|
221
|
+
timeout: SVG_RASTER_TIMEOUT_SECONDS
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
ext = '.png';
|
|
225
|
+
}
|
|
226
|
+
|
|
186
227
|
const uniqueFileName = `${name}-${crypto.randomUUID()}${ext}`;
|
|
187
228
|
const targetPath = path.join(imageType, uniqueFileName);
|
|
188
229
|
|
|
@@ -312,6 +312,17 @@ class PostsService {
|
|
|
312
312
|
});
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
// The `comments.in_reply_to_id` references form chains between a post's
|
|
316
|
+
// comments, which MySQL cannot resolve while cascade-deleting them
|
|
317
|
+
// alongside `comments.parent_id`. Clear the references first so the
|
|
318
|
+
// `comments.post_id` cascade delete can do its job
|
|
319
|
+
await this.models.Post.bulkEdit(deleteIds, 'comments', {
|
|
320
|
+
data: {in_reply_to_id: null},
|
|
321
|
+
column: 'post_id',
|
|
322
|
+
transacting: options.transacting,
|
|
323
|
+
throwErrors: true
|
|
324
|
+
});
|
|
325
|
+
|
|
315
326
|
// Posts and emails
|
|
316
327
|
await this.models.Post.bulkDestroy(deleteEmailIds, 'emails', {transacting: options.transacting, throwErrors: true});
|
|
317
328
|
const result = await this.models.Post.bulkDestroy(deleteIds, 'posts', {...options, throwErrors: true});
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('services:route-settings:service');
|
|
2
2
|
const logging = require('@tryghost/logging');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
|
-
const tpl = require('@tryghost/tpl');
|
|
5
|
-
|
|
6
|
-
const messages = {
|
|
7
|
-
loadError: 'Could not load routes.yaml file.'
|
|
8
|
-
};
|
|
9
4
|
|
|
10
5
|
function isStoredContentError(err) {
|
|
11
6
|
return err.errorType === 'ValidationError' || err.errorType === 'IncorrectUsageError';
|
|
@@ -25,8 +20,7 @@ class DynamicRoutingService {
|
|
|
25
20
|
|
|
26
21
|
/**
|
|
27
22
|
* Wire the storage-layer dependency so the API surface (upload, download)
|
|
28
|
-
* works
|
|
29
|
-
* `start()` is never called.
|
|
23
|
+
* works from early boot, well before `start()` wires up routing.
|
|
30
24
|
*
|
|
31
25
|
* @param {object} deps
|
|
32
26
|
* @param {RouteSettingsStore} deps.store - adapter-manager provided store
|
|
@@ -38,11 +32,13 @@ class DynamicRoutingService {
|
|
|
38
32
|
|
|
39
33
|
/**
|
|
40
34
|
* Wire the routing dependencies and load route settings into the router.
|
|
41
|
-
* Called from initDynamicRouting in boot.js
|
|
35
|
+
* Called from initDynamicRouting in boot.js on every boot: the URL service
|
|
36
|
+
* has to learn the routes even where no page is served, so that the APIs and
|
|
37
|
+
* background services can build URLs.
|
|
42
38
|
*
|
|
43
39
|
* @param {object} deps
|
|
44
40
|
* @param {object} deps.routerManager - frontend RouterManager singleton
|
|
45
|
-
* @param {object} deps.urlService -
|
|
41
|
+
* @param {object} deps.urlService - the URL service singleton
|
|
46
42
|
*/
|
|
47
43
|
async start({routerManager, urlService}) {
|
|
48
44
|
debug('start');
|
|
@@ -54,10 +50,8 @@ class DynamicRoutingService {
|
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
async loadRouteSettings() {
|
|
57
|
-
const {buildRouterSettings} = require('./activation-bridge');
|
|
58
|
-
|
|
59
53
|
try {
|
|
60
|
-
return
|
|
54
|
+
return await this.store.get();
|
|
61
55
|
} catch (err) {
|
|
62
56
|
// A stored-content error means the site's routes.yaml is invalid —
|
|
63
57
|
// either it fails validation or it isn't parseable YAML. Log a
|
|
@@ -92,60 +86,14 @@ class DynamicRoutingService {
|
|
|
92
86
|
// Parse and validate before anything is persisted — an invalid
|
|
93
87
|
// upload is rejected here and never reaches the store.
|
|
94
88
|
const next = parseRouteSettings(parseYaml(yamlContent), yamlContent);
|
|
95
|
-
let previous = null;
|
|
96
|
-
try {
|
|
97
|
-
previous = await this.store.get();
|
|
98
|
-
} catch (err) {
|
|
99
|
-
if (!isStoredContentError(err)) {
|
|
100
|
-
throw err;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
89
|
|
|
104
90
|
await this.store.replace(next);
|
|
105
91
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (previous) {
|
|
112
|
-
await this.store.replace(previous);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return bridge.reloadFrontend(this, urlService);
|
|
116
|
-
};
|
|
117
|
-
|
|
92
|
+
// Registration is synchronous inside the reload: RouterManager.start()
|
|
93
|
+
// always mounts the static pages router, which has permalinks, so the
|
|
94
|
+
// URL service has routers again — and reports ready — by the time this
|
|
95
|
+
// resolves. Nothing left to wait for.
|
|
118
96
|
await bridge.reloadFrontend(this, urlService);
|
|
119
|
-
|
|
120
|
-
let tries = 0;
|
|
121
|
-
|
|
122
|
-
function isBlogRunning() {
|
|
123
|
-
debug('waiting for blog running');
|
|
124
|
-
return new Promise((resolve) => {
|
|
125
|
-
setTimeout(resolve, 1000);
|
|
126
|
-
})
|
|
127
|
-
.then(() => {
|
|
128
|
-
debug('waited for blog running');
|
|
129
|
-
if (!urlService.hasFinished()) {
|
|
130
|
-
if (tries > 5) {
|
|
131
|
-
throw new errors.InternalServerError({
|
|
132
|
-
message: tpl(messages.loadError)
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
tries = tries + 1;
|
|
137
|
-
return isBlogRunning();
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return isBlogRunning()
|
|
143
|
-
.catch((err) => {
|
|
144
|
-
return bringBackValidRoutes()
|
|
145
|
-
.finally(() => {
|
|
146
|
-
throw err;
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
97
|
}
|
|
150
98
|
}
|
|
151
99
|
|