ghost 6.56.0 → 6.57.1
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-adapter-base-scheduling-0.2.1.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.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-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/about-BNojRaxf.js +1 -0
- package/core/built/admin/assets/{account-migration-D3O_yVck.js → account-migration-DMS8JsM-.js} +1 -1
- package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-D8FbP-Z-.js → account-migration-DHFbePl6.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +3 -3
- package/core/built/admin/assets/activitypub/{avatar-11EA1VSs.js → avatar-CT4tS2_S.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-jfbLG-xe.js → avatar-flipboard-ByYWKipo.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-f1jTQe2P.js → bluesky-sharing-srVnSHDz.js} +8 -8
- package/core/built/admin/assets/activitypub/{content-formatters-nmTOPY9M.js → content-formatters-C2sos5Ht.js} +3 -3
- package/core/built/admin/assets/activitypub/{copy-BCwo9ep3.js → copy-B1UBTOoZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-BEfX-0aW.js → dist-MPsb2WZ1.js} +4 -4
- package/core/built/admin/assets/activitypub/{domain-AaDlZlrA.js → domain-DzcM9Dzo.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-DPi8Tl5M.js → edit-profile-CsQ_4xAk.js} +5 -5
- package/core/built/admin/assets/activitypub/{explore-CxTzjILQ.js → explore-CJe0DZBK.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-DPv5x2MX.js → feed-CKV3qWON.js} +9 -9
- package/core/built/admin/assets/activitypub/{field-DbgA8lat.js → field-DckHfDJf.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-BWuZoYkA.js → hash-Dlp8RM55.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-35VDQ2K2.js → inbox-CsPy4R0M.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DSrF_i1O.js → inbox-DAsjzUUe.js} +10 -10
- package/core/built/admin/assets/activitypub/{label-CPjFBbo7.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-BkDC1XVZ.js → moderation-B0Inc03M.js} +7 -7
- package/core/built/admin/assets/activitypub/{note-xEyH4xT-.js → note-CSE54lER.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-DClaL3ui.js → notifications-RSwbKqdh.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-DpnOa-4Z.js → preferences-CE231n2_.js} +11 -11
- package/core/built/admin/assets/activitypub/{profile-Dvi9mz-6.js → profile-aaQ1j1Eo.js} +13 -13
- package/core/built/admin/assets/activitypub/{reply-Cs9ySd6Z.js → reply-Cjb0Vs3m.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-DFBeTnfn.js → routes-Djb0mc9k.js} +26 -26
- package/core/built/admin/assets/activitypub/{separator-C5u_VjzY.js → separator-D8pROI5n.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-CV2fFu6D.js → settings-DMc32IQl.js} +5 -5
- package/core/built/admin/assets/activitypub/{site-_TgDd_EM.js → site-DVIcsohX.js} +4 -4
- package/core/built/admin/assets/activitypub/{step-1-BfnNL2oF.js → step-1-9Yey9usF.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-DLndZkO-.js → step-2-DdYmKRXV.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-A-PZTtRE.js → step-3-hdSkcjZU.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-BUbz7tea.js → tabs-CUqseYzQ.js} +6 -6
- package/core/built/admin/assets/activitypub/{topic-filter-BdX_xYYT.js → topic-filter-Coni6OXM.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-EwuRemHh.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-cy4ACBc4.js → use-navigate-with-base-path-B046AM38.js} +24 -7
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-CyFcGfan.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-7zk8mV2p.js → x-BRfRYd16.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-DU9ghGLS.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-DFxuQrYu.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-D-3LOrdd.js +1 -0
- package/core/built/admin/assets/analytics-DTIMRhWw.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-DGCcezB6.js +1 -0
- package/core/built/admin/assets/api-keys-Dx4NeNpK.js +1 -0
- package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
- package/core/built/admin/assets/{audience-CkBb2LlO.js → audience-DkknNqhZ.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-cMllU545.js → automations-DvSU7QhW.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-DPGpyD-G.js → bluesky-sharing-yZ9A_jL8.js} +1 -1
- package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
- package/core/built/admin/assets/{breadcrumb-DIfcF5ow.js → breadcrumb-CjOH7LCU.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-DV0FkdcD.js → chart-helpers-DGxvWnBT.js} +1 -1
- package/core/built/admin/assets/checkbox-BSUcnNq_.js +1 -0
- package/core/built/admin/assets/{chunk.332.05991a101d67f732dec3.js → chunk.424.4a27628efdade15c2dab.js} +28 -27
- package/core/built/admin/assets/chunk.524.dce1c51e0bc30fb2718e.js +25 -0
- package/core/built/admin/assets/{chunk.211.6f7328d63c41536451b3.js → chunk.571.40f093ed6d883993714e.js} +5 -5
- package/core/built/admin/assets/chunk.582.3a430d5c6a744fd475b6.js +27 -0
- package/core/built/admin/assets/code-editor-view-BcyKABrG.js +1 -0
- package/core/built/admin/assets/color-picker-field-8NoSnhNI.js +1 -0
- package/core/built/admin/assets/color-utils-Dzeeuj1F.js +1 -0
- package/core/built/admin/assets/combobox-5rdgDrDV.js +1 -0
- package/core/built/admin/assets/command-BOYMbZa_.js +1 -0
- package/core/built/admin/assets/comments-BhNM5CYl.js +1 -0
- package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
- package/core/built/admin/assets/confirmation-modal-CYD7x3UQ.js +1 -0
- package/core/built/admin/assets/content-api-modal-BtW4Z1-1.js +1 -0
- package/core/built/admin/assets/copy-field-CEp_XieW.js +1 -0
- package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-bk3lNEuQ.js +1 -0
- package/core/built/admin/assets/{date-range-select-dXc-GsVe.js → date-range-select-B3Ij1Rqg.js} +1 -1
- package/core/built/admin/assets/debounce-Cx5B_9OI.js +3 -0
- package/core/built/admin/assets/design-and-theme-modal-B2xYKS1I.js +23 -0
- package/core/built/admin/assets/dirty-confirm-dialog-YE9L21gY.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-uIzbWL7K.js +1 -0
- package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-ARN6XTcR.js} +1 -1
- package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
- package/core/built/admin/assets/{dist-DyViJwIy.js → dist-Bg8bSvLA.js} +3 -3
- package/core/built/admin/assets/dist-CsvXCJSe.js +23 -0
- package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
- 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-B5ogXUBa.js → domain-DzrOAPY8.js} +1 -1
- package/core/built/admin/assets/{dropzone-BnFG7dDP.js → dropzone-CzhP33wa.js} +1 -1
- package/core/built/admin/assets/{edit-profile-B07jCZy9.js → edit-profile-oufs4R4n.js} +1 -1
- package/core/built/admin/assets/editor-BBKrunMK.js +8 -0
- package/core/built/admin/assets/embed-signup-form-modal-Ch3s6lqA.js +9 -0
- package/core/built/admin/assets/{explore-Ca1y8HW_.js → explore-CA-R01Br.js} +1 -1
- package/core/built/admin/assets/{feed-x4iFMBP4.js → feed-ClC8_Gld.js} +1 -1
- package/core/built/admin/assets/{field-ChCAabZg.js → field-B3OPenbP.js} +1 -1
- package/core/built/admin/assets/{filter-sources-LdtH1e-T.js → filter-sources-CiPLnG9A.js} +1 -1
- package/core/built/admin/assets/filters-Bz0HsRie.js +1 -0
- package/core/built/admin/assets/{filters-CjVKRP8U.js → filters-yFvybRMe.js} +1 -1
- package/core/built/admin/assets/first-promoter-modal-BC0UpYCD.js +1 -0
- package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
- package/core/built/admin/assets/{ghost-7f01131115186d1af84ead76e79646dd.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +300 -298
- package/core/built/admin/assets/{gift-link-modal-Buqh2vXh.js → gift-link-modal-BKRuqo-k.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-RzW6Bnlf.js +1 -0
- package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
- package/core/built/admin/assets/{growth-DajOBdN0.js → growth-ClNAldfL.js} +1 -1
- package/core/built/admin/assets/{growth-Dy2UOSay.js → growth-Dz8OSG06.js} +2 -2
- package/core/built/admin/assets/history-modal-DIVuQ5Ct.js +1 -0
- package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
- package/core/built/admin/assets/html-field-CVZ2sMxw.js +1 -0
- package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
- package/core/built/admin/assets/images-Ok8FFJ3A.js +1 -0
- package/core/built/admin/assets/{inbox-BUOLDoji.js → inbox-CMUVI6qb.js} +13 -13
- package/core/built/admin/assets/index-DUXM0NlS.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/{input-group-uSK3XT7b.js → input-group-2XU-iuh_.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-DrZ7xxrZ.js +1 -0
- package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
- package/core/built/admin/assets/koenig-editor-base-BPj4S760.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9254 -9251
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
- package/core/built/admin/assets/{koenig-lexical-BFc4yFed.js → koenig-lexical-Cdjv-m3_.js} +98 -98
- package/core/built/admin/assets/{kpi-card-CCt-qeWD.js → kpi-card-J8Mj1sDX.js} +1 -1
- package/core/built/admin/assets/{kpi-card-BN-E6WYX.js → kpi-card-awos0BMH.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-DlxTbTs4.js → kpi-helpers-SEhvH-8E.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BlcUttpn.js → kpi-tabs-R6BjsKhd.js} +1 -1
- package/core/built/admin/assets/{label-AVrxwmsz.js → label-BMC3dzLI.js} +1 -1
- package/core/built/admin/assets/label-picker-Bnlru5tO.js +1 -0
- package/core/built/admin/assets/{labels-DAIqKed7.js → labels-DAPjKPy2.js} +1 -1
- package/core/built/admin/assets/layout-yR9rlvEw.js +1 -0
- package/core/built/admin/assets/limit-modal-COmLevuY.js +1 -0
- package/core/built/admin/assets/{link-helpers-D9LsDOkG.js → link-helpers-Bzkk68GD.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-BvFKEpxJ.js +1 -0
- package/core/built/admin/assets/member-detail-CWdPAe3p.js +1 -0
- package/core/built/admin/assets/members-Bnt0ih2R.js +11 -0
- package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
- package/core/built/admin/assets/{moderation-CQ3urTFE.js → moderation-mPjYHgZW.js} +1 -1
- package/core/built/admin/assets/multi-select-combobox-CsoyHAiX.js +1 -0
- package/core/built/admin/assets/navigation-modal-D_c0badj.js +1 -0
- package/core/built/admin/assets/newsletter-DR49NyGS.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-B45pL1pb.js +1 -0
- package/core/built/admin/assets/newsletter-emails-B-OLfrvr.js +1 -0
- package/core/built/admin/assets/{newsletters-vt3Ni1Ov.js → newsletters-DGs5BaKH.js} +1 -1
- package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
- package/core/built/admin/assets/{note-Dk877BTv.js → note-CSVsUKBm.js} +1 -1
- package/core/built/admin/assets/{notifications-CPlLjPxO.js → notifications-DV9FRtDw.js} +1 -1
- package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-B0hFS8nk.js +1 -0
- package/core/built/admin/assets/{onboarding-route-DdStOtjy.js → onboarding-route--jL5KLPD.js} +1 -1
- package/core/built/admin/assets/overview-B-ZPNqMo.js +1 -0
- package/core/built/admin/assets/overview-D96iwoJi.js +1 -0
- package/core/built/admin/assets/{page-header-pp4dWmOS.js → page-header-DAvDYKO9.js} +1 -1
- package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
- package/core/built/admin/assets/pintura-modal-jiUCxX0Q.js +1 -0
- package/core/built/admin/assets/portal-frame-D4D4nEO6.js +1 -0
- package/core/built/admin/assets/portal-modal-BLFdYeDH.js +1 -0
- package/core/built/admin/assets/{post-analytics-BMJrI81R.js → post-analytics-BhAtiFJp.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-DK27VjJm.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-Btml5vJI.js → post-analytics-provider-DGg0FUqs.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-AcUZkbfL.js → post-share-modal-DtFAC132.js} +1 -1
- package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
- package/core/built/admin/assets/preferences-CuWLISHa.js +5 -0
- package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
- package/core/built/admin/assets/preview-modal-DctYx20m.js +1 -0
- package/core/built/admin/assets/profile-BsKYYtuL.js +1 -0
- package/core/built/admin/assets/radio-group-CI5_Kf_d.js +1 -0
- package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
- package/core/built/admin/assets/{router-provider-BmEZf6c6.js → router-provider-DSQfprxH.js} +1 -1
- package/core/built/admin/assets/select-CkXqNJDJ.js +1 -0
- package/core/built/admin/assets/settings-B4vM-rgV.js +4 -0
- package/core/built/admin/assets/{settings-BGgk7oCI.js → settings-ZCj-W56o.js} +1 -1
- package/core/built/admin/assets/settings-breadcrumbs-DIq31FwA.js +1 -0
- package/core/built/admin/assets/{share-modal-BeuydaBf.js → share-modal-BNj14g79.js} +1 -1
- package/core/built/admin/assets/slack-modal-C6SpMQ-q.js +1 -0
- package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
- package/core/built/admin/assets/{sort-button-Df4t2AL8.js → sort-button-B-k54ow2.js} +1 -1
- package/core/built/admin/assets/{source-icon-ajoaKb41.js → source-icon-Cej3sZM6.js} +1 -1
- package/core/built/admin/assets/{stats-C_lfxZAw.js → stats-B2KW8E2y.js} +1 -1
- package/core/built/admin/assets/{stats-view-Cxot1iD8.js → stats-view-CVzU2-p5.js} +1 -1
- package/core/built/admin/assets/{step-1-ogAA4lDr.js → step-1-Dbn1iIMU.js} +1 -1
- package/core/built/admin/assets/{step-2-C5Xini-7.js → step-2-BahnSuN6.js} +1 -1
- package/core/built/admin/assets/{step-3-CZv1FV81.js → step-3-CW04e2ZJ.js} +2 -2
- package/core/built/admin/assets/stripe-button-BkBZqaGG.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-CYDDrSJm.js +1 -0
- package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
- package/core/built/admin/assets/{table-DFj1wiYv.js → table-BtLcEi1M.js} +1 -1
- package/core/built/admin/assets/tabs-BaWRfg8j.js +1 -0
- package/core/built/admin/assets/tag-detail-CmqiC32L.js +1 -0
- package/core/built/admin/assets/tags-BbygUvNB.js +1 -0
- package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
- package/core/built/admin/assets/testimonials-modal--bdzppyh.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-BZmAsIy4.js +1 -0
- package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
- package/core/built/admin/assets/toggle-group-CVl-Xigr.js +1 -0
- package/core/built/admin/assets/{topic-filter-DGDxDalo.js → topic-filter-BoRHRbL1.js} +1 -1
- package/core/built/admin/assets/transistor-modal-CBPDqEGy.js +1 -0
- package/core/built/admin/assets/unsplash-modal-CixoRAO2.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-C76v4LIp.js → use-analytics-data-BiWvcf1u.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B14lZukR.js +2 -0
- package/core/built/admin/assets/use-feature-flag-BmJoc5WN.js +1 -0
- package/core/built/admin/assets/use-filter-params-ChoV72Hw.js +1 -0
- 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-aNXJOmQr.js → use-growth-stats-Dom8xVzo.js} +1 -1
- package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
- package/core/built/admin/assets/use-limiter-Dwg8ivgQ.js +2 -0
- package/core/built/admin/assets/use-pintura-config-Bj4QmiVk.js +1 -0
- package/core/built/admin/assets/use-pintura-editor-DvrslmPE.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-CG0SZioj.js → use-post-referrers-pxIHTXFs.js} +1 -1
- package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
- package/core/built/admin/assets/{use-reply-chain-data-RLdPKRmS.js → use-reply-chain-data-CmFe0N6F.js} +1 -1
- package/core/built/admin/assets/use-setting-group-Cmp_sy-d.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-BmROPrls.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-DgcF69KI.js → use-simple-pagination-D5rL3YwO.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-C1R_Kbmy.js → use-tinybird-query-DP2dMaq7.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-C9fPjVM_.js → use-tinybird-token-DrK_skPQ.js} +1 -1
- package/core/built/admin/assets/use-url-input-ChrUZBFf.js +5 -0
- package/core/built/admin/assets/user-detail-modal-XODpOqNC.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-BmTUzeoF.js → virtual-list-Dnoafqbk.js} +1 -1
- package/core/built/admin/assets/web-D71mjB2r.js +1 -0
- package/core/built/admin/assets/web-z71lA1Y3.js +2 -0
- package/core/built/admin/assets/zapier-modal-Bp507Um7.js +1 -0
- package/core/built/admin/index.html +16 -14
- 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/proxy.js +1 -1
- package/core/frontend/services/routing/events.js +20 -6
- package/core/frontend/services/routing/permalink-adapter.js +0 -13
- package/core/frontend/services/routing/permalink-adapter.ts +0 -13
- package/core/frontend/services/routing/router-manager.js +15 -9
- 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/frontend/web/middleware/admin-toolbar.js +1 -1
- package/core/frontend/web/middleware/serve-indexnow-key.js +1 -7
- 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/index.js +2 -2
- 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/themes.js +6 -1
- 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.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/schema.js +24 -5
- package/core/server/lib/common/to-plain.ts +1 -1
- package/core/server/models/base/plugins/overrides.js +7 -5
- package/core/server/models/post.js +2 -2
- 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 +28 -0
- package/core/server/services/adapter-manager/adapter-paths.ts +26 -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 +11 -2
- package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
- package/core/server/services/audience-feedback/feedback-repository.js +4 -1
- package/core/server/services/auth/session/session-service.js +18 -3
- package/core/server/services/automations/database-automations-repository.js +5 -5
- package/core/server/services/automations/database-automations-repository.ts +12 -12
- package/core/server/services/automations/database-date.js +52 -3
- package/core/server/services/automations/database-date.ts +23 -2
- package/core/server/services/automations/service.js +2 -2
- package/core/server/services/automations/service.ts +2 -2
- 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 +12 -3
- package/core/server/services/email-analytics/email-analytics-service.js +4 -3
- package/core/server/services/email-analytics/index.js +2 -3
- package/core/server/services/email-analytics/index.ts +1 -2
- package/core/server/services/email-analytics/lib/queries.js +91 -124
- package/core/server/services/email-analytics/lib/queries.ts +341 -0
- 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/indexnow-ping/index.js +0 -2
- package/core/server/services/indexnow-ping/index.ts +0 -2
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +6 -8
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +9 -17
- package/core/server/services/lib/mailgun-client.js +3 -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/service.js +72 -12
- package/core/server/services/members/api.js +1 -1
- 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 +26 -6
- package/core/server/services/members/import-export/import/row.ts +5 -1
- package/core/server/services/members/import-export/index.js +1 -5
- package/core/server/services/members/import-export/index.ts +2 -6
- package/core/server/services/members/members-api/repositories/member-repository.js +51 -0
- 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 +18 -27
- package/core/server/services/members-custom-fields/schema.ts +17 -27
- 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 -219
- package/core/server/services/members-custom-fields/values-service.ts +110 -238
- package/core/server/services/mentions/resource-service.js +1 -1
- package/core/server/services/mentions/service.js +2 -2
- package/core/server/services/route-settings/dynamic-routing-service.js +9 -59
- package/core/server/services/slack-notifications/slack-notifications.js +2 -2
- package/core/server/services/slack-ping/index.js +1 -1
- package/core/server/services/slack-ping/index.ts +1 -1
- package/core/server/services/slack-ping/slack-ping-service.js +3 -2
- package/core/server/services/slack-ping/slack-ping-service.ts +4 -5
- package/core/server/services/stats/content-stats-service.js +3 -3
- package/core/server/services/stats/members-stats-service.js +0 -7
- package/core/server/services/stats/posts-stats-service.js +2 -2
- package/core/server/services/themes/storage.js +19 -2
- 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 +12 -2
- package/core/server/web/api/endpoints/admin/routes.js +2 -2
- package/core/shared/config/env/config.testing-mysql.json +1 -2
- package/core/shared/config/env/config.testing.json +1 -2
- package/core/shared/custom-theme-settings-cache/custom-theme-settings-bread-service.js +4 -0
- package/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js +39 -0
- package/core/shared/events/index.js +1 -2
- package/core/shared/labs.js +4 -4
- package/package.json +10 -11
- package/pnpm-lock.yaml +44 -47
- package/pnpm-workspace.yaml +19 -19
- package/tsconfig.json +1 -0
- package/components/tryghost-adapter-base-scheduling-0.2.0.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.3.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/core/built/admin/assets/Combination-BCm9FL4o.js +0 -41
- package/core/built/admin/assets/activitypub/layout-CTgrezN7.js +0 -1
- package/core/built/admin/assets/automated-emails-6t2bIRWf.js +0 -7
- package/core/built/admin/assets/checkbox-BIl077a0.js +0 -1
- package/core/built/admin/assets/chunk.524.87b52f4fada529df2c4c.js +0 -25
- package/core/built/admin/assets/chunk.582.340bcca873e806cbd4bb.js +0 -27
- package/core/built/admin/assets/code-editor-view-C0aWNwdl.js +0 -1
- package/core/built/admin/assets/comments-qwhIjH3c.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-DYAXW-6V.js +0 -1
- package/core/built/admin/assets/dist-BUKmD51u.js +0 -1
- package/core/built/admin/assets/dist-CNlMj7ML.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/editor-DDYSOXGN.js +0 -8
- package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
- package/core/built/admin/assets/filters-CLehPo_D.js +0 -1
- package/core/built/admin/assets/index-08n05-rc.js +0 -67
- package/core/built/admin/assets/index-DgUzgFvS.css +0 -1
- package/core/built/admin/assets/label-picker-DVnpu_IF.js +0 -1
- package/core/built/admin/assets/layout-B0YLh7R9.js +0 -1
- package/core/built/admin/assets/member-custom-fields-C8NpZYza.js +0 -1
- package/core/built/admin/assets/member-detail-CIVAdKXf.js +0 -1
- package/core/built/admin/assets/members-oLPiSkXD.js +0 -10
- package/core/built/admin/assets/modals-CB7v-k3T.js +0 -31
- package/core/built/admin/assets/newsletter-rbeDX9Wk.js +0 -1
- package/core/built/admin/assets/overview--4V-kx9c.js +0 -1
- package/core/built/admin/assets/overview-DSLv30oz.js +0 -1
- package/core/built/admin/assets/post-analytics-header-pU2m9ZuV.js +0 -1
- package/core/built/admin/assets/preferences-Ru4CRPHC.js +0 -5
- package/core/built/admin/assets/profile-BNb1oj7D.js +0 -1
- package/core/built/admin/assets/select-B0HteHrp.js +0 -1
- package/core/built/admin/assets/settings-B1C9Q3wo.js +0 -4
- package/core/built/admin/assets/social-urls-DA2lXrhW.js +0 -8
- package/core/built/admin/assets/switch-CyQcwMAv.js +0 -1
- package/core/built/admin/assets/tabs-D4y1gqUd.js +0 -1
- package/core/built/admin/assets/tags-Dmft-Y4O.js +0 -1
- package/core/built/admin/assets/toggle-group-R2QMFvXW.js +0 -1
- package/core/built/admin/assets/use-filter-params-WrdhAISv.js +0 -1
- package/core/built/admin/assets/web-BqW0b0_C.js +0 -1
- package/core/built/admin/assets/web-Cy5GKMWM.js +0 -2
- package/core/server/services/email-analytics/jobs/update-member-email-analytics/index.js +0 -14
- 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
- /package/core/built/admin/assets/{chunk.211.6f7328d63c41536451b3.js.LICENSE.txt → chunk.571.40f093ed6d883993714e.js.LICENSE.txt} +0 -0
- /package/core/built/admin/assets/{dist-CqO2lWS0.js → dist-CqO2lWS02.js} +0 -0
- /package/core/server/api/endpoints/{gift-reminders.js → gifts.js} +0 -0
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
const pad = (value: number, length = 2): string => String(value).padStart(length, '0');
|
|
4
|
+
|
|
5
|
+
const timestamp = (date: Date): string => [
|
|
6
|
+
pad(date.getFullYear(), 4),
|
|
7
|
+
pad(date.getMonth() + 1),
|
|
8
|
+
pad(date.getDate()),
|
|
9
|
+
pad(date.getHours()),
|
|
10
|
+
pad(date.getMinutes()),
|
|
11
|
+
pad(date.getSeconds())
|
|
12
|
+
].join('-');
|
|
3
13
|
|
|
4
14
|
export const getBackupRouteSettingsFilePath = (filePath: string): string => {
|
|
5
15
|
const {dir, name, ext} = path.parse(filePath);
|
|
6
|
-
return path.join(dir, `${name}-${
|
|
16
|
+
return path.join(dir, `${name}-${timestamp(new Date())}${ext}`);
|
|
7
17
|
};
|
|
@@ -36,7 +36,7 @@ const controller = {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
async query(frame) {
|
|
39
|
-
urlSerializerUtils.
|
|
39
|
+
urlSerializerUtils.forceUrlRelations(frame, 'posts');
|
|
40
40
|
|
|
41
41
|
const model = await models.Post.findOne(Object.assign(frame.data, {status: 'sent'}), frame.options);
|
|
42
42
|
|
|
@@ -316,8 +316,8 @@ module.exports = {
|
|
|
316
316
|
return apiFramework.pipeline(require('./gift-links'), localUtils);
|
|
317
317
|
},
|
|
318
318
|
|
|
319
|
-
get
|
|
320
|
-
return apiFramework.pipeline(require('./
|
|
319
|
+
get gifts() {
|
|
320
|
+
return apiFramework.pipeline(require('./gifts'), localUtils);
|
|
321
321
|
},
|
|
322
322
|
|
|
323
323
|
get recommendationsPublic() {
|
|
@@ -82,7 +82,7 @@ const controller = {
|
|
|
82
82
|
await _addMemberContextToFrame(frame);
|
|
83
83
|
|
|
84
84
|
// previews has no input serializer, so the URL force-load happens here
|
|
85
|
-
urlSerializerUtils.
|
|
85
|
+
urlSerializerUtils.forceUrlRelations(frame, 'posts');
|
|
86
86
|
|
|
87
87
|
const model = await models.Post.findOne(Object.assign({status: 'all'}, frame.data), frame.options);
|
|
88
88
|
if (!model) {
|
|
@@ -4,8 +4,8 @@ const {requiredUrlColumns} = require('./utils/serializers/input/utils/url');
|
|
|
4
4
|
const getPostServiceInstance = require('../../services/posts/posts-service-instance');
|
|
5
5
|
const postsService = getPostServiceInstance();
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const withRelated = urlService.
|
|
7
|
+
const urlRelationsForRouting = () => {
|
|
8
|
+
const withRelated = urlService.getRequiredRelations();
|
|
9
9
|
return withRelated.length ? {withRelated} : {};
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -23,7 +23,7 @@ const controller = {
|
|
|
23
23
|
limit: '10000',
|
|
24
24
|
order: 'updated_at DESC',
|
|
25
25
|
columns: requiredUrlColumns('posts', ['id', 'slug', 'title', 'excerpt', 'url', 'updated_at', 'visibility']),
|
|
26
|
-
...
|
|
26
|
+
...urlRelationsForRouting()
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
return postsService.browsePosts(options);
|
|
@@ -4,8 +4,8 @@ const {requiredUrlColumns} = require('./utils/serializers/input/utils/url');
|
|
|
4
4
|
const getPostServiceInstance = require('../../services/posts/posts-service-instance');
|
|
5
5
|
const postsService = getPostServiceInstance();
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const withRelated = urlService.
|
|
7
|
+
const urlRelationsForRouting = () => {
|
|
8
|
+
const withRelated = urlService.getRequiredRelations();
|
|
9
9
|
return withRelated.length ? {withRelated} : {};
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -26,7 +26,7 @@ const controller = {
|
|
|
26
26
|
limit: '10000',
|
|
27
27
|
order: 'updated_at DESC',
|
|
28
28
|
columns: requiredUrlColumns('posts', ['id', 'uuid', 'url', 'title', 'slug', 'status', 'published_at', 'visibility']),
|
|
29
|
-
...
|
|
29
|
+
...urlRelationsForRouting()
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
return postsService.browsePosts(options);
|
|
@@ -46,7 +46,7 @@ const controller = {
|
|
|
46
46
|
limit: '10000',
|
|
47
47
|
order: 'updated_at DESC',
|
|
48
48
|
columns: requiredUrlColumns('pages', ['id', 'uuid', 'url', 'title', 'slug', 'status', 'published_at', 'visibility']),
|
|
49
|
-
...
|
|
49
|
+
...urlRelationsForRouting()
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
return postsService.browsePosts(options);
|
|
@@ -106,6 +106,9 @@ const controller = {
|
|
|
106
106
|
headers: {
|
|
107
107
|
cacheInvalidate: false
|
|
108
108
|
},
|
|
109
|
+
options: [
|
|
110
|
+
'copy_settings_from'
|
|
111
|
+
],
|
|
109
112
|
permissions: {
|
|
110
113
|
method: 'add'
|
|
111
114
|
},
|
|
@@ -123,7 +126,9 @@ const controller = {
|
|
|
123
126
|
name: frame.file.originalname
|
|
124
127
|
};
|
|
125
128
|
|
|
126
|
-
const {theme, themeOverridden} = await themeService.api.setFromZip(zip
|
|
129
|
+
const {theme, themeOverridden} = await themeService.api.setFromZip(zip, {
|
|
130
|
+
copySettingsFrom: frame.options.copy_settings_from
|
|
131
|
+
});
|
|
127
132
|
if (themeOverridden) {
|
|
128
133
|
frame.setHeader('X-Cache-Invalidate', '/*');
|
|
129
134
|
}
|
|
@@ -17,7 +17,7 @@ module.exports = {
|
|
|
17
17
|
browse(apiConfig, frame) {
|
|
18
18
|
debug('browse');
|
|
19
19
|
|
|
20
|
-
url.
|
|
20
|
+
url.forceUrlColumns(frame, 'authors');
|
|
21
21
|
|
|
22
22
|
if (utils.isContentAPI(frame)) {
|
|
23
23
|
setDefaultOrder(frame);
|
|
@@ -27,7 +27,7 @@ module.exports = {
|
|
|
27
27
|
read(apiConfig, frame) {
|
|
28
28
|
debug('read');
|
|
29
29
|
|
|
30
|
-
url.
|
|
30
|
+
url.forceUrlColumns(frame, 'authors');
|
|
31
31
|
|
|
32
32
|
if (utils.isContentAPI(frame)) {
|
|
33
33
|
setDefaultOrder(frame);
|
|
@@ -24,7 +24,7 @@ module.exports = {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
if (frame.options.withRelated.includes('post')) {
|
|
27
|
-
for (const relation of urlService.
|
|
27
|
+
for (const relation of urlService.getRequiredRelations()) {
|
|
28
28
|
const prefixed = `post.${relation}`;
|
|
29
29
|
if (!frame.options.withRelated.includes(prefixed)) {
|
|
30
30
|
frame.options.withRelated.push(prefixed);
|
|
@@ -56,7 +56,7 @@ function defaultRelations(frame) {
|
|
|
56
56
|
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions'];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
url.
|
|
59
|
+
url.forceUrlRelations(frame, 'pages');
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function setDefaultOrder(frame) {
|
|
@@ -126,7 +126,7 @@ module.exports = {
|
|
|
126
126
|
|
|
127
127
|
setDefaultOrder(frame);
|
|
128
128
|
forceVisibilityColumn(frame);
|
|
129
|
-
url.
|
|
129
|
+
url.forceUrlRelations(frame, 'pages');
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
if (!localUtils.isContentAPI(frame)) {
|
|
@@ -145,7 +145,7 @@ module.exports = {
|
|
|
145
145
|
removeSourceFormats(frame);
|
|
146
146
|
setDefaultOrder(frame);
|
|
147
147
|
forceVisibilityColumn(frame);
|
|
148
|
-
url.
|
|
148
|
+
url.forceUrlRelations(frame, 'pages');
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
if (!localUtils.isContentAPI(frame)) {
|
|
@@ -82,7 +82,7 @@ function defaultRelations(frame) {
|
|
|
82
82
|
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.clicks'];
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
url.
|
|
85
|
+
url.forceUrlRelations(frame, 'posts');
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function setDefaultOrder(frame) {
|
|
@@ -161,7 +161,7 @@ module.exports = {
|
|
|
161
161
|
setDefaultOrder(frame);
|
|
162
162
|
forceVisibilityColumn(frame);
|
|
163
163
|
mapWithRelated(frame);
|
|
164
|
-
url.
|
|
164
|
+
url.forceUrlRelations(frame, 'posts');
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
if (!localUtils.isContentAPI(frame)) {
|
|
@@ -188,7 +188,7 @@ module.exports = {
|
|
|
188
188
|
|
|
189
189
|
setDefaultOrder(frame);
|
|
190
190
|
forceVisibilityColumn(frame);
|
|
191
|
-
url.
|
|
191
|
+
url.forceUrlRelations(frame, 'posts');
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
if (!localUtils.isContentAPI(frame)) {
|
|
@@ -6,13 +6,13 @@ module.exports = {
|
|
|
6
6
|
debug('browse');
|
|
7
7
|
|
|
8
8
|
// Staff users route through the authors router types.
|
|
9
|
-
url.
|
|
9
|
+
url.forceUrlColumns(frame, 'authors');
|
|
10
10
|
},
|
|
11
11
|
|
|
12
12
|
read(apiConfig, frame) {
|
|
13
13
|
debug('read');
|
|
14
14
|
|
|
15
|
-
url.
|
|
15
|
+
url.forceUrlColumns(frame, 'authors');
|
|
16
16
|
|
|
17
17
|
if (frame.data.id === 'me' && frame.options.context && frame.options.context.user) {
|
|
18
18
|
frame.data.id = frame.options.context.user;
|
|
@@ -71,28 +71,41 @@ const forSetting = (attrs) => {
|
|
|
71
71
|
// build a URL (e.g. status/visibility/slug), so the service would reject the
|
|
72
72
|
// resource as thin. Force them back into the fetch and record them on the
|
|
73
73
|
// frame — the output mapper strips them from the response after the URL is
|
|
74
|
-
// built. No-op
|
|
75
|
-
const
|
|
74
|
+
// built. No-op when the routing config needs no extra columns.
|
|
75
|
+
const forceUrlColumns = (frame, routerType, extraColumns = []) => {
|
|
76
76
|
if (!Array.isArray(frame.options.columns) || !frame.options.columns.includes('url')) {
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
-
const
|
|
79
|
+
const required = new Set([...urlService.getRequiredFields(routerType), ...extraColumns]);
|
|
80
|
+
const missing = [...required].filter(field => !frame.options.columns.includes(field));
|
|
81
|
+
if (!missing.length) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// A read already carries the fields it was looked up by: `findOne` forges
|
|
85
|
+
// the model with them before the fetch, so they survive a narrowed
|
|
86
|
+
// `?fields=` list and the caller is served them today. Stripping those back
|
|
87
|
+
// out would take a field away — `posts/slug/:slug/?fields=url` still
|
|
88
|
+
// answers with its slug. They are still selected, because the lookup
|
|
89
|
+
// matches case-insensitively: the forged value can differ in case from the
|
|
90
|
+
// stored one, and the URL is built from whichever the model carries.
|
|
91
|
+
const carried = Object.keys(frame.data || {});
|
|
92
|
+
const forced = missing.filter(field => !carried.includes(field));
|
|
80
93
|
if (forced.length) {
|
|
81
|
-
frame.forcedUrlColumns = forced;
|
|
82
|
-
frame.options.columns.push(...forced);
|
|
94
|
+
frame.forcedUrlColumns = {routerType, columns: forced};
|
|
83
95
|
}
|
|
96
|
+
frame.options.columns.push(...missing);
|
|
84
97
|
};
|
|
85
98
|
|
|
86
99
|
// `url` is serialized for every post/page unless `?fields` narrows it away,
|
|
87
100
|
// so the relations the lazy URL service reads (e.g. tags for a tag-filtered
|
|
88
101
|
// collection) must be loaded whenever the URL will be built — not only for
|
|
89
102
|
// the `?fields=url` case. Forced relations are recorded on the frame for the
|
|
90
|
-
// output mapper to strip. No-op
|
|
91
|
-
const
|
|
103
|
+
// output mapper to strip. No-op when the routing config reads no relations.
|
|
104
|
+
const forceUrlRelations = (frame, routerType) => {
|
|
92
105
|
if (!localUtils.willSerializeUrl(frame)) {
|
|
93
106
|
return;
|
|
94
107
|
}
|
|
95
|
-
const relations = urlService.
|
|
108
|
+
const relations = urlService.getRequiredRelations();
|
|
96
109
|
if (relations.length) {
|
|
97
110
|
const requested = frame.options.withRelated || [];
|
|
98
111
|
// a nested include covers its parent: `authors.roles` loads authors
|
|
@@ -105,13 +118,18 @@ const forceUrlRelationsWhenLazy = (frame, routerType) => {
|
|
|
105
118
|
frame.options.withRelated = [...requested, ...forced];
|
|
106
119
|
}
|
|
107
120
|
}
|
|
108
|
-
|
|
121
|
+
// The URL is looked up by `model.id`, and Bookshelf matches eager-loaded
|
|
122
|
+
// rows back to their parent by the same key — so a `?fields=` list that
|
|
123
|
+
// omits it serializes /404/ and leaves the relations above unattached,
|
|
124
|
+
// silently and without a key on the model at all. A read looked up by id
|
|
125
|
+
// is covered by the carried-fields rule above.
|
|
126
|
+
forceUrlColumns(frame, routerType, ['id']);
|
|
109
127
|
};
|
|
110
128
|
|
|
111
|
-
// Options-object variant of
|
|
129
|
+
// Options-object variant of forceUrlColumns for endpoints that build
|
|
112
130
|
// their query options directly (search index).
|
|
113
131
|
const requiredUrlColumns = (routerType, columns) => {
|
|
114
|
-
const required = urlService.
|
|
132
|
+
const required = urlService.getRequiredFields(routerType).filter(field => !columns.includes(field));
|
|
115
133
|
return required.length ? [...columns, ...required] : columns;
|
|
116
134
|
};
|
|
117
135
|
|
|
@@ -120,5 +138,5 @@ module.exports.requiredUrlColumns = requiredUrlColumns;
|
|
|
120
138
|
module.exports.forUser = forUser;
|
|
121
139
|
module.exports.forTag = forTag;
|
|
122
140
|
module.exports.forSetting = forSetting;
|
|
123
|
-
module.exports.
|
|
124
|
-
module.exports.
|
|
141
|
+
module.exports.forceUrlColumns = forceUrlColumns;
|
|
142
|
+
module.exports.forceUrlRelations = forceUrlRelations;
|
|
@@ -136,8 +136,8 @@ module.exports = async (model, frame, options = {}) => {
|
|
|
136
136
|
clean.post(jsonModel, frame);
|
|
137
137
|
|
|
138
138
|
// Columns force-loaded for the URL computation, not requested by the caller.
|
|
139
|
-
if (frame.forcedUrlColumns) {
|
|
140
|
-
frame.forcedUrlColumns.forEach((column) => {
|
|
139
|
+
if (frame.forcedUrlColumns && frame.forcedUrlColumns.routerType === routerType) {
|
|
140
|
+
frame.forcedUrlColumns.columns.forEach((column) => {
|
|
141
141
|
delete jsonModel[column];
|
|
142
142
|
});
|
|
143
143
|
}
|
|
@@ -8,8 +8,8 @@ module.exports = (model, frame) => {
|
|
|
8
8
|
clean.tag(jsonModel, frame);
|
|
9
9
|
|
|
10
10
|
// Columns force-loaded for the URL computation, not requested by the caller.
|
|
11
|
-
if (frame.forcedUrlColumns) {
|
|
12
|
-
frame.forcedUrlColumns.forEach((column) => {
|
|
11
|
+
if (frame.forcedUrlColumns && frame.forcedUrlColumns.routerType === 'tags') {
|
|
12
|
+
frame.forcedUrlColumns.columns.forEach((column) => {
|
|
13
13
|
delete jsonModel[column];
|
|
14
14
|
});
|
|
15
15
|
}
|
|
@@ -6,6 +6,14 @@ module.exports = (model, frame) => {
|
|
|
6
6
|
|
|
7
7
|
url.forUser(model.id, jsonModel, frame.options);
|
|
8
8
|
|
|
9
|
+
// Force-loaded for the URL computation, not requested by the caller (the
|
|
10
|
+
// author permalink substitutes :slug, so `?fields=url` pulls it in).
|
|
11
|
+
if (frame.forcedUrlColumns && frame.forcedUrlColumns.routerType === 'authors') {
|
|
12
|
+
frame.forcedUrlColumns.columns.forEach((column) => {
|
|
13
|
+
delete jsonModel[column];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
clean.author(jsonModel, frame);
|
|
10
18
|
|
|
11
19
|
return jsonModel;
|
|
@@ -10,8 +10,9 @@ const forPost = (id, attrs, frame, type = 'posts') => {
|
|
|
10
10
|
// type from `attrs` would silently fall back to 'posts' for pages.
|
|
11
11
|
// The mapper that owns the resource always knows which it is.
|
|
12
12
|
//
|
|
13
|
-
// `id` is passed separately for the same reason:
|
|
14
|
-
//
|
|
13
|
+
// `id` is passed separately for the same reason: `?fields=url` strips it
|
|
14
|
+
// from `attrs`, and the URL service needs it for `:id` permalinks and to
|
|
15
|
+
// name the resource when it reports a problem.
|
|
15
16
|
//
|
|
16
17
|
// When `url` was not requested (`?fields=id,title`), don't compute it at
|
|
17
18
|
// all — attrs is stripped to the requested columns, so the resource would
|
|
@@ -24,17 +25,23 @@ const forPost = (id, attrs, frame, type = 'posts') => {
|
|
|
24
25
|
return attrs;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// for non-api callers (mentions, email) whose frame has no endpoint fields.
|
|
28
|
+
// Names the fetch that produced this resource. It matters on the degrade
|
|
29
|
+
// path: a thin resource is answered with a silent /404/, and the report is
|
|
30
|
+
// the only way back to the endpoint that under-fetched it. Ignored by URL
|
|
31
|
+
// generation itself; read only into that report.
|
|
32
32
|
const options = {absolute: true};
|
|
33
33
|
if (frame && (frame.docName || frame.method)) {
|
|
34
|
-
options.serializerContext = {
|
|
34
|
+
options.serializerContext = {
|
|
35
|
+
apiType: frame.apiType,
|
|
36
|
+
docName: frame.docName,
|
|
37
|
+
method: frame.method,
|
|
38
|
+
withRelated: frame.options && frame.options.withRelated,
|
|
39
|
+
columns: frame.options && frame.options.columns,
|
|
40
|
+
forcedUrlRelations: frame.forcedUrlRelations
|
|
41
|
+
};
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
attrs.url = urlService.
|
|
44
|
+
attrs.url = urlService.getUrlForResource({...attrs, id, type}, options);
|
|
38
45
|
|
|
39
46
|
/**
|
|
40
47
|
* CASE: admin api should serve preview urls
|
|
@@ -71,7 +78,7 @@ const forPost = (id, attrs, frame, type = 'posts') => {
|
|
|
71
78
|
|
|
72
79
|
const forUser = (id, attrs, options) => {
|
|
73
80
|
if (!options.columns || (options.columns && options.columns.includes('url'))) {
|
|
74
|
-
attrs.url = urlService.
|
|
81
|
+
attrs.url = urlService.getUrlForResource({...attrs, id, type: 'authors'}, {absolute: true});
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
return attrs;
|
|
@@ -79,7 +86,7 @@ const forUser = (id, attrs, options) => {
|
|
|
79
86
|
|
|
80
87
|
const forTag = (id, attrs, options) => {
|
|
81
88
|
if (!options.columns || (options.columns && options.columns.includes('url'))) {
|
|
82
|
-
attrs.url = urlService.
|
|
89
|
+
attrs.url = urlService.getUrlForResource({...attrs, id, type: 'tags'}, {absolute: true});
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
return attrs;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createNonTransactionalMigration} = require('../../utils');
|
|
3
|
+
const {addColumn, dropColumn, addUnique, dropUnique, addIndex, dropIndex} = require('../../../schema/commands');
|
|
4
|
+
|
|
5
|
+
const TABLE = 'members_custom_field_values';
|
|
6
|
+
|
|
7
|
+
// Named explicitly; the derived name would overrun MySQL's identifier limit, which
|
|
8
|
+
// schema.test.js checks for every table.
|
|
9
|
+
const LEAF_UNIQUE = 'members_custom_field_values_leaf_unique';
|
|
10
|
+
|
|
11
|
+
// Values are discarded rather than converted: custom fields sit behind a private flag and
|
|
12
|
+
// have never been released, so only a site deliberately opted in can hold one.
|
|
13
|
+
async function discardStoredValues(knex) {
|
|
14
|
+
const discarded = await knex(TABLE).del();
|
|
15
|
+
if (discarded > 0) {
|
|
16
|
+
logging.warn(`Discarded ${discarded} custom field value(s): the storage format changed before the feature was released`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Column changes ask for `auto` so MySQL picks an in-place algorithm where it can; the
|
|
21
|
+
// helpers otherwise default to a full table copy, which blocks writes.
|
|
22
|
+
//
|
|
23
|
+
// Two constraints shape the order of everything below.
|
|
24
|
+
//
|
|
25
|
+
// MySQL refuses to drop the last index serving a foreign key, counting any index whose
|
|
26
|
+
// leftmost column is the referencing one, so each replacement is added before the thing
|
|
27
|
+
// it replaces is dropped.
|
|
28
|
+
//
|
|
29
|
+
// This migration is not transactional, so a crash leaves the table half-changed with
|
|
30
|
+
// nothing recorded as done and knex-migrator runs it again on the next boot. Every step
|
|
31
|
+
// therefore tolerates having already happened: the constraint and index helpers swallow
|
|
32
|
+
// that themselves, the column steps ask first.
|
|
33
|
+
module.exports = createNonTransactionalMigration(
|
|
34
|
+
async function up(knex) {
|
|
35
|
+
// First, so nothing can collide with the new constraint.
|
|
36
|
+
await discardStoredValues(knex);
|
|
37
|
+
|
|
38
|
+
if (!await knex.schema.hasColumn(TABLE, 'path')) {
|
|
39
|
+
await addColumn(TABLE, 'path', knex, undefined, {algorithm: 'auto'});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// `member_id` stays leftmost, covering that foreign key the moment this exists.
|
|
43
|
+
await addUnique(TABLE, ['member_id', 'custom_field_id', 'path'], knex, LEAF_UNIQUE);
|
|
44
|
+
await dropUnique(TABLE, ['member_id', 'custom_field_id'], knex);
|
|
45
|
+
|
|
46
|
+
if (await knex.schema.hasColumn(TABLE, 'value_json')) {
|
|
47
|
+
await dropColumn(TABLE, 'value_json', knex, {}, {algorithm: 'auto'});
|
|
48
|
+
}
|
|
49
|
+
await addIndex(TABLE, ['custom_field_id', 'path'], knex);
|
|
50
|
+
|
|
51
|
+
// Only present after a rollback, where `down` adds it to keep a foreign key
|
|
52
|
+
// covered. Leaving it would be drift against a fresh install.
|
|
53
|
+
await dropIndex(TABLE, ['custom_field_id'], knex);
|
|
54
|
+
},
|
|
55
|
+
async function down(knex) {
|
|
56
|
+
// Rebuilding a composite would need the field-type catalog, which a migration must
|
|
57
|
+
// not read: it has to keep working when the catalog moves on.
|
|
58
|
+
await discardStoredValues(knex);
|
|
59
|
+
|
|
60
|
+
await addUnique(TABLE, ['member_id', 'custom_field_id'], knex);
|
|
61
|
+
await dropUnique(TABLE, ['member_id', 'custom_field_id', 'path'], knex, LEAF_UNIQUE);
|
|
62
|
+
|
|
63
|
+
// Back before the composite one goes, or MySQL has nothing covering that key.
|
|
64
|
+
await addIndex(TABLE, ['custom_field_id'], knex);
|
|
65
|
+
await dropIndex(TABLE, ['custom_field_id', 'path'], knex);
|
|
66
|
+
|
|
67
|
+
if (!await knex.schema.hasColumn(TABLE, 'value_json')) {
|
|
68
|
+
await addColumn(TABLE, 'value_json', knex, {type: 'text', maxlength: 65535, nullable: true}, {algorithm: 'auto'});
|
|
69
|
+
}
|
|
70
|
+
if (await knex.schema.hasColumn(TABLE, 'path')) {
|
|
71
|
+
await dropColumn(TABLE, 'path', knex, {}, {algorithm: 'auto'});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createTransactionalMigration} = require('../../utils');
|
|
3
|
+
|
|
4
|
+
module.exports = createTransactionalMigration(
|
|
5
|
+
async function up(knex) {
|
|
6
|
+
logging.info('Resetting automation email analytics counts');
|
|
7
|
+
|
|
8
|
+
const updatedRevisions = await knex('automation_action_revisions').update({
|
|
9
|
+
email_sent_count: null,
|
|
10
|
+
email_opened_count: null,
|
|
11
|
+
email_clicked_count: null
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
logging.info(`Reset automation email analytics counts for ${updatedRevisions} action revisions`);
|
|
15
|
+
|
|
16
|
+
const deletedRecipients = await knex('automated_email_recipients')
|
|
17
|
+
.whereNotNull('automation_action_revision_id')
|
|
18
|
+
.del();
|
|
19
|
+
|
|
20
|
+
logging.info(`Deleted ${deletedRecipients} automation email recipient rows`);
|
|
21
|
+
},
|
|
22
|
+
async function down() {
|
|
23
|
+
logging.info('Skipping rollback for automation email analytics reset');
|
|
24
|
+
}
|
|
25
|
+
);
|
|
@@ -276,13 +276,18 @@ async function dropIndex(tableName, columns, transaction = db.knex) {
|
|
|
276
276
|
* @param {string} tableName - name of the table to add unique constraint to
|
|
277
277
|
* @param {string|string[]} columns - column(s) to form unique constraint with
|
|
278
278
|
* @param {import('knex').Knex} [transaction] - connection object containing knex reference
|
|
279
|
+
* @param {string} [indexName] - name for the constraint; knex derives one from the table
|
|
280
|
+
* and every column when omitted, which can overrun MySQL's 64-character limit
|
|
279
281
|
*/
|
|
280
|
-
async function addUnique(tableName, columns, transaction = db.knex) {
|
|
282
|
+
async function addUnique(tableName, columns, transaction = db.knex, indexName) {
|
|
281
283
|
try {
|
|
282
284
|
logging.info(`Adding unique constraint for '${columns}' in table '${tableName}'`);
|
|
283
285
|
|
|
284
286
|
return await transaction.schema.table(tableName, function (table) {
|
|
285
|
-
table
|
|
287
|
+
// Knex derives a name from the table and every column when it is not given
|
|
288
|
+
// one, and that can overrun MySQL's 64-character identifier limit on a wide
|
|
289
|
+
// table or a constraint over several columns.
|
|
290
|
+
table.unique(columns, indexName ? {indexName} : undefined);
|
|
286
291
|
});
|
|
287
292
|
} catch (err) {
|
|
288
293
|
if (err.code === 'SQLITE_ERROR') {
|
|
@@ -304,12 +309,14 @@ async function addUnique(tableName, columns, transaction = db.knex) {
|
|
|
304
309
|
* @param {string|string[]} columns - column(s) unique constraint was formed
|
|
305
310
|
* @param {import('knex').Knex} transaction - connection object containing knex reference
|
|
306
311
|
*/
|
|
307
|
-
async function dropUnique(tableName, columns, transaction = db.knex) {
|
|
312
|
+
async function dropUnique(tableName, columns, transaction = db.knex, indexName) {
|
|
308
313
|
try {
|
|
309
314
|
logging.info(`Dropping unique constraint for '${columns}' in table '${tableName}'`);
|
|
310
315
|
|
|
311
316
|
return await transaction.schema.table(tableName, function (table) {
|
|
312
|
-
|
|
317
|
+
// Named constraints have to be dropped by that name: the one knex would
|
|
318
|
+
// derive from the columns is not what is on the table.
|
|
319
|
+
table.dropUnique(columns, indexName);
|
|
313
320
|
});
|
|
314
321
|
} catch (err) {
|
|
315
322
|
if (err.code === 'SQLITE_ERROR') {
|
|
@@ -542,7 +549,16 @@ function createTable(table, transaction = db.knex, tableSpec = schema[table]) {
|
|
|
542
549
|
});
|
|
543
550
|
}
|
|
544
551
|
if (tableSpec['@@UNIQUE_CONSTRAINTS@@']) {
|
|
545
|
-
tableSpec['@@UNIQUE_CONSTRAINTS@@'].forEach(unique =>
|
|
552
|
+
tableSpec['@@UNIQUE_CONSTRAINTS@@'].forEach((unique) => {
|
|
553
|
+
// An entry is normally the columns alone, and knex names the constraint
|
|
554
|
+
// after the table and every one of them. The object form is for when
|
|
555
|
+
// that derived name would overrun MySQL's 64-character limit.
|
|
556
|
+
if (unique && typeof unique === 'object' && !Array.isArray(unique)) {
|
|
557
|
+
t.unique(unique.columns, {indexName: unique.indexName});
|
|
558
|
+
} else {
|
|
559
|
+
t.unique(unique);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
546
562
|
}
|
|
547
563
|
if (tableSpec['@@PRIMARY_KEY@@']) {
|
|
548
564
|
t.primary(tableSpec['@@PRIMARY_KEY@@']);
|
|
@@ -682,15 +682,34 @@ module.exports = {
|
|
|
682
682
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
683
683
|
custom_field_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_custom_fields.id', cascadeDelete: true},
|
|
684
684
|
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
//
|
|
685
|
+
// Which part of the field's value this row carries. A scalar has one part and
|
|
686
|
+
// stores it under the empty path; a composite stores one row per sub-field it
|
|
687
|
+
// fills, under that sub-field's key. Not nullable, because MySQL counts NULLs
|
|
688
|
+
// as distinct in a unique index and the constraint below would stop holding.
|
|
689
|
+
path: {type: 'string', maxlength: 191, nullable: false, defaultTo: ''},
|
|
690
|
+
// One row, one value: a part a member has not filled in has no row at all. The
|
|
691
|
+
// column stays nullable even so, because making it NOT NULL is only reachable
|
|
692
|
+
// through knex's dropNullable, which rewrites the column on MySQL and widens
|
|
693
|
+
// TEXT to MEDIUMTEXT — a migrated site would then accept sixteen megabytes in a
|
|
694
|
+
// column a fresh install bounds at 65,535 bytes. The bound matching long_text's
|
|
695
|
+
// exactly is worth more than the schema restating what the write path enforces.
|
|
688
696
|
value_text: {type: 'text', maxlength: 65535, nullable: true},
|
|
689
|
-
value_json: {type: 'text', maxlength: 65535, nullable: true},
|
|
690
697
|
created_at: {type: 'dateTime', nullable: false},
|
|
691
698
|
updated_at: {type: 'dateTime', nullable: true},
|
|
699
|
+
// Named, because the name knex derives from the table and all three columns is
|
|
700
|
+
// 65 characters and MySQL stops at 64. The migration that first created this
|
|
701
|
+
// table already shortened a column for the same reason; a third column spends
|
|
702
|
+
// what headroom that bought.
|
|
692
703
|
'@@UNIQUE_CONSTRAINTS@@': [
|
|
693
|
-
['member_id', 'custom_field_id']
|
|
704
|
+
{columns: ['member_id', 'custom_field_id', 'path'], indexName: 'members_custom_field_values_leaf_unique'}
|
|
705
|
+
],
|
|
706
|
+
// What a segment filter looks up: every member holding a given value for a
|
|
707
|
+
// given part of a given field. The value itself is not in the index — it is
|
|
708
|
+
// TEXT, so MySQL would need a prefix length, and the schema's index builder
|
|
709
|
+
// applies one length to every column in a composite index rather than to a
|
|
710
|
+
// single chosen one.
|
|
711
|
+
'@@INDEXES@@': [
|
|
712
|
+
['custom_field_id', 'path']
|
|
694
713
|
]
|
|
695
714
|
},
|
|
696
715
|
members_stripe_customers: {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* callers accept either shape uniformly:
|
|
8
8
|
*
|
|
9
9
|
* const data = toPlain(post);
|
|
10
|
-
* urlService.
|
|
10
|
+
* urlService.getUrlForResource({...data, type: 'posts'}, ...)
|
|
11
11
|
*/
|
|
12
12
|
type JsonSerializable<T> = {
|
|
13
13
|
toJSON(): T;
|