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.
Files changed (428) hide show
  1. package/bin/validate-adapters.js +74 -0
  2. package/bin/validate-adapters.ts +87 -0
  3. package/bin/warm-compile-cache.sh +60 -0
  4. package/components/tryghost-adapter-base-scheduling-0.2.1.tgz +0 -0
  5. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  6. package/components/tryghost-kg-converters-1.2.4.tgz +0 -0
  7. package/components/tryghost-kg-default-cards-10.3.4.tgz +0 -0
  8. package/components/tryghost-kg-default-nodes-2.1.5.tgz +0 -0
  9. package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
  10. package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
  11. package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
  12. package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
  13. package/core/app.js +1 -1
  14. package/core/boot.js +21 -25
  15. package/core/bridge.js +10 -12
  16. package/core/built/admin/assets/Combination-BDvCdg1t.js +41 -0
  17. package/core/built/admin/assets/about-BNojRaxf.js +1 -0
  18. package/core/built/admin/assets/{account-migration-D3O_yVck.js → account-migration-DMS8JsM-.js} +1 -1
  19. package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
  20. package/core/built/admin/assets/activitypub/{account-migration-D8FbP-Z-.js → account-migration-DHFbePl6.js} +7 -7
  21. package/core/built/admin/assets/activitypub/activitypub.js +3 -3
  22. package/core/built/admin/assets/activitypub/{avatar-11EA1VSs.js → avatar-CT4tS2_S.js} +2 -2
  23. package/core/built/admin/assets/activitypub/{avatar-flipboard-jfbLG-xe.js → avatar-flipboard-ByYWKipo.js} +2 -2
  24. package/core/built/admin/assets/activitypub/{bluesky-sharing-f1jTQe2P.js → bluesky-sharing-srVnSHDz.js} +8 -8
  25. package/core/built/admin/assets/activitypub/{content-formatters-nmTOPY9M.js → content-formatters-C2sos5Ht.js} +3 -3
  26. package/core/built/admin/assets/activitypub/{copy-BCwo9ep3.js → copy-B1UBTOoZ.js} +2 -2
  27. package/core/built/admin/assets/activitypub/{dist-BEfX-0aW.js → dist-MPsb2WZ1.js} +4 -4
  28. package/core/built/admin/assets/activitypub/{domain-AaDlZlrA.js → domain-DzcM9Dzo.js} +9 -9
  29. package/core/built/admin/assets/activitypub/{edit-profile-DPi8Tl5M.js → edit-profile-CsQ_4xAk.js} +5 -5
  30. package/core/built/admin/assets/activitypub/{explore-CxTzjILQ.js → explore-CJe0DZBK.js} +8 -8
  31. package/core/built/admin/assets/activitypub/{feed-DPv5x2MX.js → feed-CKV3qWON.js} +9 -9
  32. package/core/built/admin/assets/activitypub/{field-DbgA8lat.js → field-DckHfDJf.js} +3 -3
  33. package/core/built/admin/assets/activitypub/{hash-BWuZoYkA.js → hash-Dlp8RM55.js} +2 -2
  34. package/core/built/admin/assets/activitypub/{inbox-35VDQ2K2.js → inbox-CsPy4R0M.js} +2 -2
  35. package/core/built/admin/assets/activitypub/{inbox-DSrF_i1O.js → inbox-DAsjzUUe.js} +10 -10
  36. package/core/built/admin/assets/activitypub/{label-CPjFBbo7.js → label-DWyo9E5T.js} +3 -3
  37. package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +1 -0
  38. package/core/built/admin/assets/activitypub/{moderation-BkDC1XVZ.js → moderation-B0Inc03M.js} +7 -7
  39. package/core/built/admin/assets/activitypub/{note-xEyH4xT-.js → note-CSE54lER.js} +7 -7
  40. package/core/built/admin/assets/activitypub/{notifications-DClaL3ui.js → notifications-RSwbKqdh.js} +9 -9
  41. package/core/built/admin/assets/activitypub/{preferences-DpnOa-4Z.js → preferences-CE231n2_.js} +11 -11
  42. package/core/built/admin/assets/activitypub/{profile-Dvi9mz-6.js → profile-aaQ1j1Eo.js} +13 -13
  43. package/core/built/admin/assets/activitypub/{reply-Cs9ySd6Z.js → reply-Cjb0Vs3m.js} +2 -2
  44. package/core/built/admin/assets/activitypub/{routes-DFBeTnfn.js → routes-Djb0mc9k.js} +26 -26
  45. package/core/built/admin/assets/activitypub/{separator-C5u_VjzY.js → separator-D8pROI5n.js} +3 -3
  46. package/core/built/admin/assets/activitypub/{settings-CV2fFu6D.js → settings-DMc32IQl.js} +5 -5
  47. package/core/built/admin/assets/activitypub/{site-_TgDd_EM.js → site-DVIcsohX.js} +4 -4
  48. package/core/built/admin/assets/activitypub/{step-1-BfnNL2oF.js → step-1-9Yey9usF.js} +5 -5
  49. package/core/built/admin/assets/activitypub/{step-2-DLndZkO-.js → step-2-DdYmKRXV.js} +6 -6
  50. package/core/built/admin/assets/activitypub/{step-3-A-PZTtRE.js → step-3-hdSkcjZU.js} +10 -10
  51. package/core/built/admin/assets/activitypub/{tabs-BUbz7tea.js → tabs-CUqseYzQ.js} +6 -6
  52. package/core/built/admin/assets/activitypub/{topic-filter-BdX_xYYT.js → topic-filter-Coni6OXM.js} +3 -3
  53. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-EwuRemHh.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
  54. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-cy4ACBc4.js → use-navigate-with-base-path-B046AM38.js} +24 -7
  55. package/core/built/admin/assets/activitypub/{use-reply-chain-data-CyFcGfan.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
  56. package/core/built/admin/assets/activitypub/{x-7zk8mV2p.js → x-BRfRYd16.js} +2 -2
  57. package/core/built/admin/assets/add-integration-modal-DU9ghGLS.js +1 -0
  58. package/core/built/admin/assets/add-newsletter-modal-DFxuQrYu.js +1 -0
  59. package/core/built/admin/assets/add-recommendation-modal-D-3LOrdd.js +1 -0
  60. package/core/built/admin/assets/analytics-DTIMRhWw.js +1 -0
  61. package/core/built/admin/assets/announcement-bar-modal-DGCcezB6.js +1 -0
  62. package/core/built/admin/assets/api-keys-Dx4NeNpK.js +1 -0
  63. package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
  64. package/core/built/admin/assets/{audience-CkBb2LlO.js → audience-DkknNqhZ.js} +1 -1
  65. package/core/built/admin/assets/automated-emails-PayPCT8L.js +1 -0
  66. package/core/built/admin/assets/{automations-DhKeS_U5.js → automations-162q5HjL.js} +1 -1
  67. package/core/built/admin/assets/{automations-cMllU545.js → automations-DvSU7QhW.js} +1 -1
  68. package/core/built/admin/assets/{bluesky-sharing-DPGpyD-G.js → bluesky-sharing-yZ9A_jL8.js} +1 -1
  69. package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
  70. package/core/built/admin/assets/{breadcrumb-DIfcF5ow.js → breadcrumb-CjOH7LCU.js} +1 -1
  71. package/core/built/admin/assets/{chart-helpers-DV0FkdcD.js → chart-helpers-DGxvWnBT.js} +1 -1
  72. package/core/built/admin/assets/checkbox-BSUcnNq_.js +1 -0
  73. package/core/built/admin/assets/{chunk.332.05991a101d67f732dec3.js → chunk.424.4a27628efdade15c2dab.js} +28 -27
  74. package/core/built/admin/assets/chunk.524.dce1c51e0bc30fb2718e.js +25 -0
  75. package/core/built/admin/assets/{chunk.211.6f7328d63c41536451b3.js → chunk.571.40f093ed6d883993714e.js} +5 -5
  76. package/core/built/admin/assets/chunk.582.3a430d5c6a744fd475b6.js +27 -0
  77. package/core/built/admin/assets/code-editor-view-BcyKABrG.js +1 -0
  78. package/core/built/admin/assets/color-picker-field-8NoSnhNI.js +1 -0
  79. package/core/built/admin/assets/color-utils-Dzeeuj1F.js +1 -0
  80. package/core/built/admin/assets/combobox-5rdgDrDV.js +1 -0
  81. package/core/built/admin/assets/command-BOYMbZa_.js +1 -0
  82. package/core/built/admin/assets/comments-BhNM5CYl.js +1 -0
  83. package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
  84. package/core/built/admin/assets/confirmation-modal-CYD7x3UQ.js +1 -0
  85. package/core/built/admin/assets/content-api-modal-BtW4Z1-1.js +1 -0
  86. package/core/built/admin/assets/copy-field-CEp_XieW.js +1 -0
  87. package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
  88. package/core/built/admin/assets/custom-integration-modal-bk3lNEuQ.js +1 -0
  89. package/core/built/admin/assets/{date-range-select-dXc-GsVe.js → date-range-select-B3Ij1Rqg.js} +1 -1
  90. package/core/built/admin/assets/debounce-Cx5B_9OI.js +3 -0
  91. package/core/built/admin/assets/design-and-theme-modal-B2xYKS1I.js +23 -0
  92. package/core/built/admin/assets/dirty-confirm-dialog-YE9L21gY.js +1 -0
  93. package/core/built/admin/assets/disabled-sources-indicator-uIzbWL7K.js +1 -0
  94. package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-ARN6XTcR.js} +1 -1
  95. package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
  96. package/core/built/admin/assets/{dist-DyViJwIy.js → dist-Bg8bSvLA.js} +3 -3
  97. package/core/built/admin/assets/dist-CsvXCJSe.js +23 -0
  98. package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
  99. package/core/built/admin/assets/dist-j1xxxnY8.js +1 -0
  100. package/core/built/admin/assets/dist-myQaY0ka.js +1 -0
  101. package/core/built/admin/assets/{domain-B5ogXUBa.js → domain-DzrOAPY8.js} +1 -1
  102. package/core/built/admin/assets/{dropzone-BnFG7dDP.js → dropzone-CzhP33wa.js} +1 -1
  103. package/core/built/admin/assets/{edit-profile-B07jCZy9.js → edit-profile-oufs4R4n.js} +1 -1
  104. package/core/built/admin/assets/editor-BBKrunMK.js +8 -0
  105. package/core/built/admin/assets/embed-signup-form-modal-Ch3s6lqA.js +9 -0
  106. package/core/built/admin/assets/{explore-Ca1y8HW_.js → explore-CA-R01Br.js} +1 -1
  107. package/core/built/admin/assets/{feed-x4iFMBP4.js → feed-ClC8_Gld.js} +1 -1
  108. package/core/built/admin/assets/{field-ChCAabZg.js → field-B3OPenbP.js} +1 -1
  109. package/core/built/admin/assets/{filter-sources-LdtH1e-T.js → filter-sources-CiPLnG9A.js} +1 -1
  110. package/core/built/admin/assets/filters-Bz0HsRie.js +1 -0
  111. package/core/built/admin/assets/{filters-CjVKRP8U.js → filters-yFvybRMe.js} +1 -1
  112. package/core/built/admin/assets/first-promoter-modal-BC0UpYCD.js +1 -0
  113. package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
  114. package/core/built/admin/assets/{ghost-7f01131115186d1af84ead76e79646dd.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +300 -298
  115. package/core/built/admin/assets/{gift-link-modal-Buqh2vXh.js → gift-link-modal-BKRuqo-k.js} +1 -1
  116. package/core/built/admin/assets/{gift-links-CsUhefWG.js → gift-links-CVuqLhsZ.js} +1 -1
  117. package/core/built/admin/assets/global-data-provider-RzW6Bnlf.js +1 -0
  118. package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
  119. package/core/built/admin/assets/{growth-DajOBdN0.js → growth-ClNAldfL.js} +1 -1
  120. package/core/built/admin/assets/{growth-Dy2UOSay.js → growth-Dz8OSG06.js} +2 -2
  121. package/core/built/admin/assets/history-modal-DIVuQ5Ct.js +1 -0
  122. package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
  123. package/core/built/admin/assets/html-field-CVZ2sMxw.js +1 -0
  124. package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
  125. package/core/built/admin/assets/images-Ok8FFJ3A.js +1 -0
  126. package/core/built/admin/assets/{inbox-BUOLDoji.js → inbox-CMUVI6qb.js} +13 -13
  127. package/core/built/admin/assets/index-DUXM0NlS.js +67 -0
  128. package/core/built/admin/assets/index-Ds2iUoF6.css +1 -0
  129. package/core/built/admin/assets/index.es-B9tvf-t4.js +1 -0
  130. package/core/built/admin/assets/{input-group-uSK3XT7b.js → input-group-2XU-iuh_.js} +1 -1
  131. package/core/built/admin/assets/integration-header-B4DyXV06.js +1 -0
  132. package/core/built/admin/assets/integrations-BT6_iJvu.js +1 -0
  133. package/core/built/admin/assets/invite-user-modal-DrZ7xxrZ.js +1 -0
  134. package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
  135. package/core/built/admin/assets/koenig-editor-base-BPj4S760.js +1 -0
  136. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9254 -9251
  137. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
  138. package/core/built/admin/assets/{koenig-lexical-BFc4yFed.js → koenig-lexical-Cdjv-m3_.js} +98 -98
  139. package/core/built/admin/assets/{kpi-card-CCt-qeWD.js → kpi-card-J8Mj1sDX.js} +1 -1
  140. package/core/built/admin/assets/{kpi-card-BN-E6WYX.js → kpi-card-awos0BMH.js} +1 -1
  141. package/core/built/admin/assets/{kpi-helpers-DlxTbTs4.js → kpi-helpers-SEhvH-8E.js} +1 -1
  142. package/core/built/admin/assets/{kpi-tabs-BlcUttpn.js → kpi-tabs-R6BjsKhd.js} +1 -1
  143. package/core/built/admin/assets/{label-AVrxwmsz.js → label-BMC3dzLI.js} +1 -1
  144. package/core/built/admin/assets/label-picker-Bnlru5tO.js +1 -0
  145. package/core/built/admin/assets/{labels-DAIqKed7.js → labels-DAPjKPy2.js} +1 -1
  146. package/core/built/admin/assets/layout-yR9rlvEw.js +1 -0
  147. package/core/built/admin/assets/limit-modal-COmLevuY.js +1 -0
  148. package/core/built/admin/assets/{link-helpers-D9LsDOkG.js → link-helpers-Bzkk68GD.js} +1 -1
  149. package/core/built/admin/assets/member-custom-fields-BvFKEpxJ.js +1 -0
  150. package/core/built/admin/assets/member-detail-CWdPAe3p.js +1 -0
  151. package/core/built/admin/assets/members-Bnt0ih2R.js +11 -0
  152. package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
  153. package/core/built/admin/assets/{moderation-CQ3urTFE.js → moderation-mPjYHgZW.js} +1 -1
  154. package/core/built/admin/assets/multi-select-combobox-CsoyHAiX.js +1 -0
  155. package/core/built/admin/assets/navigation-modal-D_c0badj.js +1 -0
  156. package/core/built/admin/assets/newsletter-DR49NyGS.js +1 -0
  157. package/core/built/admin/assets/newsletter-detail-modal-B45pL1pb.js +1 -0
  158. package/core/built/admin/assets/newsletter-emails-B-OLfrvr.js +1 -0
  159. package/core/built/admin/assets/{newsletters-vt3Ni1Ov.js → newsletters-DGs5BaKH.js} +1 -1
  160. package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
  161. package/core/built/admin/assets/{note-Dk877BTv.js → note-CSVsUKBm.js} +1 -1
  162. package/core/built/admin/assets/{notifications-CPlLjPxO.js → notifications-DV9FRtDw.js} +1 -1
  163. package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
  164. package/core/built/admin/assets/offers-container-modal-B0hFS8nk.js +1 -0
  165. package/core/built/admin/assets/{onboarding-route-DdStOtjy.js → onboarding-route--jL5KLPD.js} +1 -1
  166. package/core/built/admin/assets/overview-B-ZPNqMo.js +1 -0
  167. package/core/built/admin/assets/overview-D96iwoJi.js +1 -0
  168. package/core/built/admin/assets/{page-header-pp4dWmOS.js → page-header-DAvDYKO9.js} +1 -1
  169. package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
  170. package/core/built/admin/assets/pintura-modal-jiUCxX0Q.js +1 -0
  171. package/core/built/admin/assets/portal-frame-D4D4nEO6.js +1 -0
  172. package/core/built/admin/assets/portal-modal-BLFdYeDH.js +1 -0
  173. package/core/built/admin/assets/{post-analytics-BMJrI81R.js → post-analytics-BhAtiFJp.js} +1 -1
  174. package/core/built/admin/assets/post-analytics-header-DK27VjJm.js +1 -0
  175. package/core/built/admin/assets/{post-analytics-provider-Btml5vJI.js → post-analytics-provider-DGg0FUqs.js} +1 -1
  176. package/core/built/admin/assets/{post-share-modal-AcUZkbfL.js → post-share-modal-DtFAC132.js} +1 -1
  177. package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
  178. package/core/built/admin/assets/preferences-CuWLISHa.js +5 -0
  179. package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
  180. package/core/built/admin/assets/preview-modal-DctYx20m.js +1 -0
  181. package/core/built/admin/assets/profile-BsKYYtuL.js +1 -0
  182. package/core/built/admin/assets/radio-group-CI5_Kf_d.js +1 -0
  183. package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
  184. package/core/built/admin/assets/{router-provider-BmEZf6c6.js → router-provider-DSQfprxH.js} +1 -1
  185. package/core/built/admin/assets/select-CkXqNJDJ.js +1 -0
  186. package/core/built/admin/assets/settings-B4vM-rgV.js +4 -0
  187. package/core/built/admin/assets/{settings-BGgk7oCI.js → settings-ZCj-W56o.js} +1 -1
  188. package/core/built/admin/assets/settings-breadcrumbs-DIq31FwA.js +1 -0
  189. package/core/built/admin/assets/{share-modal-BeuydaBf.js → share-modal-BNj14g79.js} +1 -1
  190. package/core/built/admin/assets/slack-modal-C6SpMQ-q.js +1 -0
  191. package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
  192. package/core/built/admin/assets/{sort-button-Df4t2AL8.js → sort-button-B-k54ow2.js} +1 -1
  193. package/core/built/admin/assets/{source-icon-ajoaKb41.js → source-icon-Cej3sZM6.js} +1 -1
  194. package/core/built/admin/assets/{stats-C_lfxZAw.js → stats-B2KW8E2y.js} +1 -1
  195. package/core/built/admin/assets/{stats-view-Cxot1iD8.js → stats-view-CVzU2-p5.js} +1 -1
  196. package/core/built/admin/assets/{step-1-ogAA4lDr.js → step-1-Dbn1iIMU.js} +1 -1
  197. package/core/built/admin/assets/{step-2-C5Xini-7.js → step-2-BahnSuN6.js} +1 -1
  198. package/core/built/admin/assets/{step-3-CZv1FV81.js → step-3-CW04e2ZJ.js} +2 -2
  199. package/core/built/admin/assets/stripe-button-BkBZqaGG.js +1 -0
  200. package/core/built/admin/assets/stripe-connect-modal-CYDDrSJm.js +1 -0
  201. package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
  202. package/core/built/admin/assets/{table-DFj1wiYv.js → table-BtLcEi1M.js} +1 -1
  203. package/core/built/admin/assets/tabs-BaWRfg8j.js +1 -0
  204. package/core/built/admin/assets/tag-detail-CmqiC32L.js +1 -0
  205. package/core/built/admin/assets/tags-BbygUvNB.js +1 -0
  206. package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
  207. package/core/built/admin/assets/testimonials-modal--bdzppyh.js +1 -0
  208. package/core/built/admin/assets/tier-detail-modal-BZmAsIy4.js +1 -0
  209. package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
  210. package/core/built/admin/assets/toggle-group-CVl-Xigr.js +1 -0
  211. package/core/built/admin/assets/{topic-filter-DGDxDalo.js → topic-filter-BoRHRbL1.js} +1 -1
  212. package/core/built/admin/assets/transistor-modal-CBPDqEGy.js +1 -0
  213. package/core/built/admin/assets/unsplash-modal-CixoRAO2.js +1 -0
  214. package/core/built/admin/assets/{use-analytics-data-C76v4LIp.js → use-analytics-data-BiWvcf1u.js} +1 -1
  215. package/core/built/admin/assets/use-check-theme-limit-error-B14lZukR.js +2 -0
  216. package/core/built/admin/assets/use-feature-flag-BmJoc5WN.js +1 -0
  217. package/core/built/admin/assets/use-filter-params-ChoV72Hw.js +1 -0
  218. package/core/built/admin/assets/use-filterable-api-iMpDZBxD.js +1 -0
  219. package/core/built/admin/assets/use-form-vRjI0Kwk.js +1 -0
  220. package/core/built/admin/assets/{use-growth-stats-aNXJOmQr.js → use-growth-stats-Dom8xVzo.js} +1 -1
  221. package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
  222. package/core/built/admin/assets/use-limiter-Dwg8ivgQ.js +2 -0
  223. package/core/built/admin/assets/use-pintura-config-Bj4QmiVk.js +1 -0
  224. package/core/built/admin/assets/use-pintura-editor-DvrslmPE.js +1 -0
  225. package/core/built/admin/assets/{use-post-referrers-CG0SZioj.js → use-post-referrers-pxIHTXFs.js} +1 -1
  226. package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
  227. package/core/built/admin/assets/{use-reply-chain-data-RLdPKRmS.js → use-reply-chain-data-CmFe0N6F.js} +1 -1
  228. package/core/built/admin/assets/use-setting-group-Cmp_sy-d.js +1 -0
  229. package/core/built/admin/assets/use-settings-navigation-BmROPrls.js +1 -0
  230. package/core/built/admin/assets/{use-simple-pagination-DgcF69KI.js → use-simple-pagination-D5rL3YwO.js} +1 -1
  231. package/core/built/admin/assets/{use-tinybird-query-C1R_Kbmy.js → use-tinybird-query-DP2dMaq7.js} +1 -1
  232. package/core/built/admin/assets/{use-tinybird-token-C9fPjVM_.js → use-tinybird-token-DrK_skPQ.js} +1 -1
  233. package/core/built/admin/assets/use-url-input-ChrUZBFf.js +5 -0
  234. package/core/built/admin/assets/user-detail-modal-XODpOqNC.js +1 -0
  235. package/core/built/admin/assets/{users-Dkv8NQZe.js → users-B6hIds6n.js} +1 -1
  236. package/core/built/admin/assets/validator-Cr0sMKuW.js +7 -0
  237. package/core/built/admin/assets/{virtual-list-BmTUzeoF.js → virtual-list-Dnoafqbk.js} +1 -1
  238. package/core/built/admin/assets/web-D71mjB2r.js +1 -0
  239. package/core/built/admin/assets/web-z71lA1Y3.js +2 -0
  240. package/core/built/admin/assets/zapier-modal-Bp507Um7.js +1 -0
  241. package/core/built/admin/index.html +16 -14
  242. package/core/frontend/helpers/authors.js +1 -1
  243. package/core/frontend/helpers/link.js +3 -8
  244. package/core/frontend/helpers/tags.js +1 -1
  245. package/core/frontend/meta/author-url.js +2 -2
  246. package/core/frontend/meta/url.js +4 -4
  247. package/core/frontend/services/proxy.js +1 -1
  248. package/core/frontend/services/routing/events.js +20 -6
  249. package/core/frontend/services/routing/permalink-adapter.js +0 -13
  250. package/core/frontend/services/routing/permalink-adapter.ts +0 -13
  251. package/core/frontend/services/routing/router-manager.js +15 -9
  252. package/core/frontend/services/sitemap/base-site-map-generator.js +1 -28
  253. package/core/frontend/services/sitemap/site-map-manager.js +22 -57
  254. package/core/frontend/web/middleware/admin-toolbar.js +1 -1
  255. package/core/frontend/web/middleware/serve-indexnow-key.js +1 -7
  256. package/core/server/adapters/route-settings/utils.js +10 -2
  257. package/core/server/adapters/route-settings/utils.ts +12 -2
  258. package/core/server/api/endpoints/email-post.js +1 -1
  259. package/core/server/api/endpoints/index.js +2 -2
  260. package/core/server/api/endpoints/previews.js +1 -1
  261. package/core/server/api/endpoints/search-index-public.js +3 -3
  262. package/core/server/api/endpoints/search-index.js +4 -4
  263. package/core/server/api/endpoints/themes.js +6 -1
  264. package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -2
  265. package/core/server/api/endpoints/utils/serializers/input/comments.js +1 -1
  266. package/core/server/api/endpoints/utils/serializers/input/pages.js +3 -3
  267. package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
  268. package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
  269. package/core/server/api/endpoints/utils/serializers/input/users.js +2 -2
  270. package/core/server/api/endpoints/utils/serializers/input/utils/url.js +31 -13
  271. package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +2 -2
  272. package/core/server/api/endpoints/utils/serializers/output/mappers/tags.js +2 -2
  273. package/core/server/api/endpoints/utils/serializers/output/mappers/users.js +8 -0
  274. package/core/server/api/endpoints/utils/serializers/output/utils/url.js +18 -11
  275. package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +74 -0
  276. package/core/server/data/migrations/versions/6.57/2026-08-04-18-54-14-reset-automation-email-analytics.js +25 -0
  277. package/core/server/data/schema/commands.js +21 -5
  278. package/core/server/data/schema/schema.js +24 -5
  279. package/core/server/lib/common/to-plain.ts +1 -1
  280. package/core/server/models/base/plugins/overrides.js +7 -5
  281. package/core/server/models/post.js +2 -2
  282. package/core/server/services/adapter-manager/adapter-manager.js +1 -45
  283. package/core/server/services/adapter-manager/adapter-manager.ts +2 -48
  284. package/core/server/services/adapter-manager/adapter-paths.js +28 -0
  285. package/core/server/services/adapter-manager/adapter-paths.ts +26 -0
  286. package/core/server/services/adapter-manager/base-classes.js +22 -0
  287. package/core/server/services/adapter-manager/base-classes.ts +22 -0
  288. package/core/server/services/adapter-manager/index.js +4 -27
  289. package/core/server/services/adapter-manager/index.ts +4 -31
  290. package/core/server/services/adapter-manager/utils.js +50 -0
  291. package/core/server/services/adapter-manager/utils.ts +57 -0
  292. package/core/server/services/audience-feedback/audience-feedback-controller.js +11 -2
  293. package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
  294. package/core/server/services/audience-feedback/feedback-repository.js +4 -1
  295. package/core/server/services/auth/session/session-service.js +18 -3
  296. package/core/server/services/automations/database-automations-repository.js +5 -5
  297. package/core/server/services/automations/database-automations-repository.ts +12 -12
  298. package/core/server/services/automations/database-date.js +52 -3
  299. package/core/server/services/automations/database-date.ts +23 -2
  300. package/core/server/services/automations/service.js +2 -2
  301. package/core/server/services/automations/service.ts +2 -2
  302. package/core/server/services/comments/comments-service-emails.js +1 -1
  303. package/core/server/services/comments/comments-service.js +1 -1
  304. package/core/server/services/email-analytics/email-analytics-service-wrapper.js +12 -3
  305. package/core/server/services/email-analytics/email-analytics-service.js +4 -3
  306. package/core/server/services/email-analytics/index.js +2 -3
  307. package/core/server/services/email-analytics/index.ts +1 -2
  308. package/core/server/services/email-analytics/lib/queries.js +91 -124
  309. package/core/server/services/email-analytics/lib/queries.ts +341 -0
  310. package/core/server/services/email-service/email-service-wrapper.js +2 -1
  311. package/core/server/services/email-service/mailgun-email-provider.js +15 -0
  312. package/core/server/services/indexnow-ping/index.js +0 -2
  313. package/core/server/services/indexnow-ping/index.ts +0 -2
  314. package/core/server/services/indexnow-ping/indexnow-ping-service.js +6 -8
  315. package/core/server/services/indexnow-ping/indexnow-ping-service.ts +9 -17
  316. package/core/server/services/lib/mailgun-client.js +3 -3
  317. package/core/server/services/media-inliner/external-media-inliner.js +12 -8
  318. package/core/server/services/member-attribution/url-translator.js +5 -7
  319. package/core/server/services/member-welcome-emails/service.js +72 -12
  320. package/core/server/services/members/api.js +1 -1
  321. package/core/server/services/members/import-export/import/completion-email.js +8 -4
  322. package/core/server/services/members/import-export/import/completion-email.ts +9 -4
  323. package/core/server/services/members/import-export/import/importer.js +25 -4
  324. package/core/server/services/members/import-export/import/importer.ts +26 -6
  325. package/core/server/services/members/import-export/import/row.ts +5 -1
  326. package/core/server/services/members/import-export/index.js +1 -5
  327. package/core/server/services/members/import-export/index.ts +2 -6
  328. package/core/server/services/members/members-api/repositories/member-repository.js +51 -0
  329. package/core/server/services/members-custom-fields/queries.js +4 -13
  330. package/core/server/services/members-custom-fields/queries.ts +4 -13
  331. package/core/server/services/members-custom-fields/schema.js +18 -27
  332. package/core/server/services/members-custom-fields/schema.ts +17 -27
  333. package/core/server/services/members-custom-fields/storage.js +94 -27
  334. package/core/server/services/members-custom-fields/storage.ts +114 -39
  335. package/core/server/services/members-custom-fields/values-service.js +98 -219
  336. package/core/server/services/members-custom-fields/values-service.ts +110 -238
  337. package/core/server/services/mentions/resource-service.js +1 -1
  338. package/core/server/services/mentions/service.js +2 -2
  339. package/core/server/services/route-settings/dynamic-routing-service.js +9 -59
  340. package/core/server/services/slack-notifications/slack-notifications.js +2 -2
  341. package/core/server/services/slack-ping/index.js +1 -1
  342. package/core/server/services/slack-ping/index.ts +1 -1
  343. package/core/server/services/slack-ping/slack-ping-service.js +3 -2
  344. package/core/server/services/slack-ping/slack-ping-service.ts +4 -5
  345. package/core/server/services/stats/content-stats-service.js +3 -3
  346. package/core/server/services/stats/members-stats-service.js +0 -7
  347. package/core/server/services/stats/posts-stats-service.js +2 -2
  348. package/core/server/services/themes/storage.js +19 -2
  349. package/core/server/services/url/config.js +90 -135
  350. package/core/server/services/url/index.js +6 -49
  351. package/core/server/services/url/lazy-find-resource.js +14 -11
  352. package/core/server/services/url/lazy-find-resource.ts +14 -11
  353. package/core/server/services/url/lazy-url-service.js +174 -94
  354. package/core/server/services/url/lazy-url-service.ts +233 -97
  355. package/core/server/services/url/routable-resources.js +62 -71
  356. package/core/server/services/url/router-filter.js +12 -4
  357. package/core/server/services/url/router-filter.ts +12 -4
  358. package/core/server/services/webhooks/index.js +1 -1
  359. package/core/server/services/webhooks/serialize.js +12 -2
  360. package/core/server/web/api/endpoints/admin/routes.js +2 -2
  361. package/core/shared/config/env/config.testing-mysql.json +1 -2
  362. package/core/shared/config/env/config.testing.json +1 -2
  363. package/core/shared/custom-theme-settings-cache/custom-theme-settings-bread-service.js +4 -0
  364. package/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js +39 -0
  365. package/core/shared/events/index.js +1 -2
  366. package/core/shared/labs.js +4 -4
  367. package/package.json +10 -11
  368. package/pnpm-lock.yaml +44 -47
  369. package/pnpm-workspace.yaml +19 -19
  370. package/tsconfig.json +1 -0
  371. package/components/tryghost-adapter-base-scheduling-0.2.0.tgz +0 -0
  372. package/components/tryghost-kg-converters-1.2.3.tgz +0 -0
  373. package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
  374. package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
  375. package/core/built/admin/assets/Combination-BCm9FL4o.js +0 -41
  376. package/core/built/admin/assets/activitypub/layout-CTgrezN7.js +0 -1
  377. package/core/built/admin/assets/automated-emails-6t2bIRWf.js +0 -7
  378. package/core/built/admin/assets/checkbox-BIl077a0.js +0 -1
  379. package/core/built/admin/assets/chunk.524.87b52f4fada529df2c4c.js +0 -25
  380. package/core/built/admin/assets/chunk.582.340bcca873e806cbd4bb.js +0 -27
  381. package/core/built/admin/assets/code-editor-view-C0aWNwdl.js +0 -1
  382. package/core/built/admin/assets/comments-qwhIjH3c.js +0 -1
  383. package/core/built/admin/assets/disabled-sources-indicator-DYAXW-6V.js +0 -1
  384. package/core/built/admin/assets/dist-BUKmD51u.js +0 -1
  385. package/core/built/admin/assets/dist-CNlMj7ML.js +0 -1
  386. package/core/built/admin/assets/dist-DDY6GRVz.js +0 -1
  387. package/core/built/admin/assets/dist-DG78yroX.js +0 -23
  388. package/core/built/admin/assets/editor-DDYSOXGN.js +0 -8
  389. package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
  390. package/core/built/admin/assets/filters-CLehPo_D.js +0 -1
  391. package/core/built/admin/assets/index-08n05-rc.js +0 -67
  392. package/core/built/admin/assets/index-DgUzgFvS.css +0 -1
  393. package/core/built/admin/assets/label-picker-DVnpu_IF.js +0 -1
  394. package/core/built/admin/assets/layout-B0YLh7R9.js +0 -1
  395. package/core/built/admin/assets/member-custom-fields-C8NpZYza.js +0 -1
  396. package/core/built/admin/assets/member-detail-CIVAdKXf.js +0 -1
  397. package/core/built/admin/assets/members-oLPiSkXD.js +0 -10
  398. package/core/built/admin/assets/modals-CB7v-k3T.js +0 -31
  399. package/core/built/admin/assets/newsletter-rbeDX9Wk.js +0 -1
  400. package/core/built/admin/assets/overview--4V-kx9c.js +0 -1
  401. package/core/built/admin/assets/overview-DSLv30oz.js +0 -1
  402. package/core/built/admin/assets/post-analytics-header-pU2m9ZuV.js +0 -1
  403. package/core/built/admin/assets/preferences-Ru4CRPHC.js +0 -5
  404. package/core/built/admin/assets/profile-BNb1oj7D.js +0 -1
  405. package/core/built/admin/assets/select-B0HteHrp.js +0 -1
  406. package/core/built/admin/assets/settings-B1C9Q3wo.js +0 -4
  407. package/core/built/admin/assets/social-urls-DA2lXrhW.js +0 -8
  408. package/core/built/admin/assets/switch-CyQcwMAv.js +0 -1
  409. package/core/built/admin/assets/tabs-D4y1gqUd.js +0 -1
  410. package/core/built/admin/assets/tags-Dmft-Y4O.js +0 -1
  411. package/core/built/admin/assets/toggle-group-R2QMFvXW.js +0 -1
  412. package/core/built/admin/assets/use-filter-params-WrdhAISv.js +0 -1
  413. package/core/built/admin/assets/web-BqW0b0_C.js +0 -1
  414. package/core/built/admin/assets/web-Cy5GKMWM.js +0 -2
  415. package/core/server/services/email-analytics/jobs/update-member-email-analytics/index.js +0 -14
  416. package/core/server/services/url/local-file-cache.js +0 -75
  417. package/core/server/services/url/queue.js +0 -260
  418. package/core/server/services/url/resource.js +0 -102
  419. package/core/server/services/url/resources.js +0 -480
  420. package/core/server/services/url/url-generator.js +0 -276
  421. package/core/server/services/url/url-service-facade.js +0 -366
  422. package/core/server/services/url/url-service-facade.ts +0 -519
  423. package/core/server/services/url/url-service.js +0 -387
  424. package/core/server/services/url/urls.js +0 -140
  425. package/core/shared/events/url-resource-updated-event.js +0 -33
  426. /package/core/built/admin/assets/{chunk.211.6f7328d63c41536451b3.js.LICENSE.txt → chunk.571.40f093ed6d883993714e.js.LICENSE.txt} +0 -0
  427. /package/core/built/admin/assets/{dist-CqO2lWS0.js → dist-CqO2lWS02.js} +0 -0
  428. /package/core/server/api/endpoints/{gift-reminders.js → gifts.js} +0 -0
@@ -13,7 +13,7 @@ class SlackPingServiceWrapper {
13
13
  const {blogIcon} = require('../../lib/image');
14
14
  const events = require('../../lib/common/events');
15
15
  const logging = require('@tryghost/logging');
16
- const request = require('@tryghost/request');
16
+ const request = require('../../lib/request-external');
17
17
  const settingsCache = require('../../../shared/settings-cache');
18
18
  const urlService = require('../url');
19
19
  const urlUtils = require('../../../shared/url-utils').default;
@@ -54,7 +54,7 @@ class SlackPingService {
54
54
  const blogTitle = this.settingsCache.get('title');
55
55
  // If this is a post, we want to send the link of the post
56
56
  if (hasPostProperties(post)) {
57
- message = this.urlService.facade.getUrlForResource({ ...post, type: 'posts' }, { absolute: true });
57
+ message = this.urlService.getUrlForResource({ ...post, type: 'posts' }, { absolute: true });
58
58
  title = post.title ? post.title : null;
59
59
  author = post.authors ? post.authors[0] : null;
60
60
  if (post.custom_excerpt) {
@@ -136,7 +136,7 @@ class SlackPingService {
136
136
  fields: [
137
137
  {
138
138
  title: 'Author',
139
- value: author ? `<${this.urlService.facade.getUrlForResource({ ...author, type: 'authors' }, { absolute: true })} | ${author.name}>` : null,
139
+ value: author ? `<${this.urlService.getUrlForResource({ ...author, type: 'authors' }, { absolute: true })} | ${author.name}>` : null,
140
140
  short: true
141
141
  }
142
142
  ],
@@ -156,6 +156,7 @@ class SlackPingService {
156
156
  };
157
157
  }
158
158
  return this.request(slackSettings.url, {
159
+ method: 'POST',
159
160
  body: JSON.stringify(slackData),
160
161
  headers: {
161
162
  'Content-type': 'application/json'
@@ -9,9 +9,7 @@ type SettingsCache = {
9
9
  };
10
10
 
11
11
  type UrlService = {
12
- facade: {
13
- getUrlForResource(resource: Record<string, unknown>, options: {absolute: boolean}): string | null;
14
- };
12
+ getUrlForResource(resource: Record<string, unknown>, options: {absolute: boolean}): string | null;
15
13
  };
16
14
 
17
15
  type UrlUtils = {
@@ -126,7 +124,7 @@ export class SlackPingService {
126
124
 
127
125
  // If this is a post, we want to send the link of the post
128
126
  if (hasPostProperties(post)) {
129
- message = this.urlService.facade.getUrlForResource({...post, type: 'posts'}, {absolute: true});
127
+ message = this.urlService.getUrlForResource({...post, type: 'posts'}, {absolute: true});
130
128
  title = post.title ? post.title : null;
131
129
  author = post.authors ? post.authors[0] : null;
132
130
 
@@ -214,7 +212,7 @@ export class SlackPingService {
214
212
  fields: [
215
213
  {
216
214
  title: 'Author',
217
- value: author ? `<${this.urlService.facade.getUrlForResource({...author, type: 'authors'}, {absolute: true})} | ${author.name}>` : null,
215
+ value: author ? `<${this.urlService.getUrlForResource({...author, type: 'authors'}, {absolute: true})} | ${author.name}>` : null,
218
216
  short: true
219
217
  }
220
218
  ],
@@ -234,6 +232,7 @@ export class SlackPingService {
234
232
  }
235
233
 
236
234
  return this.request(slackSettings.url, {
235
+ method: 'POST',
237
236
  body: JSON.stringify(slackData),
238
237
  headers: {
239
238
  'Content-type': 'application/json'
@@ -199,7 +199,7 @@ class ContentStatsService {
199
199
  }
200
200
 
201
201
  try {
202
- const resource = await this.urlService.facade.resolveUrl(pathname);
202
+ const resource = await this.urlService.resolveUrl(pathname);
203
203
 
204
204
  if (resource) {
205
205
  // resource.type is the routing-level plural form (posts/pages/tags/authors).
@@ -251,8 +251,8 @@ class ContentStatsService {
251
251
  if (this.urlService && item.pathname) {
252
252
  try {
253
253
  // Check if URL service is ready
254
- if (this.urlService.facade.hasFinished && this.urlService.facade.hasFinished()) {
255
- const resource = await this.urlService.facade.resolveUrl(item.pathname);
254
+ if (this.urlService.hasFinished && this.urlService.hasFinished()) {
255
+ const resource = await this.urlService.resolveUrl(item.pathname);
256
256
  urlExists = !!resource; // Convert to boolean
257
257
  }
258
258
  // If URL service isn't ready, we default to true (clickable)
@@ -111,13 +111,6 @@ class MembersStatsService {
111
111
  const startDateMoment = moment.utc(startDate).startOf('day');
112
112
  const endDateMoment = moment.utc(today).startOf('day');
113
113
 
114
- // Create a map of events by date for fast lookup
115
- const eventsMap = new Map();
116
- rows.forEach((row) => {
117
- const date = moment(row.date).format('YYYY-MM-DD');
118
- eventsMap.set(date, row);
119
- });
120
-
121
114
  // Sort rows chronologically to calculate historical totals
122
115
  rows.sort((a, b) => moment(a.date).valueOf() - moment(b.date).valueOf());
123
116
 
@@ -222,8 +222,8 @@ class PostsStatsService {
222
222
  if (this.urlService && row.attribution_url) {
223
223
  try {
224
224
  // Check if URL service is ready
225
- if (this.urlService.facade.hasFinished && this.urlService.facade.hasFinished()) {
226
- const resource = await this.urlService.facade.resolveUrl(row.attribution_url);
225
+ if (this.urlService.hasFinished && this.urlService.hasFinished()) {
226
+ const resource = await this.urlService.resolveUrl(row.attribution_url);
227
227
  urlExists = !!resource; // Convert to boolean
228
228
  }
229
229
  // If URL service isn't ready, we default to true (clickable)
@@ -8,6 +8,7 @@ const errors = require('@tryghost/errors');
8
8
 
9
9
  const validate = require('./validate');
10
10
  const list = require('./list');
11
+ const customThemeSettings = require('../custom-theme-settings');
11
12
  const ThemeStorage = require('./theme-storage');
12
13
  const themeLoader = require('./loader');
13
14
  const activator = require('./activation-bridge');
@@ -24,7 +25,8 @@ const messages = {
24
25
  invalidThemeName: 'Please select a valid theme.',
25
26
  overrideDefaultTheme: 'Please rename your zip, it\'s not allowed to override the default theme.',
26
27
  destroyDefaultTheme: 'Deleting the default theme is not allowed.',
27
- destroyActive: 'Deleting the active theme is not allowed.'
28
+ destroyActive: 'Deleting the active theme is not allowed.',
29
+ copySettingsFromDoesNotExist: 'Theme to copy settings from is not installed.'
28
30
  };
29
31
 
30
32
  const INVALID_THEME_REGEX = /^[./]*$/;
@@ -51,7 +53,7 @@ module.exports = {
51
53
  name: themeName
52
54
  });
53
55
  },
54
- setFromZip: async (zip) => {
56
+ setFromZip: async (zip, {copySettingsFrom} = {}) => {
55
57
  const themeName = getStorage().getSanitizedFileName(zip.name.split('.zip')[0]);
56
58
  const backupName = `${themeName}_${ObjectID()}`;
57
59
 
@@ -69,12 +71,27 @@ module.exports = {
69
71
  });
70
72
  }
71
73
 
74
+ if (copySettingsFrom && !list.get(copySettingsFrom)) {
75
+ throw new errors.ValidationError({
76
+ message: tpl(messages.copySettingsFromDoesNotExist)
77
+ });
78
+ }
79
+
72
80
  let checkedTheme;
73
81
  let overrideTheme;
74
82
  let renamedExisting = false;
75
83
 
76
84
  try {
77
85
  checkedTheme = await validate.checkSafe(themeName, zip, true);
86
+
87
+ // CASE: theme uploaded as a copy of another theme, carry over that
88
+ // theme's settings so activating the copy keeps the site's design.
89
+ // Happens before any file changes so a failed copy leaves the
90
+ // installed themes untouched
91
+ if (copySettingsFrom) {
92
+ await customThemeSettings.api.copySettingsBetweenThemes(copySettingsFrom, themeName);
93
+ }
94
+
78
95
  const themeExists = await getStorage().exists(themeName);
79
96
  // CASE: move the existing theme to a backup folder
80
97
  if (themeExists) {
@@ -1,153 +1,108 @@
1
1
  /*
2
- * These are the default resources and filters.
3
- * They contain minimum filters for public accessibility of resources.
2
+ * Columns a routes.yaml filter must not be evaluated against.
3
+ *
4
+ * Ghost used to answer URLs from a map precomputed at boot, and kept that map
5
+ * lean by dropping these columns from every cached record. A collection filter
6
+ * referencing one therefore matched it as absent, which NQL reads as null. The
7
+ * URL service loads full records and would see the real value, so it strips
8
+ * these before evaluating a filter — otherwise routes.yaml files written
9
+ * against the old behaviour would start routing differently.
10
+ *
11
+ * `lazy-find-resource` strips the same columns so a resolved resource has the
12
+ * shape callers have always seen.
4
13
  */
5
14
 
6
15
  // TODO: switch exclude lists to include lists to make this more explicit
7
16
  module.exports = [
8
17
  {
9
18
  type: 'posts',
10
- modelOptions: {
11
- modelName: 'Post',
12
- filter: 'status:published+type:post',
13
- exclude: [
14
- 'title',
15
- 'mobiledoc',
16
- 'lexical',
17
- 'html',
18
- 'plaintext',
19
- // @TODO: https://github.com/TryGhost/Ghost/issues/10335
20
- // 'page',
21
- 'status',
22
- 'codeinjection_head',
23
- 'codeinjection_foot',
24
- 'meta_title',
25
- 'meta_description',
26
- 'custom_excerpt',
27
- 'og_image',
28
- 'og_title',
29
- 'og_description',
30
- 'twitter_image',
31
- 'twitter_title',
32
- 'twitter_description',
33
- 'custom_template',
34
- 'locale',
35
- 'newsletter_id',
36
- 'show_title_and_feature_image',
37
- 'email_recipient_filter',
38
- 'comment_id',
39
- 'tiers'
40
- ],
41
- withRelated: ['tags', 'authors'],
42
- withRelatedPrimary: {
43
- primary_tag: 'tags',
44
- primary_author: 'authors'
45
- },
46
- withRelatedFields: {
47
- tags: ['tags.id', 'tags.slug'],
48
- authors: ['users.id', 'users.slug']
49
- }
50
- },
51
- events: {
52
- add: 'post.published',
53
- update: 'post.published.edited',
54
- remove: 'post.unpublished'
55
- }
19
+ exclude: [
20
+ 'title',
21
+ 'mobiledoc',
22
+ 'lexical',
23
+ 'html',
24
+ 'plaintext',
25
+ // @TODO: https://github.com/TryGhost/Ghost/issues/10335
26
+ // 'page',
27
+ 'status',
28
+ 'codeinjection_head',
29
+ 'codeinjection_foot',
30
+ 'meta_title',
31
+ 'meta_description',
32
+ 'custom_excerpt',
33
+ 'og_image',
34
+ 'og_title',
35
+ 'og_description',
36
+ 'twitter_image',
37
+ 'twitter_title',
38
+ 'twitter_description',
39
+ 'custom_template',
40
+ 'locale',
41
+ 'newsletter_id',
42
+ 'show_title_and_feature_image',
43
+ 'email_recipient_filter',
44
+ 'comment_id',
45
+ 'tiers'
46
+ ]
56
47
  },
57
48
  {
58
49
  type: 'pages',
59
- modelOptions: {
60
- modelName: 'Post',
61
- exclude: [
62
- 'title',
63
- 'mobiledoc',
64
- 'lexical',
65
- 'html',
66
- 'plaintext',
67
- // @TODO: https://github.com/TryGhost/Ghost/issues/10335
68
- // 'page',
69
- // 'status',
70
- 'codeinjection_head',
71
- 'codeinjection_foot',
72
- 'meta_title',
73
- 'meta_description',
74
- 'custom_excerpt',
75
- 'og_image',
76
- 'og_title',
77
- 'og_description',
78
- 'twitter_image',
79
- 'twitter_title',
80
- 'twitter_description',
81
- 'custom_template',
82
- 'locale',
83
- 'tags',
84
- 'authors',
85
- 'primary_tag',
86
- 'primary_author',
87
- 'newsletter_id',
88
- 'show_title_and_feature_image',
89
- 'email_recipient_filter',
90
- 'comment_id',
91
- 'tiers'
92
- ],
93
- filter: 'status:published+type:page'
94
- },
95
- events: {
96
- add: 'page.published',
97
- update: 'page.published.edited',
98
- remove: 'page.unpublished'
99
- }
50
+ exclude: [
51
+ 'title',
52
+ 'mobiledoc',
53
+ 'lexical',
54
+ 'html',
55
+ 'plaintext',
56
+ // @TODO: https://github.com/TryGhost/Ghost/issues/10335
57
+ // 'page',
58
+ // 'status',
59
+ 'codeinjection_head',
60
+ 'codeinjection_foot',
61
+ 'meta_title',
62
+ 'meta_description',
63
+ 'custom_excerpt',
64
+ 'og_image',
65
+ 'og_title',
66
+ 'og_description',
67
+ 'twitter_image',
68
+ 'twitter_title',
69
+ 'twitter_description',
70
+ 'custom_template',
71
+ 'locale',
72
+ 'tags',
73
+ 'authors',
74
+ 'primary_tag',
75
+ 'primary_author',
76
+ 'newsletter_id',
77
+ 'show_title_and_feature_image',
78
+ 'email_recipient_filter',
79
+ 'comment_id',
80
+ 'tiers'
81
+ ]
100
82
  },
101
83
  {
102
84
  type: 'tags',
103
- keep: ['id', 'slug'],
104
- modelOptions: {
105
- modelName: 'Tag',
106
- exclude: [
107
- 'description',
108
- 'meta_title',
109
- 'meta_description',
110
- 'parent_id'
111
- ],
112
- filter: 'visibility:public',
113
- shouldHavePosts: {
114
- joinTo: 'tag_id',
115
- joinTable: 'posts_tags'
116
- }
117
- },
118
- events: {
119
- add: 'tag.added',
120
- update: ['tag.edited', 'tag.attached', 'tag.detached'],
121
- remove: 'tag.deleted'
122
- }
85
+ exclude: [
86
+ 'description',
87
+ 'meta_title',
88
+ 'meta_description',
89
+ 'parent_id'
90
+ ]
123
91
  },
124
92
  {
125
93
  type: 'authors',
126
- modelOptions: {
127
- modelName: 'User',
128
- exclude: [
129
- 'bio',
130
- 'website',
131
- 'location',
132
- 'facebook',
133
- 'twitter',
134
- 'locale',
135
- 'accessibility',
136
- 'meta_title',
137
- 'meta_description',
138
- 'tour',
139
- 'last_seen'
140
- ],
141
- filter: 'visibility:public',
142
- shouldHavePosts: {
143
- joinTo: 'author_id',
144
- joinTable: 'posts_authors'
145
- }
146
- },
147
- events: {
148
- add: 'user.activated',
149
- update: ['user.activated.edited', 'user.attached', 'user.detached'],
150
- remove: 'user.deleted'
151
- }
94
+ exclude: [
95
+ 'bio',
96
+ 'website',
97
+ 'location',
98
+ 'facebook',
99
+ 'twitter',
100
+ 'locale',
101
+ 'accessibility',
102
+ 'meta_title',
103
+ 'meta_description',
104
+ 'tour',
105
+ 'last_seen'
106
+ ]
152
107
  }
153
108
  ];
@@ -1,50 +1,7 @@
1
- const config = require('../../../shared/config');
2
- const LocalFileCache = require('./local-file-cache');
3
- const UrlService = require('./url-service');
4
- const UrlServiceFacade = require('./url-service-facade');
1
+ const LazyUrlService = require('./lazy-url-service');
2
+ const {createFindResource} = require('./lazy-find-resource');
3
+ const models = require('../../models');
5
4
 
6
- // NOTE: instead of a path we could give UrlService a "data-resolver" of some sort
7
- // so it doesn't have to contain the logic to read data at all. This would be
8
- // a possible improvement in the future
9
- let writeDisabled = false;
10
- let storagePath = config.getContentPath('data');
11
-
12
- // TODO: remove this hack in favor of loading from the content path when it's possible to do so
13
- // by mocking content folders in pre-boot phase
14
- if (process.env.NODE_ENV.startsWith('test')){
15
- storagePath = config.get('paths').urlCache;
16
-
17
- // NOTE: prevents test suites from overwriting cache fixtures.
18
- // A better solution would be injecting a different implementation of the
19
- // cache based on the environment, this approach should do the trick for now
20
- writeDisabled = true;
21
- }
22
-
23
- const cache = new LocalFileCache({storagePath, writeDisabled});
24
- const urlService = new UrlService({cache});
25
-
26
- // Build a lazy backend alongside eager for shadow comparison, gated by the
27
- // existing `lazyRouting` flag (unset by default = pure eager, unchanged).
28
- // models is already loaded via url-service -> resources, so this require is safe.
29
- let lazyUrlService = null;
30
- let fetchRoutableResources = null;
31
- if (config.get('lazyRouting')) {
32
- const LazyUrlService = require('./lazy-url-service');
33
- const {createFindResource} = require('./lazy-find-resource');
34
- const {createFetchRoutableResources} = require('./routable-resources');
35
- const models = require('../../models');
36
- lazyUrlService = new LazyUrlService({findResource: createFindResource(models)});
37
- fetchRoutableResources = createFetchRoutableResources({lazyUrlService});
38
- }
39
-
40
- const urlServiceFacade = lazyUrlService
41
- ? new UrlServiceFacade({urlService, lazyUrlService, compare: true, fetchRoutableResources})
42
- : new UrlServiceFacade({urlService});
43
-
44
- // Singleton: default export remains the eager UrlService for backwards
45
- // compatibility with existing imports. The new facade is exposed alongside
46
- // it via `urlService.facade` so RouterManager and migrating callers can
47
- // reach for it without forcing every consumer to update at once.
48
- urlService.facade = urlServiceFacade;
49
-
50
- module.exports = urlService;
5
+ // Singleton: every caller shares the router registrations made by
6
+ // RouterManager at boot and on every routes.yaml reload.
7
+ module.exports = new LazyUrlService({findResource: createFindResource(models)});
@@ -7,15 +7,17 @@ const POST_SCOPE = { type: 'post', status: 'published' };
7
7
  const PAGE_SCOPE = { type: 'page', status: 'published' };
8
8
  const POST_RELATIONS = ['tags', 'authors'];
9
9
  const RELATION_KEYS = ['tags', 'authors', 'primary_tag', 'primary_author'];
10
- // Drop the same fields the eager resourceConfig excludes so the resolved record
11
- // has the same shape the eager service exposes (no post body, no extra
12
- // author/tag fields). posts_meta is an auto-loaded relation eager never keeps.
10
+ // Drop the same columns the URL service excludes from filter evaluation, so a
11
+ // resolved record keeps the shape callers have always seen (no post body, no
12
+ // extra author/tag fields). posts_meta is an auto-loaded relation Ghost has
13
+ // never exposed here.
13
14
  function excludeFor(type) {
14
15
  const cfg = resourcesConfig.find((c) => c.type === type);
15
- const exclude = (cfg && cfg.modelOptions.exclude) || [];
16
+ const exclude = cfg?.exclude ?? [];
16
17
  return [...exclude, 'posts_meta'];
17
18
  }
18
- // Eager trims relations to {id, slug} via withRelatedFields; match that.
19
+ // Relations are trimmed to {id, slug}: permalinks and filters read nothing
20
+ // else off them, and callers have never seen more.
19
21
  function trimRelation(value) {
20
22
  if (Array.isArray(value)) {
21
23
  return value.map(item => _.pick(item, ['id', 'slug']));
@@ -25,11 +27,11 @@ function trimRelation(value) {
25
27
  }
26
28
  return value;
27
29
  }
28
- function pruneToEagerShape(record, type) {
30
+ function pruneToPublicShape(record, type) {
29
31
  const pruned = _.omit(record, excludeFor(type));
30
32
  if (type === 'pages') {
31
- // Eager always exposes primary_tag/primary_author as null on pages (they
32
- // are virtual Post fields) but never carries the tags/authors arrays.
33
+ // primary_tag/primary_author are always null on pages (they are virtual
34
+ // Post fields), and pages never carry the tags/authors arrays.
33
35
  pruned.primary_tag = null;
34
36
  pruned.primary_author = null;
35
37
  return pruned;
@@ -43,8 +45,9 @@ function pruneToEagerShape(record, type) {
43
45
  }
44
46
  /**
45
47
  * Builds the per-request DB lookup hook injected into LazyUrlService.resolveUrl.
46
- * Visibility rules mirror the eager service so a guessed slug can't surface
47
- * anything the eager path hid; unknown types resolve to null.
48
+ * Visibility rules match the forward lookup's base filters, so a guessed slug
49
+ * can't surface anything a generated URL would have hidden; unknown types
50
+ * resolve to null.
48
51
  */
49
52
  function createFindResource(models) {
50
53
  const loadOne = async (Model, query, type, options = {}) => {
@@ -57,7 +60,7 @@ function createFindResource(models) {
57
60
  if (Array.isArray(record.authors)) {
58
61
  record.primary_author = record.authors[0] ?? null;
59
62
  }
60
- return pruneToEagerShape(record, type);
63
+ return pruneToPublicShape(record, type);
61
64
  };
62
65
  return (type, params) => {
63
66
  switch (type) {
@@ -18,16 +18,18 @@ const PAGE_SCOPE = {type: 'page', status: 'published'};
18
18
  const POST_RELATIONS = ['tags', 'authors'];
19
19
  const RELATION_KEYS = ['tags', 'authors', 'primary_tag', 'primary_author'];
20
20
 
21
- // Drop the same fields the eager resourceConfig excludes so the resolved record
22
- // has the same shape the eager service exposes (no post body, no extra
23
- // author/tag fields). posts_meta is an auto-loaded relation eager never keeps.
21
+ // Drop the same columns the URL service excludes from filter evaluation, so a
22
+ // resolved record keeps the shape callers have always seen (no post body, no
23
+ // extra author/tag fields). posts_meta is an auto-loaded relation Ghost has
24
+ // never exposed here.
24
25
  function excludeFor(type: string): string[] {
25
26
  const cfg = resourcesConfig.find((c: {type: string}) => c.type === type);
26
- const exclude = (cfg && cfg.modelOptions.exclude) || [];
27
+ const exclude = cfg?.exclude ?? [];
27
28
  return [...exclude, 'posts_meta'];
28
29
  }
29
30
 
30
- // Eager trims relations to {id, slug} via withRelatedFields; match that.
31
+ // Relations are trimmed to {id, slug}: permalinks and filters read nothing
32
+ // else off them, and callers have never seen more.
31
33
  function trimRelation(value: unknown): unknown {
32
34
  if (Array.isArray(value)) {
33
35
  return value.map(item => _.pick(item, ['id', 'slug']));
@@ -38,12 +40,12 @@ function trimRelation(value: unknown): unknown {
38
40
  return value;
39
41
  }
40
42
 
41
- function pruneToEagerShape(record: Record<string, unknown>, type: string): Record<string, unknown> {
43
+ function pruneToPublicShape(record: Record<string, unknown>, type: string): Record<string, unknown> {
42
44
  const pruned = _.omit(record, excludeFor(type));
43
45
 
44
46
  if (type === 'pages') {
45
- // Eager always exposes primary_tag/primary_author as null on pages (they
46
- // are virtual Post fields) but never carries the tags/authors arrays.
47
+ // primary_tag/primary_author are always null on pages (they are virtual
48
+ // Post fields), and pages never carry the tags/authors arrays.
47
49
  pruned.primary_tag = null;
48
50
  pruned.primary_author = null;
49
51
  return pruned;
@@ -59,8 +61,9 @@ function pruneToEagerShape(record: Record<string, unknown>, type: string): Recor
59
61
 
60
62
  /**
61
63
  * Builds the per-request DB lookup hook injected into LazyUrlService.resolveUrl.
62
- * Visibility rules mirror the eager service so a guessed slug can't surface
63
- * anything the eager path hid; unknown types resolve to null.
64
+ * Visibility rules match the forward lookup's base filters, so a guessed slug
65
+ * can't surface anything a generated URL would have hidden; unknown types
66
+ * resolve to null.
64
67
  */
65
68
  export function createFindResource(models: Models): FindResource {
66
69
  const loadOne = async (
@@ -78,7 +81,7 @@ export function createFindResource(models: Models): FindResource {
78
81
  if (Array.isArray(record.authors)) {
79
82
  record.primary_author = record.authors[0] ?? null;
80
83
  }
81
- return pruneToEagerShape(record, type);
84
+ return pruneToPublicShape(record, type);
82
85
  };
83
86
 
84
87
  return (type: string, params: ResourceLookupParams): Promise<Record<string, unknown> | null> => {