ghost 6.55.0 → 6.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) 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-custom-field-types-0.0.0.tgz +0 -0
  5. package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
  6. package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
  7. package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
  8. package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
  9. package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
  10. package/components/tryghost-nql-string-0.0.0.tgz +0 -0
  11. package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
  12. package/core/app.js +1 -1
  13. package/core/boot.js +21 -25
  14. package/core/bridge.js +10 -12
  15. package/core/built/admin/assets/Combination-BDvCdg1t.js +41 -0
  16. package/core/built/admin/assets/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
  17. package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
  18. package/core/built/admin/assets/about-C7O-AgRZ.js +1 -0
  19. package/core/built/admin/assets/{account-migration-sjHLx5dy.js → account-migration-Cv_ol1ko.js} +1 -1
  20. package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
  21. package/core/built/admin/assets/activitypub/{account-migration-DoYXtnZ8.js → account-migration-DHFbePl6.js} +7 -7
  22. package/core/built/admin/assets/activitypub/activitypub.js +6 -6
  23. package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-CT4tS2_S.js} +2 -2
  24. package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-ByYWKipo.js} +2 -2
  25. package/core/built/admin/assets/activitypub/{bluesky-sharing-Bxeh6MKW.js → bluesky-sharing-srVnSHDz.js} +16 -16
  26. package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-C2sos5Ht.js} +3 -3
  27. package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-B1UBTOoZ.js} +2 -2
  28. package/core/built/admin/assets/activitypub/{dist-D8sFfdpC.js → dist-MPsb2WZ1.js} +12 -12
  29. package/core/built/admin/assets/activitypub/{domain-CWueFW6q.js → domain-DzcM9Dzo.js} +9 -9
  30. package/core/built/admin/assets/activitypub/{edit-profile-BGxPU7N5.js → edit-profile-CsQ_4xAk.js} +945 -945
  31. package/core/built/admin/assets/activitypub/{explore-DBK-u0Ch.js → explore-CJe0DZBK.js} +8 -8
  32. package/core/built/admin/assets/activitypub/{feed-Bcir5RQZ.js → feed-CKV3qWON.js} +33 -33
  33. package/core/built/admin/assets/activitypub/{field-BV-CIT3d.js → field-DckHfDJf.js} +3 -3
  34. package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-Dlp8RM55.js} +2 -2
  35. package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-CsPy4R0M.js} +2 -2
  36. package/core/built/admin/assets/activitypub/{inbox-yC0nfvzl.js → inbox-DAsjzUUe.js} +85 -85
  37. package/core/built/admin/assets/activitypub/{label-DTRS9TDk.js → label-DWyo9E5T.js} +3 -3
  38. package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +1 -0
  39. package/core/built/admin/assets/activitypub/{moderation-Dwl9t0pb.js → moderation-B0Inc03M.js} +11 -11
  40. package/core/built/admin/assets/activitypub/{note-B-zKsZ7r.js → note-CSE54lER.js} +7 -7
  41. package/core/built/admin/assets/activitypub/{notifications-DjJZD54t.js → notifications-RSwbKqdh.js} +9 -9
  42. package/core/built/admin/assets/activitypub/{preferences-CoU8vs6d.js → preferences-CE231n2_.js} +20 -20
  43. package/core/built/admin/assets/activitypub/{profile-BAsaJekx.js → profile-aaQ1j1Eo.js} +237 -237
  44. package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cjb0Vs3m.js} +2 -2
  45. package/core/built/admin/assets/activitypub/{routes-CLH3OVTP.js → routes-Djb0mc9k.js} +1139 -1150
  46. package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-D8pROI5n.js} +3 -3
  47. package/core/built/admin/assets/activitypub/{settings-DA6pNJ-Q.js → settings-DMc32IQl.js} +13 -13
  48. package/core/built/admin/assets/activitypub/{site-CQD42Pw5.js → site-DVIcsohX.js} +81 -81
  49. package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-9Yey9usF.js} +5 -5
  50. package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DdYmKRXV.js} +6 -6
  51. package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-hdSkcjZU.js} +10 -10
  52. package/core/built/admin/assets/activitypub/{tabs-Cg0saow1.js → tabs-CUqseYzQ.js} +317 -315
  53. package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-Coni6OXM.js} +3 -3
  54. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
  55. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-B046AM38.js} +115 -96
  56. package/core/built/admin/assets/activitypub/{use-reply-chain-data-Br4aOWOh.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
  57. package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-BRfRYd16.js} +2 -2
  58. package/core/built/admin/assets/add-integration-modal-m9Dv6f77.js +1 -0
  59. package/core/built/admin/assets/add-newsletter-modal-CPSVZL2H.js +1 -0
  60. package/core/built/admin/assets/add-recommendation-modal-Ci9Z5yat.js +1 -0
  61. package/core/built/admin/assets/analytics-DqSCVV8U.js +1 -0
  62. package/core/built/admin/assets/announcement-bar-modal-DU3avV7d.js +1 -0
  63. package/core/built/admin/assets/api-keys-BK0LylyK.js +1 -0
  64. package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
  65. package/core/built/admin/assets/{audience-DjEWNhp3.js → audience-DTJtXXWY.js} +1 -1
  66. package/core/built/admin/assets/automated-emails-PayPCT8L.js +1 -0
  67. package/core/built/admin/assets/{automations-DhKeS_U5.js → automations-162q5HjL.js} +1 -1
  68. package/core/built/admin/assets/{automations-Bx3VcFIL.js → automations-dtQ_MRZ7.js} +1 -1
  69. package/core/built/admin/assets/{bluesky-sharing-ClMd0-Ft.js → bluesky-sharing-B8Es3AuV.js} +1 -1
  70. package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
  71. package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
  72. package/core/built/admin/assets/{breadcrumb-BD0U2ygg.js → breadcrumb-tg5zL--z.js} +1 -1
  73. package/core/built/admin/assets/build-CsiH26uS.js +1 -0
  74. package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
  75. package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
  76. package/core/built/admin/assets/{chart-helpers-fjQKRVyY.js → chart-helpers-CZElTGYL.js} +1 -1
  77. package/core/built/admin/assets/checkbox-Cyo-x7mC.js +1 -0
  78. package/core/built/admin/assets/chunk.524.cd5498dd4e195f679266.js +25 -0
  79. package/core/built/admin/assets/chunk.582.6f92ec1f03734bf2ea3f.js +27 -0
  80. package/core/built/admin/assets/{chunk.481.6c7395a49484bb4583aa.js → chunk.873.b8fc05a8ab877fb819ed.js} +62 -60
  81. package/core/built/admin/assets/code-editor-view-Dq1rQ4tS.js +1 -0
  82. package/core/built/admin/assets/color-picker-field-BonBgxhU.js +1 -0
  83. package/core/built/admin/assets/color-utils-BsrwXuDz.js +1 -0
  84. package/core/built/admin/assets/combobox-uX5r_paI.js +1 -0
  85. package/core/built/admin/assets/command-DxwBO2pF.js +1 -0
  86. package/core/built/admin/assets/comments-Dn-RO-nU.js +1 -0
  87. package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
  88. package/core/built/admin/assets/confirmation-modal-B6D6mk6R.js +1 -0
  89. package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
  90. package/core/built/admin/assets/content-api-modal-BxQ-7T6u.js +1 -0
  91. package/core/built/admin/assets/copy-field-BAxL4Zqn.js +1 -0
  92. package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
  93. package/core/built/admin/assets/custom-integration-modal---d_l7lc.js +1 -0
  94. package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
  95. package/core/built/admin/assets/{date-range-select-BFp58EMk.js → date-range-select-PB2n1HZf.js} +1 -1
  96. package/core/built/admin/assets/debounce-Dg2fW5Rg.js +3 -0
  97. package/core/built/admin/assets/design-and-theme-modal-DYRT207a.js +23 -0
  98. package/core/built/admin/assets/dirty-confirm-dialog-BnDJ39ao.js +1 -0
  99. package/core/built/admin/assets/disabled-sources-indicator-iMHrkyV3.js +1 -0
  100. package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
  101. package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
  102. package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS02.js} +1 -1
  103. package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-D5vhMdXf.js} +1 -1
  104. package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
  105. package/core/built/admin/assets/dist-WeOIvSYM.js +23 -0
  106. package/core/built/admin/assets/{dist-DyViJwIy.js → dist-cFzKLG7y.js} +3 -3
  107. package/core/built/admin/assets/dist-j1xxxnY8.js +1 -0
  108. package/core/built/admin/assets/dist-myQaY0ka.js +1 -0
  109. package/core/built/admin/assets/domain-BR8FIdjD.js +1 -0
  110. package/core/built/admin/assets/{dropzone-DlK-K2DQ.js → dropzone-hNxmUvwD.js} +1 -1
  111. package/core/built/admin/assets/{edit-profile-BKT-REJw.js → edit-profile-CENrkY0l.js} +1 -1
  112. package/core/built/admin/assets/editor-B18D0lm8.js +8 -0
  113. package/core/built/admin/assets/embed-signup-form-modal-BsCYUMvQ.js +9 -0
  114. package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
  115. package/core/built/admin/assets/{explore-ClUpJZOp.js → explore-BmrnljWw.js} +1 -1
  116. package/core/built/admin/assets/{feed-DF92dUMP.js → feed-Dy4li6i8.js} +1 -1
  117. package/core/built/admin/assets/{field-Do7lieBA.js → field-Dh_lwk13.js} +1 -1
  118. package/core/built/admin/assets/filter-sources-D2zDuJH5.js +1 -0
  119. package/core/built/admin/assets/filters-3JBQcNtJ.js +1 -0
  120. package/core/built/admin/assets/filters-CFt_BDe4.js +1 -0
  121. package/core/built/admin/assets/first-promoter-modal-D6Rjxugw.js +1 -0
  122. package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
  123. package/core/built/admin/assets/{ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
  124. package/core/built/admin/assets/{ghost-c4057474943effc568c5659ccc4909d7.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +420 -398
  125. package/core/built/admin/assets/{ghost-dark-db36d795d21c61426837df40af270966.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
  126. package/core/built/admin/assets/{gift-link-modal-f9VIMNDv.js → gift-link-modal-zMtlJhLQ.js} +1 -1
  127. package/core/built/admin/assets/{gift-links-CsUhefWG.js → gift-links-CVuqLhsZ.js} +1 -1
  128. package/core/built/admin/assets/global-data-provider-B949LDXJ.js +1 -0
  129. package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
  130. package/core/built/admin/assets/{growth-CVw1My4S.js → growth-C38Xtp8D.js} +2 -2
  131. package/core/built/admin/assets/{growth-B3I6gNLm.js → growth-CFws67_i.js} +1 -1
  132. package/core/built/admin/assets/history-modal-ILAOkbxH.js +1 -0
  133. package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
  134. package/core/built/admin/assets/html-field-9a9Id_2q.js +1 -0
  135. package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
  136. package/core/built/admin/assets/images-CrQKVc9Q.js +1 -0
  137. package/core/built/admin/assets/{inbox-CFBSu4Ed.js → inbox-BrSRzyvz.js} +6 -6
  138. package/core/built/admin/assets/index-BHy2DZX-.js +67 -0
  139. package/core/built/admin/assets/index-Ds2iUoF6.css +1 -0
  140. package/core/built/admin/assets/index.es-B9tvf-t4.js +1 -0
  141. package/core/built/admin/assets/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
  142. package/core/built/admin/assets/input-group-6vEPvX9S.js +1 -0
  143. package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.js} +1 -1
  144. package/core/built/admin/assets/integration-header-B4DyXV06.js +1 -0
  145. package/core/built/admin/assets/integrations-BT6_iJvu.js +1 -0
  146. package/core/built/admin/assets/invite-user-modal-5xDCEw90.js +1 -0
  147. package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
  148. package/core/built/admin/assets/koenig-editor-base-BWLEB9Vf.js +1 -0
  149. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9535 -9527
  150. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
  151. package/core/built/admin/assets/{koenig-lexical-f8vHRYvz.js → koenig-lexical-Cj62r_y9.js} +86 -86
  152. package/core/built/admin/assets/{kpi-card-DSap9rAy.js → kpi-card-DOUnkZcN.js} +1 -1
  153. package/core/built/admin/assets/{kpi-card-DU0rn9Cj.js → kpi-card-IHDo8Q56.js} +1 -1
  154. package/core/built/admin/assets/{kpi-helpers-Dz33AV6h.js → kpi-helpers-CjUdECAK.js} +1 -1
  155. package/core/built/admin/assets/{kpi-tabs-BtTTrKvJ.js → kpi-tabs-X3DkOYbI.js} +1 -1
  156. package/core/built/admin/assets/{label-DS8Mi6G5.js → label-Dq6QaOuS.js} +1 -1
  157. package/core/built/admin/assets/label-picker-8YlhS2U3.js +1 -0
  158. package/core/built/admin/assets/labels-DAPjKPy2.js +1 -0
  159. package/core/built/admin/assets/layout-TMj4P-vv.js +1 -0
  160. package/core/built/admin/assets/limit-modal-BRPsit_B.js +1 -0
  161. package/core/built/admin/assets/link-helpers-Bzkk68GD.js +1 -0
  162. package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
  163. package/core/built/admin/assets/member-custom-fields-CmvsuuV_.js +1 -0
  164. package/core/built/admin/assets/member-detail-DCvRrkmJ.js +1 -0
  165. package/core/built/admin/assets/members-DxbSKngD.js +11 -0
  166. package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
  167. package/core/built/admin/assets/{moderation-DySbgyUS.js → moderation-Da5lhhWh.js} +1 -1
  168. package/core/built/admin/assets/multi-select-combobox-CYdkH7KQ.js +1 -0
  169. package/core/built/admin/assets/navigation-modal-BIoTwtMy.js +1 -0
  170. package/core/built/admin/assets/newsletter-BptlVoJm.js +1 -0
  171. package/core/built/admin/assets/newsletter-detail-modal-BIvxqE0N.js +1 -0
  172. package/core/built/admin/assets/newsletter-emails-Bf8IZLsP.js +1 -0
  173. package/core/built/admin/assets/newsletters-BgrIZk0V.js +1 -0
  174. package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
  175. package/core/built/admin/assets/{note-C8kxrz6w.js → note-DYcLOmDb.js} +1 -1
  176. package/core/built/admin/assets/{notifications-CgtSfX7t.js → notifications-B2SPFIV9.js} +1 -1
  177. package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
  178. package/core/built/admin/assets/offers-container-modal-BZnZITMw.js +1 -0
  179. package/core/built/admin/assets/{onboarding-route-4qtziA9d.js → onboarding-route-DgdfxkQh.js} +1 -1
  180. package/core/built/admin/assets/overview-BYQJXSog.js +1 -0
  181. package/core/built/admin/assets/overview-Dt2_mR6U.js +1 -0
  182. package/core/built/admin/assets/{page-header-B3Wb9Jqv.js → page-header-CpmxJj5B.js} +1 -1
  183. package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
  184. package/core/built/admin/assets/pintura-modal-B4MuBuuO.js +1 -0
  185. package/core/built/admin/assets/portal-frame-BF0NBIji.js +1 -0
  186. package/core/built/admin/assets/portal-modal-DMVbm0oy.js +1 -0
  187. package/core/built/admin/assets/{post-analytics-We-ko5Cb.js → post-analytics-CXWBOGyP.js} +1 -1
  188. package/core/built/admin/assets/post-analytics-header-ChzjXVPt.js +1 -0
  189. package/core/built/admin/assets/{post-analytics-provider-B6UynTGb.js → post-analytics-provider-Cgw4U_fw.js} +1 -1
  190. package/core/built/admin/assets/{post-share-modal-DFfO93_0.js → post-share-modal-B9Vmk_XM.js} +1 -1
  191. package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
  192. package/core/built/admin/assets/preferences-BfzJmnsd.js +5 -0
  193. package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
  194. package/core/built/admin/assets/preview-modal-Dw_HtD6S.js +1 -0
  195. package/core/built/admin/assets/profile-BT5vtv9b.js +1 -0
  196. package/core/built/admin/assets/radio-group-I7ou7PoJ.js +1 -0
  197. package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
  198. package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-DSQfprxH.js} +1 -1
  199. package/core/built/admin/assets/select-D9oU1IoM.js +1 -0
  200. package/core/built/admin/assets/settings-CfZFbyu_.js +4 -0
  201. package/core/built/admin/assets/{settings-WXNeWyU1.js → settings-CpvHWix7.js} +1 -1
  202. package/core/built/admin/assets/settings-breadcrumbs-K0USP2nw.js +1 -0
  203. package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
  204. package/core/built/admin/assets/{share-modal-DsgW3wbg.js → share-modal-ClIyPzc5.js} +1 -1
  205. package/core/built/admin/assets/slack-modal-1XXqOfiw.js +1 -0
  206. package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
  207. package/core/built/admin/assets/{sort-button-Bb_FCl7e.js → sort-button-CdBsm40v.js} +1 -1
  208. package/core/built/admin/assets/{source-icon-BeaRxUn9.js → source-icon-CEYmVLXR.js} +1 -1
  209. package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
  210. package/core/built/admin/assets/{stats-Bjj_kmJ1.js → stats-D8nZCTy5.js} +1 -1
  211. package/core/built/admin/assets/{stats-view-BxPbW6K1.js → stats-view-CugFVf0g.js} +1 -1
  212. package/core/built/admin/assets/{step-1-B1e3-1F8.js → step-1-DOYjrORn.js} +1 -1
  213. package/core/built/admin/assets/{step-2-C5WaeBJo.js → step-2-vA6RsarH.js} +1 -1
  214. package/core/built/admin/assets/{step-3-B46iVPwu.js → step-3-BtIBxMId.js} +2 -2
  215. package/core/built/admin/assets/stripe-button-UFP4QRXd.js +1 -0
  216. package/core/built/admin/assets/stripe-connect-modal-C_L3uM6n.js +1 -0
  217. package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
  218. package/core/built/admin/assets/{table-CB-4_HBJ.js → table-TMCgCQK7.js} +1 -1
  219. package/core/built/admin/assets/tabs-Dd6oOKbv.js +1 -0
  220. package/core/built/admin/assets/tag-detail-D3l-UJPR.js +1 -0
  221. package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
  222. package/core/built/admin/assets/tags-WdKQGWR8.js +1 -0
  223. package/core/built/admin/assets/testimonials-modal-CqWp-xXJ.js +1 -0
  224. package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
  225. package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
  226. package/core/built/admin/assets/tier-detail-modal-BkcR2u0A.js +1 -0
  227. package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
  228. package/core/built/admin/assets/toggle-group-Guorw93z.js +1 -0
  229. package/core/built/admin/assets/{topic-filter-DAFT87tQ.js → topic-filter-CbgoWIHn.js} +1 -1
  230. package/core/built/admin/assets/transistor-modal-DuNnnpal.js +1 -0
  231. package/core/built/admin/assets/unsplash-modal-y2bVwTHH.js +1 -0
  232. package/core/built/admin/assets/{use-analytics-data-S0FTd43s.js → use-analytics-data-B-b4wNSC.js} +1 -1
  233. package/core/built/admin/assets/use-check-theme-limit-error-m09J38Mq.js +2 -0
  234. package/core/built/admin/assets/use-feature-flag-ud0Qz8vt.js +1 -0
  235. package/core/built/admin/assets/{use-filter-params-Gpoq2E1L.js → use-filter-params-BnbBDc7T.js} +1 -1
  236. package/core/built/admin/assets/use-filterable-api-iMpDZBxD.js +1 -0
  237. package/core/built/admin/assets/use-form-vRjI0Kwk.js +1 -0
  238. package/core/built/admin/assets/{use-growth-stats-CIaiHOVb.js → use-growth-stats-DPU5rt3W.js} +1 -1
  239. package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
  240. package/core/built/admin/assets/use-limiter-BkN3E4gw.js +2 -0
  241. package/core/built/admin/assets/use-pintura-config-C_z8HTG2.js +1 -0
  242. package/core/built/admin/assets/use-pintura-editor-DNgbkhGp.js +1 -0
  243. package/core/built/admin/assets/{use-post-referrers-Cq2vBPrA.js → use-post-referrers-C2mXyrp-.js} +1 -1
  244. package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
  245. package/core/built/admin/assets/{use-reply-chain-data-DBDRtGu-.js → use-reply-chain-data-DaK8q2qQ.js} +1 -1
  246. package/core/built/admin/assets/use-setting-group-YlYcoNyP.js +1 -0
  247. package/core/built/admin/assets/use-settings-navigation-Bf1g1rEh.js +1 -0
  248. package/core/built/admin/assets/{use-simple-pagination-CjYNxdwe.js → use-simple-pagination-D9ckKo5-.js} +1 -1
  249. package/core/built/admin/assets/{use-tinybird-query-falHAtuK.js → use-tinybird-query-GSa32hVw.js} +1 -1
  250. package/core/built/admin/assets/{use-tinybird-token-D7-8ND1y.js → use-tinybird-token-CKLOZFsw.js} +1 -1
  251. package/core/built/admin/assets/use-url-input-z-p0Kewm.js +5 -0
  252. package/core/built/admin/assets/user-detail-modal-BMxOA4Sv.js +1 -0
  253. package/core/built/admin/assets/{users-Dkv8NQZe.js → users-B6hIds6n.js} +1 -1
  254. package/core/built/admin/assets/validator-Cr0sMKuW.js +7 -0
  255. package/core/built/admin/assets/{virtual-list-CvnC9zF2.js → virtual-list-RHeEL05L.js} +1 -1
  256. package/core/built/admin/assets/{web-jFYWCKDn.js → web-Bw2QMO5u.js} +2 -2
  257. package/core/built/admin/assets/web-xJtcwMy4.js +1 -0
  258. package/core/built/admin/assets/zapier-modal-BP2f4-vY.js +1 -0
  259. package/core/built/admin/index.html +20 -18
  260. package/core/frontend/helpers/authors.js +1 -1
  261. package/core/frontend/helpers/link.js +3 -8
  262. package/core/frontend/helpers/tags.js +1 -1
  263. package/core/frontend/meta/author-url.js +2 -2
  264. package/core/frontend/meta/url.js +4 -4
  265. package/core/frontend/services/data/fetch-data.js +10 -5
  266. package/core/frontend/services/proxy.js +1 -1
  267. package/core/frontend/services/routing/api-adapter.js +115 -0
  268. package/core/frontend/services/routing/api-adapter.ts +157 -0
  269. package/core/frontend/services/routing/collection-router.js +5 -3
  270. package/core/frontend/services/routing/controllers/index.js +1 -1
  271. package/core/frontend/services/routing/controllers/rss.js +26 -31
  272. package/core/frontend/services/routing/controllers/rss.ts +66 -0
  273. package/core/frontend/services/routing/controllers/static.js +8 -7
  274. package/core/frontend/services/routing/events.js +20 -6
  275. package/core/frontend/services/routing/parent-router.js +17 -4
  276. package/core/frontend/services/routing/permalink-adapter.js +13 -0
  277. package/core/frontend/services/routing/permalink-adapter.ts +10 -0
  278. package/core/frontend/services/routing/registry.js +59 -33
  279. package/core/frontend/services/routing/router-manager.js +24 -22
  280. package/core/frontend/services/routing/static-routes-router.js +4 -4
  281. package/core/frontend/services/routing/taxonomy-router.js +6 -2
  282. package/core/frontend/services/sitemap/base-site-map-generator.js +1 -28
  283. package/core/frontend/services/sitemap/site-map-manager.js +22 -57
  284. package/core/server/adapters/route-settings/utils.js +10 -2
  285. package/core/server/adapters/route-settings/utils.ts +12 -2
  286. package/core/server/api/endpoints/email-post.js +1 -1
  287. package/core/server/api/endpoints/previews.js +1 -1
  288. package/core/server/api/endpoints/search-index-public.js +3 -3
  289. package/core/server/api/endpoints/search-index.js +4 -4
  290. package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -2
  291. package/core/server/api/endpoints/utils/serializers/input/comments.js +1 -1
  292. package/core/server/api/endpoints/utils/serializers/input/pages.js +3 -3
  293. package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
  294. package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
  295. package/core/server/api/endpoints/utils/serializers/input/users.js +2 -2
  296. package/core/server/api/endpoints/utils/serializers/input/utils/url.js +31 -13
  297. package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +2 -2
  298. package/core/server/api/endpoints/utils/serializers/output/mappers/tags.js +2 -2
  299. package/core/server/api/endpoints/utils/serializers/output/mappers/users.js +8 -0
  300. package/core/server/api/endpoints/utils/serializers/output/utils/url.js +18 -11
  301. package/core/server/data/migrations/versions/6.56/2026-08-03-19-27-51-add-automation-run-step-id-to-automated-email-recipients.js +8 -0
  302. package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
  303. package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +74 -0
  304. package/core/server/data/migrations/versions/6.57/2026-08-04-18-54-14-reset-automation-email-analytics.js +25 -0
  305. package/core/server/data/schema/commands.js +21 -5
  306. package/core/server/data/schema/fixtures/fixtures.json +2 -2
  307. package/core/server/data/schema/schema.js +25 -5
  308. package/core/server/lib/common/to-plain.ts +1 -1
  309. package/core/server/models/post.js +12 -3
  310. package/core/server/services/adapter-manager/adapter-manager.js +1 -45
  311. package/core/server/services/adapter-manager/adapter-manager.ts +2 -48
  312. package/core/server/services/adapter-manager/adapter-paths.js +24 -0
  313. package/core/server/services/adapter-manager/adapter-paths.ts +21 -0
  314. package/core/server/services/adapter-manager/base-classes.js +22 -0
  315. package/core/server/services/adapter-manager/base-classes.ts +22 -0
  316. package/core/server/services/adapter-manager/index.js +4 -27
  317. package/core/server/services/adapter-manager/index.ts +4 -31
  318. package/core/server/services/adapter-manager/utils.js +50 -0
  319. package/core/server/services/adapter-manager/utils.ts +57 -0
  320. package/core/server/services/audience-feedback/audience-feedback-controller.js +1 -1
  321. package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
  322. package/core/server/services/automations/automations-repository.ts +3 -1
  323. package/core/server/services/automations/database-automations-repository.js +33 -14
  324. package/core/server/services/automations/database-automations-repository.ts +41 -15
  325. package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
  326. package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
  327. package/core/server/services/automations/poll.js +3 -1
  328. package/core/server/services/automations/poll.ts +4 -1
  329. package/core/server/services/automations/service.js +7 -3
  330. package/core/server/services/automations/service.ts +7 -3
  331. package/core/server/services/comments/comments-service-emails.js +1 -1
  332. package/core/server/services/comments/comments-service.js +1 -1
  333. package/core/server/services/email-analytics/email-analytics-service-wrapper.js +11 -2
  334. package/core/server/services/email-analytics/email-analytics-service.js +1 -1
  335. package/core/server/services/email-service/email-service-wrapper.js +2 -1
  336. package/core/server/services/email-service/mailgun-email-provider.js +15 -0
  337. package/core/server/services/gift-links/CONTEXT.md +9 -0
  338. package/core/server/services/gifts/CONTEXT.md +55 -0
  339. package/core/server/services/gifts/README.md +28 -0
  340. package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
  341. package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
  342. package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
  343. package/core/server/services/gifts/gift-checkout-offer.js +74 -0
  344. package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
  345. package/core/server/services/gifts/gift-codec.js +63 -0
  346. package/core/server/services/gifts/gift-codec.ts +62 -0
  347. package/core/server/services/gifts/gift-controller.js +6 -31
  348. package/core/server/services/gifts/gift-controller.ts +6 -69
  349. package/core/server/services/gifts/gift-email-service.ts +3 -2
  350. package/core/server/services/gifts/gift-schema.js +34 -0
  351. package/core/server/services/gifts/gift-schema.ts +39 -0
  352. package/core/server/services/gifts/gift-service-wrapper.js +13 -11
  353. package/core/server/services/gifts/gift-service.js +265 -52
  354. package/core/server/services/gifts/gift-service.ts +431 -97
  355. package/core/server/services/gifts/gift.ts +16 -39
  356. package/core/server/services/indexnow-ping/indexnow-ping-service.js +1 -1
  357. package/core/server/services/indexnow-ping/indexnow-ping-service.ts +2 -4
  358. package/core/server/services/lib/mailgun-client.js +3 -3
  359. package/core/server/services/link-tracking/link-click-repository.js +6 -1
  360. package/core/server/services/link-tracking/link-click-tracking-service.js +12 -3
  361. package/core/server/services/media-inliner/external-media-inliner.js +12 -8
  362. package/core/server/services/member-attribution/url-translator.js +5 -7
  363. package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +4 -3
  364. package/core/server/services/member-welcome-emails/service.js +79 -15
  365. package/core/server/services/members/api.js +2 -3
  366. package/core/server/services/members/import-export/import/completion-email.js +8 -4
  367. package/core/server/services/members/import-export/import/completion-email.ts +9 -4
  368. package/core/server/services/members/import-export/import/importer.js +25 -4
  369. package/core/server/services/members/import-export/import/importer.ts +27 -6
  370. package/core/server/services/members/import-export/import/row.ts +5 -1
  371. package/core/server/services/members/import-export/index.js +6 -2
  372. package/core/server/services/members/import-export/index.ts +10 -4
  373. package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
  374. package/core/server/services/members/members-api/members-api.js +13 -7
  375. package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
  376. package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
  377. package/core/server/services/members/members-api/services/payments-service.js +6 -68
  378. package/core/server/services/members-custom-fields/queries.js +4 -13
  379. package/core/server/services/members-custom-fields/queries.ts +4 -13
  380. package/core/server/services/members-custom-fields/schema.js +19 -21
  381. package/core/server/services/members-custom-fields/schema.ts +18 -20
  382. package/core/server/services/members-custom-fields/storage.js +94 -27
  383. package/core/server/services/members-custom-fields/storage.ts +114 -39
  384. package/core/server/services/members-custom-fields/values-service.js +98 -103
  385. package/core/server/services/members-custom-fields/values-service.ts +116 -114
  386. package/core/server/services/mentions/resource-service.js +1 -1
  387. package/core/server/services/mentions/service.js +2 -2
  388. package/core/server/services/oembed/oembed-service.js +44 -3
  389. package/core/server/services/posts/posts-service.js +11 -0
  390. package/core/server/services/route-settings/dynamic-routing-service.js +10 -62
  391. package/core/server/services/route-settings/route-settings-parser.js +25 -25
  392. package/core/server/services/route-settings/route-settings-parser.ts +27 -27
  393. package/core/server/services/route-settings/validation-errors.js +68 -4
  394. package/core/server/services/route-settings/validation-errors.ts +79 -4
  395. package/core/server/services/slack-ping/slack-ping-service.js +2 -2
  396. package/core/server/services/slack-ping/slack-ping-service.ts +3 -5
  397. package/core/server/services/stats/content-stats-service.js +3 -3
  398. package/core/server/services/stats/posts-stats-service.js +29 -25
  399. package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
  400. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
  401. package/core/server/services/stripe/stripe-api.js +10 -0
  402. package/core/server/services/url/config.js +90 -135
  403. package/core/server/services/url/index.js +6 -49
  404. package/core/server/services/url/lazy-find-resource.js +14 -11
  405. package/core/server/services/url/lazy-find-resource.ts +14 -11
  406. package/core/server/services/url/lazy-url-service.js +174 -94
  407. package/core/server/services/url/lazy-url-service.ts +233 -97
  408. package/core/server/services/url/routable-resources.js +62 -71
  409. package/core/server/services/url/router-filter.js +12 -4
  410. package/core/server/services/url/router-filter.ts +12 -4
  411. package/core/server/services/webhooks/index.js +1 -1
  412. package/core/server/services/webhooks/serialize.js +2 -1
  413. package/core/server/web/gift-preview/controller.js +10 -25
  414. package/core/shared/config/env/config.testing-mysql.json +1 -2
  415. package/core/shared/config/env/config.testing.json +1 -2
  416. package/core/shared/events/index.js +1 -2
  417. package/core/shared/labs.js +4 -4
  418. package/package.json +10 -10
  419. package/pnpm-lock.yaml +27 -20
  420. package/pnpm-workspace.yaml +3 -2
  421. package/tsconfig.json +1 -0
  422. package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
  423. package/core/built/admin/assets/Combination-CJG7Flp-.js +0 -41
  424. package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +0 -1
  425. package/core/built/admin/assets/automated-emails-DRy7qY-J.js +0 -7
  426. package/core/built/admin/assets/checkbox-CWa28lgO.js +0 -1
  427. package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +0 -25
  428. package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +0 -27
  429. package/core/built/admin/assets/code-editor-view-CCr0rBWS.js +0 -1
  430. package/core/built/admin/assets/comments-DyUSMUdj.js +0 -1
  431. package/core/built/admin/assets/disabled-sources-indicator-CUxMfiPa.js +0 -1
  432. package/core/built/admin/assets/dist-2Tnz2Yfq.js +0 -1
  433. package/core/built/admin/assets/dist-CjY-f5Z1.js +0 -1
  434. package/core/built/admin/assets/dist-DDY6GRVz.js +0 -1
  435. package/core/built/admin/assets/dist-DG78yroX.js +0 -23
  436. package/core/built/admin/assets/domain-C0bV2UV8.js +0 -1
  437. package/core/built/admin/assets/editor-Dp865O-4.js +0 -8
  438. package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
  439. package/core/built/admin/assets/filter-sources-BV2rnpfA.js +0 -1
  440. package/core/built/admin/assets/filters-CUXJ2Nm9.js +0 -1
  441. package/core/built/admin/assets/filters-DfbIWc_l.js +0 -1
  442. package/core/built/admin/assets/index-9Cw8z3rA.css +0 -1
  443. package/core/built/admin/assets/index-BGG7NckO.js +0 -67
  444. package/core/built/admin/assets/input-group-BSgG_lVw.js +0 -1
  445. package/core/built/admin/assets/label-picker-1DvSuLA1.js +0 -1
  446. package/core/built/admin/assets/labels-CDSUt4J_.js +0 -1
  447. package/core/built/admin/assets/layout-yQEi2qDM.js +0 -1
  448. package/core/built/admin/assets/link-helpers-7EYqvvHs.js +0 -1
  449. package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +0 -1
  450. package/core/built/admin/assets/member-detail-BAq7gDaa.js +0 -1
  451. package/core/built/admin/assets/members-CQpVFJu4.js +0 -10
  452. package/core/built/admin/assets/modals-ReORvWnu.js +0 -31
  453. package/core/built/admin/assets/newsletter-CJQvtHqN.js +0 -1
  454. package/core/built/admin/assets/newsletters-B3QQXfGT.js +0 -1
  455. package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
  456. package/core/built/admin/assets/overview-Bf2sbznq.js +0 -1
  457. package/core/built/admin/assets/overview-CBfePxcr.js +0 -1
  458. package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +0 -1
  459. package/core/built/admin/assets/preferences-DJPfHEGu.js +0 -5
  460. package/core/built/admin/assets/profile-DqBQp-gT.js +0 -1
  461. package/core/built/admin/assets/select-C5_xEt5b.js +0 -1
  462. package/core/built/admin/assets/settings-BwiVIXau.js +0 -4
  463. package/core/built/admin/assets/social-urls-BQPFEk1_.js +0 -8
  464. package/core/built/admin/assets/switch-z5Y6Uzt6.js +0 -1
  465. package/core/built/admin/assets/tabs-CCEgKYzq.js +0 -1
  466. package/core/built/admin/assets/tags-Cg2VJGmG.js +0 -1
  467. package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
  468. package/core/built/admin/assets/toggle-group-b3IlxvhV.js +0 -1
  469. package/core/built/admin/assets/web-B_5GZRvH.js +0 -1
  470. package/core/server/services/gifts/gift-repository.js +0 -2
  471. package/core/server/services/gifts/gift-repository.ts +0 -29
  472. package/core/server/services/route-settings/activation-bridge.js +0 -174
  473. package/core/server/services/route-settings/activation-bridge.ts +0 -250
  474. package/core/server/services/url/local-file-cache.js +0 -75
  475. package/core/server/services/url/queue.js +0 -260
  476. package/core/server/services/url/resource.js +0 -102
  477. package/core/server/services/url/resources.js +0 -480
  478. package/core/server/services/url/url-generator.js +0 -276
  479. package/core/server/services/url/url-service-facade.js +0 -366
  480. package/core/server/services/url/url-service-facade.ts +0 -519
  481. package/core/server/services/url/url-service.js +0 -387
  482. package/core/server/services/url/urls.js +0 -140
  483. package/core/shared/events/url-resource-updated-event.js +0 -33
@@ -1,519 +0,0 @@
1
- /**
2
- * UrlServiceFacade
3
- *
4
- * Sits in front of the URL service so callers can use a stable, resource-based
5
- * interface regardless of the underlying implementation. The facade can be
6
- * built with two backends:
7
- *
8
- * - urlService: the legacy eager UrlService that precomputes a full
9
- * resource → URL map at boot.
10
- * - lazyUrlService: an on-demand implementation (LazyUrlService) that
11
- * computes URLs and ownership per call.
12
- *
13
- * When `lazyUrlService` is provided the facade routes calls to it; otherwise
14
- * it delegates to the eager `urlService`. This lets the lazy implementation be
15
- * swapped in behind a config flag without touching individual callers.
16
- */
17
-
18
- const _ = require('lodash');
19
- const logging = require('@tryghost/logging');
20
- const errors = require('@tryghost/errors');
21
-
22
- /**
23
- * Routing-level resource. `type` is one of the plural router keys
24
- * ('posts', 'pages', 'tags', 'authors'). Concrete records carry additional
25
- * fields (slug, published_at, primary_tag, ...) used by permalink templates.
26
- *
27
- * `id` is required: the eager backend's URL lookup is id-based, so a missing
28
- * `id` would silently return `/404/`. Every in-tree caller already passes an
29
- * id; the type makes that mandatory.
30
- */
31
- export interface Resource {
32
- type: string;
33
- id: string;
34
- [key: string]: unknown;
35
- }
36
-
37
- export interface UrlOptions {
38
- absolute?: boolean;
39
- withSubdirectory?: boolean;
40
- // Bulk callers (the sitemap index build) set this: enumeration parity is
41
- // covered by the getRoutableResources id-set comparison and per-URL
42
- // parity by organic request traffic, so teeing hundreds of thousands of
43
- // rows per rebuild would only capture stacks and recompute for nothing.
44
- // Dies with compare mode.
45
- skipComparison?: boolean;
46
- // Diagnostic only: the producing api endpoint (docName/method/apiType),
47
- // threaded from the output serializer so a compare mismatch or thin-resource
48
- // throw names its producer in the logs. The compare-log caller stack
49
- // truncates at the async api-framework boundary, so the stack alone can't.
50
- // Ignored by both backends; only read into the compare context.
51
- serializerContext?: {apiType?: string; docName?: string; method?: string};
52
- }
53
-
54
- /**
55
- * Eager UrlService's resource envelope: `{config: {type}, data: {...}}`.
56
- * Returned by `getResource(path)`. The `data` field carries the raw record;
57
- * `id` is always present because the eager URL service keys its in-memory
58
- * map by id, and the type makes that contract explicit.
59
- */
60
- export interface LegacyResourceEnvelope {
61
- config: {type: string; [key: string]: unknown};
62
- data: {id: string; [key: string]: unknown};
63
- }
64
-
65
- /**
66
- * Surface of the eager UrlService that the facade depends on. The full class
67
- * has many more methods; only those used here need typing.
68
- */
69
- export interface EagerUrlService {
70
- getUrlByResourceId(id: string, options?: UrlOptions): string;
71
- owns(routerId: string, id: string): boolean;
72
- getResource(path: string): LegacyResourceEnvelope | null;
73
- hasFinished(): boolean;
74
- onRouterAddedType(...args: unknown[]): unknown;
75
- onRouterUpdated(...args: unknown[]): unknown;
76
- // undefined until the eager cache has initialised the type: the compiler
77
- // enforces the guard at the read site.
78
- resources: {getAllByType(type: string): Array<{data: Record<string, unknown>}> | undefined};
79
- }
80
-
81
- /**
82
- * On-demand enumeration of the routable rows of a type — the lazy
83
- * counterpart of the eager service's in-memory cache. Built by
84
- * `createFetchRoutableResources` (routable-resources.js).
85
- */
86
- export type FetchRoutableResources = (
87
- type: string,
88
- options?: {columns?: string[]}
89
- ) => Promise<Array<Record<string, unknown>>>;
90
-
91
- export interface LazyUrlServiceBackend {
92
- getUrlForResource(resource: Resource, options?: UrlOptions): string;
93
- ownsResource(routerId: string, resource: Resource): boolean;
94
- resolveUrl(path: string): Promise<Resource | null>;
95
- getRequiredRelations(): string[];
96
- getRequiredFields(routerType: string): string[];
97
- hasFinished(): boolean;
98
- onRouterAddedType(...args: unknown[]): unknown;
99
- onRouterUpdated(...args: unknown[]): unknown;
100
- reset(): void;
101
- }
102
-
103
- export class UrlServiceFacade {
104
- private urlService: EagerUrlService;
105
- private lazyUrlService: LazyUrlServiceBackend | null;
106
- private compare: boolean;
107
- private fetchRoutableResources: FetchRoutableResources | null;
108
- private enumComparesInFlight: Set<string>;
109
-
110
- constructor({
111
- urlService,
112
- lazyUrlService = null,
113
- compare = false,
114
- fetchRoutableResources = null
115
- }: {
116
- urlService: EagerUrlService;
117
- lazyUrlService?: LazyUrlServiceBackend | null;
118
- compare?: boolean;
119
- fetchRoutableResources?: FetchRoutableResources | null;
120
- }) {
121
- this.urlService = urlService;
122
- this.lazyUrlService = lazyUrlService;
123
- this.compare = compare;
124
- this.fetchRoutableResources = fetchRoutableResources;
125
- this.enumComparesInFlight = new Set();
126
- }
127
-
128
- isLazy(): boolean {
129
- return !!this.lazyUrlService && !this.compare;
130
- }
131
-
132
- isComparing(): boolean {
133
- return this.compare && !!this.lazyUrlService;
134
- }
135
-
136
-
137
- /**
138
- * The full resource record is required: the lazy backend evaluates NQL
139
- * filters and applies permalink templates against it.
140
- */
141
- getUrlForResource(resource: Resource, options?: UrlOptions): string {
142
- if (this.isLazy()) {
143
- return this.lazyUrlService!.getUrlForResource(resource, options);
144
- }
145
- const url = this.urlService.getUrlByResourceId(resource.id, options);
146
- if (this.isComparing() && !options?.skipComparison) {
147
- const context = this._compareContext(resource,
148
- options?.serializerContext ? {serializer: options.serializerContext} : {});
149
- // Snapshot: callers mutate the resource's nested objects in place
150
- // after this returns, but the comparison runs later via setImmediate.
151
- const snapshot = _.cloneDeep(resource);
152
- setImmediate(() => this._compare('getUrlForResource', url,
153
- () => this.lazyUrlService!.getUrlForResource(snapshot, options),
154
- context));
155
- }
156
- return url;
157
- }
158
-
159
- /**
160
- * All routable rows of a type. Eager answers from its in-memory cache;
161
- * lazy fetches from the database on demand. In compare mode the eager
162
- * answer is returned and the lazy fetch runs in the background, with any
163
- * id-set divergence logged — counts and id samples only, never row
164
- * bodies (a large site has hundreds of thousands).
165
- */
166
- async getRoutableResources(type: string, options: {columns?: string[]} = {}): Promise<Array<Record<string, unknown>>> {
167
- if (this.isLazy()) {
168
- if (!this.fetchRoutableResources) {
169
- throw new errors.IncorrectUsageError({
170
- message: 'getRoutableResources requires an injected fetchRoutableResources in lazy mode'
171
- });
172
- }
173
- return this.fetchRoutableResources(type, options);
174
- }
175
-
176
- const eagerRows = (this.urlService.resources.getAllByType(type) || []).map(resource => resource.data);
177
-
178
- // Single-flight per type: rapid invalidation cycles must not stack
179
- // concurrent full-table comparison walks.
180
- if (this.isComparing() && this.fetchRoutableResources && !this.enumComparesInFlight.has(type)) {
181
- this.enumComparesInFlight.add(type);
182
- void this._compareRoutableResources(type, options, eagerRows).finally(() => {
183
- this.enumComparesInFlight.delete(type);
184
- });
185
- }
186
- return eagerRows;
187
- }
188
-
189
- private async _compareRoutableResources(
190
- type: string,
191
- options: {columns?: string[]},
192
- eagerRows: Array<Record<string, unknown>>
193
- ): Promise<void> {
194
- let lazyRows;
195
- try {
196
- lazyRows = await this.fetchRoutableResources!(type, options);
197
- } catch (err) {
198
- this._reportLazyError('getRoutableResources', err as Error, {type});
199
- return;
200
- }
201
-
202
- const eagerIds = new Set(eagerRows.map(row => row.id));
203
- const lazyIds = new Set(lazyRows.map(row => row.id));
204
- const missingFromLazy = [...eagerIds].filter(id => !lazyIds.has(id));
205
- const extraInLazy = [...lazyIds].filter(id => !eagerIds.has(id));
206
-
207
- if (!missingFromLazy.length && !extraInLazy.length) {
208
- return;
209
- }
210
- this._report(new errors.InternalServerError({
211
- message: 'URL service parity mismatch',
212
- code: 'LAZY_URL_PARITY_MISMATCH',
213
- errorDetails: {
214
- method: 'getRoutableResources',
215
- type,
216
- eagerCount: eagerIds.size,
217
- lazyCount: lazyIds.size,
218
- missingFromLazy: missingFromLazy.slice(0, 10),
219
- extraInLazy: extraInLazy.slice(0, 10)
220
- }
221
- }));
222
- }
223
-
224
- ownsResource(routerIdentifier: string, resource: Resource): boolean {
225
- if (this.isLazy()) {
226
- return this.lazyUrlService!.ownsResource(routerIdentifier, resource);
227
- }
228
- const owns = this.urlService.owns(routerIdentifier, resource.id);
229
- if (this.isComparing()) {
230
- const context = this._compareContext(resource, {routerIdentifier});
231
- // Snapshot, as in getUrlForResource.
232
- const snapshot = _.cloneDeep(resource);
233
- setImmediate(() => this._compare('ownsResource', owns,
234
- () => this.lazyUrlService!.ownsResource(routerIdentifier, snapshot),
235
- context));
236
- }
237
- return owns;
238
- }
239
-
240
- // Context for a compare report. Must be built synchronously in the calling
241
- // frame: the comparison itself runs from setImmediate, where the caller's
242
- // stack is gone — so a lazy throw reported there names only the URL service
243
- // internals, not which caller handed over the (possibly thin) resource.
244
- // `caller` recaptures those frames; `resourceKeys` fingerprints the shape
245
- // the caller passed (e.g. a Content-API-serialized post vs a full model).
246
- private _compareContext(resource: Resource, extra: Record<string, unknown> = {}): Record<string, unknown> {
247
- const caller: {stack?: string} = {};
248
- Error.captureStackTrace(caller, this._compareContext);
249
- return {
250
- type: resource.type,
251
- id: resource.id,
252
- status: (resource as Record<string, unknown>).status,
253
- resourceKeys: Object.keys(resource),
254
- caller: caller.stack,
255
- ...extra
256
- };
257
- }
258
-
259
- /**
260
- * Reverse URL lookup. Returns a flat resource shape (e.g. `{type, id, slug}`)
261
- * rather than the legacy `{config: {type}, data: {...}}` envelope. Async to
262
- * match the lazy implementation's contract.
263
- */
264
- async resolveUrl(urlPath: string): Promise<Resource | null> {
265
- if (this.isLazy()) {
266
- return this.lazyUrlService!.resolveUrl(urlPath);
267
- }
268
- const resource = this.urlService.getResource(urlPath);
269
- // The routing-level type ('posts', 'pages', 'tags', 'authors') wins
270
- // over any DB type field on resource.data so the flat Resource is
271
- // unambiguous.
272
- const eagerResult = resource
273
- ? Object.assign({}, resource.data, {type: resource.config.type}) as Resource
274
- : null;
275
- if (this.isComparing()) {
276
- // Fire-and-forget: don't await lazy so the reverse lookup adds no
277
- // latency for its callers; the lazy DB read runs in the background.
278
- // Snapshot the eager result, as in getUrlForResource.
279
- const eagerSnapshot = _.cloneDeep(eagerResult);
280
- void this._compareAsync('resolveUrl', eagerSnapshot,
281
- () => this.lazyUrlService!.resolveUrl(urlPath),
282
- {path: urlPath},
283
- (a, b) => this._resolvesToSameResource(a, b));
284
- }
285
- return eagerResult;
286
- }
287
-
288
- // Returns [] when there is no lazy backend: eager looks URLs up by id and
289
- // never touches a resource's relations.
290
- getRequiredRelations(): string[] {
291
- if (this.lazyUrlService) {
292
- return this.lazyUrlService.getRequiredRelations();
293
- }
294
- return [];
295
- }
296
-
297
- // Columns a resource of this type must carry for the lazy backend to build
298
- // its URL. [] with no lazy backend: eager looks URLs up by id and never
299
- // reads these fields.
300
- getRequiredFields(routerType: string): string[] {
301
- if (this.lazyUrlService) {
302
- return this.lazyUrlService.getRequiredFields(routerType);
303
- }
304
- return [];
305
- }
306
-
307
- hasFinished(): boolean {
308
- if (this.isLazy()) {
309
- return this.lazyUrlService!.hasFinished();
310
- }
311
- // Track eager when comparing: lazy always reports ready and would gate traffic in early.
312
- return this.urlService.hasFinished();
313
- }
314
-
315
- // While comparing, register on both so lazy sees the same routers as eager.
316
- onRouterAddedType(...args: unknown[]): unknown {
317
- if (this.isComparing()) {
318
- this._runLazyHook('onRouterAddedType', () => this.lazyUrlService!.onRouterAddedType(...args));
319
- return this.urlService.onRouterAddedType(...args);
320
- }
321
- if (this.lazyUrlService) {
322
- return this.lazyUrlService.onRouterAddedType(...args);
323
- }
324
- return this.urlService.onRouterAddedType(...args);
325
- }
326
-
327
- onRouterUpdated(...args: unknown[]): unknown {
328
- if (this.isComparing()) {
329
- this._runLazyHook('onRouterUpdated', () => this.lazyUrlService!.onRouterUpdated(...args));
330
- return this.urlService.onRouterUpdated(...args);
331
- }
332
- if (this.lazyUrlService) {
333
- return this.lazyUrlService.onRouterUpdated(...args);
334
- }
335
- return this.urlService.onRouterUpdated(...args);
336
- }
337
-
338
- /**
339
- * Reset all router registrations. Used when routes.yaml is reloaded in
340
- * lazy mode. In eager mode the URL service handles resets via its queue.
341
- */
342
- reset(): void {
343
- if (this.lazyUrlService) {
344
- this._runLazyHook('reset', () => this.lazyUrlService!.reset());
345
- }
346
- }
347
-
348
- // Runs a lazy router hook in compare mode. Lazy failures are swallowed and
349
- // reported so they can never block the authoritative eager hook (or, for
350
- // reset, break a routes reload).
351
- private _runLazyHook(method: string, fn: () => void): void {
352
- try {
353
- fn();
354
- } catch (err) {
355
- this._reportLazyError(method, err as Error, {});
356
- }
357
- }
358
-
359
- // Runs lazy alongside eager and logs any divergence; eager's value is always
360
- // returned. Lazy errors are swallowed so a comparison can't break a request.
361
- private _compare(
362
- method: string,
363
- eagerValue: unknown,
364
- getLazyValue: () => unknown,
365
- context: Record<string, unknown>,
366
- isEqual: (a: unknown, b: unknown) => boolean = (a, b) => a === b
367
- ): void {
368
- let lazyValue: unknown;
369
- try {
370
- lazyValue = getLazyValue();
371
- } catch (err) {
372
- this._reportLazyError(method, err as Error, context);
373
- return;
374
- }
375
- this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
376
- }
377
-
378
- private async _compareAsync(
379
- method: string,
380
- eagerValue: unknown,
381
- getLazyValue: () => Promise<unknown>,
382
- context: Record<string, unknown>,
383
- isEqual: (a: unknown, b: unknown) => boolean = (a, b) => a === b
384
- ): Promise<void> {
385
- let lazyValue: unknown;
386
- try {
387
- lazyValue = await getLazyValue();
388
- } catch (err) {
389
- this._reportLazyError(method, err as Error, context);
390
- return;
391
- }
392
- this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
393
- }
394
-
395
- private _isNotFound(value: unknown): boolean {
396
- return typeof value === 'string' && value.endsWith('/404/');
397
- }
398
-
399
- // resolveUrl's contract is which resource a path maps to, not the exact
400
- // serialized record. Eager (raw-knex) and lazy (model.toJSON) shape the same
401
- // DB row differently — lazy carries a `parent` key eager omits, eager keeps
402
- // __GHOST_URL__ placeholders lazy expands, timestamps differ in precision —
403
- // so comparing whole records reports the same resolved resource as a
404
- // mismatch. Compare resolved identity instead.
405
- private _resolvesToSameResource(a: unknown, b: unknown): boolean {
406
- if (a === null || b === null) {
407
- return a === b;
408
- }
409
- const ra = a as Resource;
410
- const rb = b as Resource;
411
- return ra.id === rb.id && ra.type === rb.type;
412
- }
413
-
414
- // Divergences that are not lazy regressions, so logging them only buries
415
- // the ones that are. Each branch is a class confirmed from production
416
- // compare data.
417
- private _isExpectedDivergence(
418
- method: string,
419
- eagerValue: unknown,
420
- lazyValue: unknown,
421
- context: Record<string, unknown>
422
- ): boolean {
423
- // Eager leaves tags/authors with no published posts out of its URL map
424
- // (the shouldHavePosts gate) so they resolve to /404/, while lazy has no
425
- // cheap way to run that check and returns the real URL. Eager cache
426
- // staleness (a tag gaining its first post after boot) looks the same.
427
- // Whether to keep lazy's behaviour is still open, but either way it is
428
- // not a lazy bug, so exclude it to surface the divergences that are.
429
- if (method === 'getUrlForResource'
430
- && (context.type === 'tags' || context.type === 'authors')
431
- && this._isNotFound(eagerValue) && !this._isNotFound(lazyValue)) {
432
- return true;
433
- }
434
-
435
- // A site's owner starts with the default `ghost-user` slug and is
436
- // renamed during setup. The rename emits no event the eager cache
437
- // consumes, so eager serves /author/ghost-user/ until the next boot
438
- // while lazy has the real slug from the database.
439
- if (method === 'getUrlForResource'
440
- && context.type === 'authors'
441
- && typeof eagerValue === 'string' && eagerValue.endsWith('/author/ghost-user/')
442
- && !this._isNotFound(lazyValue)) {
443
- return true;
444
- }
445
-
446
- // lazy returns /404/ where eager serves a real URL: eager cached a
447
- // resource that is no longer routable (unpublished or deleted since,
448
- // with no event to evict it). Suppress only when the resource is
449
- // provably not published — a published resource lazy refuses to route
450
- // is a real lazy bug, so that keeps logging.
451
- if (method === 'getUrlForResource'
452
- && this._isNotFound(lazyValue) && !this._isNotFound(eagerValue)
453
- && typeof context.status === 'string' && context.status !== 'published') {
454
- return true;
455
- }
456
- return false;
457
- }
458
-
459
- private _reportMismatch(
460
- method: string,
461
- eagerValue: unknown,
462
- lazyValue: unknown,
463
- context: Record<string, unknown>,
464
- isEqual: (a: unknown, b: unknown) => boolean
465
- ): void {
466
- if (!isEqual(eagerValue, lazyValue)) {
467
- if (this._isExpectedDivergence(method, eagerValue, lazyValue, context)) {
468
- return;
469
- }
470
- const {caller, ...details} = context;
471
- const report = new errors.InternalServerError({
472
- message: 'URL service parity mismatch',
473
- code: 'LAZY_URL_PARITY_MISMATCH',
474
- errorDetails: {method, eager: eagerValue, lazy: lazyValue, ...details}
475
- });
476
- this._applyCallerStack(report, caller);
477
- this._report(report);
478
- }
479
- }
480
-
481
- private _reportLazyError(method: string, err: Error, context: Record<string, unknown>): void {
482
- const {caller, ...details} = context;
483
- const report = new errors.InternalServerError({
484
- message: 'Lazy URL service threw during comparison',
485
- code: 'LAZY_URL_COMPARE_ERROR',
486
- err,
487
- errorDetails: {method, ...details}
488
- });
489
- // @tryghost/errors copies the wrapped error's enumerable props over the
490
- // new error, so a thrown error carrying its own errorDetails (e.g. the
491
- // thin-resource report) silently clobbers the compare context passed
492
- // above. Re-merge after construction so both survive in the logs.
493
- const innerDetails = (err as {errorDetails?: Record<string, unknown>}).errorDetails;
494
- report.errorDetails = {method, ...details, ...innerDetails};
495
- this._applyCallerStack(report, caller);
496
- this._report(report);
497
- }
498
-
499
- // The report's own stack is setImmediate scaffolding — the caller frames
500
- // captured at call time are the stack worth logging.
501
- private _applyCallerStack(report: Error, caller: unknown): void {
502
- if (typeof caller !== 'string') {
503
- return;
504
- }
505
- const frames = caller.split('\n').slice(1).join('\n');
506
- report.stack = `${report.name}: ${report.message}\n${frames}`;
507
- }
508
-
509
- private _report(error: Error): void {
510
- logging.error(error);
511
- }
512
- }
513
-
514
- // `export class` already emits `exports.UrlServiceFacade`. We additionally
515
- // re-attach `module.exports = UrlServiceFacade` AND keep the named export, so
516
- // both `const UrlServiceFacade = require('./url-service-facade')` and
517
- // `const { UrlServiceFacade } = require('./url-service-facade')` work.
518
- module.exports = UrlServiceFacade;
519
- module.exports.UrlServiceFacade = UrlServiceFacade;