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,276 +0,0 @@
1
- const nql = require('@tryghost/nql');
2
- const debug = require('@tryghost/debug')('services:url:generator');
3
- const localUtils = require('../../../shared/url-utils').default;
4
-
5
- // @TODO: merge with filter plugin
6
- const EXPANSIONS = [{
7
- key: 'author',
8
- replacement: 'authors.slug'
9
- }, {
10
- key: 'tags',
11
- replacement: 'tags.slug'
12
- }, {
13
- key: 'tag',
14
- replacement: 'tags.slug'
15
- }, {
16
- key: 'authors',
17
- replacement: 'authors.slug'
18
- }, {
19
- key: 'primary_tag',
20
- replacement: 'primary_tag.slug'
21
- }, {
22
- key: 'primary_author',
23
- replacement: 'primary_author.slug'
24
- }];
25
-
26
- /**
27
- * The UrlGenerator class is responsible to generate urls based on a router's conditions.
28
- * It is the component which sits between routers and resources and connects them together.
29
- * Each url generator can own resources. Each resource can only be owned by one generator,
30
- * because each resource can only live on one url at a time.
31
- *
32
- * Each router is represented by a url generator.
33
- */
34
- class UrlGenerator {
35
- /**
36
- * @param {Object} options
37
- * @param {string} options.identifier frontend router ID reference
38
- * @param {string} options.filter NQL filter string
39
- * @param {string} options.resourceType resource type (e.g. 'posts', 'tags')
40
- * @param {string} options.permalink permalink string
41
- * @param {Object} options.queue instance of the backend Queue
42
- * @param {Object} options.resources instance of the backend Resources
43
- * @param {Object} options.urls instance of the backend URLs (used to store the urls)
44
- * @param {number} options.position an ID of the generator
45
- */
46
- constructor({identifier, filter, resourceType, permalink, queue, resources, urls, position}) {
47
- this.identifier = identifier;
48
- this.resourceType = resourceType;
49
- this.permalink = permalink;
50
- this.queue = queue;
51
- this.urls = urls;
52
- this.resources = resources;
53
- this.uid = position;
54
-
55
- // CASE: routers can define custom filters, but not required.
56
- if (filter) {
57
- this.filter = filter;
58
- this.nql = nql(this.filter, {
59
- expansions: EXPANSIONS,
60
- transformer: nql.utils.mapKeyValues({
61
- key: {
62
- from: 'page',
63
- to: 'type'
64
- },
65
- values: [{
66
- from: false,
67
- to: 'post'
68
- }, {
69
- from: true,
70
- to: 'page'
71
- }]
72
- })
73
- });
74
- debug('filter', this.filter);
75
- }
76
-
77
- this._listeners();
78
- }
79
-
80
- /**
81
- * @NOTE: currently only used if the permalink setting changes and it's used for this url generator.
82
- * @TODO: https://github.com/TryGhost/Ghost/issues/10699
83
- */
84
- regenerateResources() {
85
- const myResources = this.urls.getByGeneratorId(this.uid);
86
-
87
- myResources.forEach((object) => {
88
- this.urls.removeResourceId(object.resource.data.id);
89
- object.resource.release();
90
- this._try(object.resource);
91
- });
92
- }
93
-
94
- /**
95
- * @description Helper function to register listeners for each url generator instance.
96
- * @private
97
- */
98
- _listeners() {
99
- /**
100
- * Listen on two events:
101
- *
102
- * - init: bootstrap or url reset
103
- * - added: resource was added to the database
104
- */
105
- this.queue.register({
106
- event: 'init',
107
- tolerance: 100
108
- }, this._onInit.bind(this));
109
-
110
- this.queue.register({
111
- event: 'added'
112
- }, this._onAdded.bind(this));
113
- }
114
-
115
- /**
116
- * @description Listener which get's called when the resources were fully fetched from the database.
117
- *
118
- * Each url generator will be called and can try to own resources now.
119
- *
120
- * @private
121
- */
122
- _onInit() {
123
- // @NOTE: get the resources of my type e.g. posts.
124
- const resources = this.resources.getAllByType(this.resourceType);
125
-
126
- debug('_onInit', this.resourceType, resources.length);
127
-
128
- for (const resource of resources) {
129
- this._try(resource);
130
- }
131
- }
132
-
133
- /**
134
- * @description Listener which get's called when a resource was added on runtime.
135
- * @param {Object} event
136
- * @param {string} event.type
137
- * @param {string} event.id
138
- * @private
139
- */
140
- _onAdded(event) {
141
- debug('_onAdded', this.toString());
142
-
143
- // CASE: you are type "pages", but the incoming type is "users"
144
- if (event.type !== this.resourceType) {
145
- return;
146
- }
147
-
148
- const resource = this.resources.getByIdAndType(event.type, event.id);
149
- this._try(resource);
150
- }
151
-
152
- /**
153
- * @description Try to own a resource and generate it's url if so.
154
- * @param {import('./resource')} resource - instance of the Resource class
155
- * @returns {boolean}
156
- * @private
157
- */
158
- _try(resource) {
159
- /**
160
- * CASE: another url generator has taken this resource already.
161
- *
162
- * We have to remember that, because each url generator can generate a different url
163
- * for a resource. So we can't directly check `this.urls.getUrl(url)`.
164
- */
165
- if (resource.isReserved()) {
166
- return false;
167
- }
168
-
169
- // CASE 1: route has no custom filter, it will own the resource for sure
170
- let shouldReserve = !this.filter;
171
-
172
- // CASE 2: find out if my filter matches the resource
173
- if (!shouldReserve) {
174
- try {
175
- shouldReserve = this.nql.queryJSON(resource.data);
176
- } catch (err) {
177
- debug(`Failed to queryJSON with filter "${this.filter}"`, err);
178
-
179
- return false;
180
- }
181
- }
182
-
183
- if (shouldReserve) {
184
- const url = this._generateUrl(resource);
185
- this.urls.add({
186
- url: url,
187
- generatorId: this.uid,
188
- resource: resource
189
- });
190
-
191
- resource.reserve();
192
- this._resourceListeners(resource);
193
- return true;
194
- } else {
195
- return false;
196
- }
197
- }
198
-
199
- /**
200
- * @description Generate url based on the permalink configuration of the target router.
201
- *
202
- * @NOTE We currently generate relative urls (https://github.com/TryGhost/Ghost/commit/7b0d5d465ba41073db0c3c72006da625fa11df32).
203
- */
204
- _generateUrl(resource) {
205
- return localUtils.replacePermalink(this.permalink, resource.data);
206
- }
207
-
208
- /**
209
- * @description Helper function to register resource listeners.
210
- *
211
- * I want to know if my resources changes.
212
- *
213
- * If the owned resource get's updated, we simply release/free the resource and push it back to the queue.
214
- * This is the easiest, less error prone implementation.
215
- *
216
- * Imagine you have two collections: `featured:true` and `type:page`.
217
- * If a published post status get's featured and you have not explicitly defined `featured:false`, we wouldn't
218
- * be able to figure out if this resource still belongs to me, because the filter still matches.
219
- */
220
- _resourceListeners(resource) {
221
- const onUpdate = (updatedResource) => {
222
- // 1. remove old resource
223
- this.urls.removeResourceId(updatedResource.data.id);
224
-
225
- // 2. free resource, the url <-> resource connection no longer exists
226
- updatedResource.release();
227
-
228
- // 3. post has the change to get owned from a different collection again
229
- debug('put back in queue', updatedResource.data.id);
230
-
231
- this.queue.start({
232
- event: 'added',
233
- action: 'added:' + resource.data.id,
234
- eventData: {
235
- id: resource.data.id,
236
- type: this.resourceType
237
- }
238
- });
239
- };
240
-
241
- const onRemoved = (removedResource) => {
242
- this.urls.removeResourceId(removedResource.data.id);
243
- removedResource.release();
244
- };
245
-
246
- resource.removeAllListeners();
247
- resource.addListener('updated', onUpdate.bind(this));
248
- resource.addListener('removed', onRemoved.bind(this));
249
- }
250
-
251
- /**
252
- * @description Figure out if this url generator own's a resource id.
253
- * @param {string} id
254
- * @returns {boolean}
255
- */
256
- hasId(id) {
257
- const existingUrl = this.urls.getByResourceId(id);
258
-
259
- if (existingUrl && existingUrl.generatorId === this.uid) {
260
- return true;
261
- }
262
-
263
- return false;
264
- }
265
-
266
- /**
267
- * @description Get all urls of this url generator.
268
- * NOTE: the method is only used for testing purposes at the moment.
269
- * @returns {Array}
270
- */
271
- getUrls() {
272
- return this.urls.getByGeneratorId(this.uid);
273
- }
274
- }
275
-
276
- module.exports = UrlGenerator;
@@ -1,366 +0,0 @@
1
- "use strict";
2
- /**
3
- * UrlServiceFacade
4
- *
5
- * Sits in front of the URL service so callers can use a stable, resource-based
6
- * interface regardless of the underlying implementation. The facade can be
7
- * built with two backends:
8
- *
9
- * - urlService: the legacy eager UrlService that precomputes a full
10
- * resource → URL map at boot.
11
- * - lazyUrlService: an on-demand implementation (LazyUrlService) that
12
- * computes URLs and ownership per call.
13
- *
14
- * When `lazyUrlService` is provided the facade routes calls to it; otherwise
15
- * it delegates to the eager `urlService`. This lets the lazy implementation be
16
- * swapped in behind a config flag without touching individual callers.
17
- */
18
- Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.UrlServiceFacade = void 0;
20
- const _ = require('lodash');
21
- const logging = require('@tryghost/logging');
22
- const errors = require('@tryghost/errors');
23
- class UrlServiceFacade {
24
- urlService;
25
- lazyUrlService;
26
- compare;
27
- fetchRoutableResources;
28
- enumComparesInFlight;
29
- constructor({ urlService, lazyUrlService = null, compare = false, fetchRoutableResources = null }) {
30
- this.urlService = urlService;
31
- this.lazyUrlService = lazyUrlService;
32
- this.compare = compare;
33
- this.fetchRoutableResources = fetchRoutableResources;
34
- this.enumComparesInFlight = new Set();
35
- }
36
- isLazy() {
37
- return !!this.lazyUrlService && !this.compare;
38
- }
39
- isComparing() {
40
- return this.compare && !!this.lazyUrlService;
41
- }
42
- /**
43
- * The full resource record is required: the lazy backend evaluates NQL
44
- * filters and applies permalink templates against it.
45
- */
46
- getUrlForResource(resource, options) {
47
- if (this.isLazy()) {
48
- return this.lazyUrlService.getUrlForResource(resource, options);
49
- }
50
- const url = this.urlService.getUrlByResourceId(resource.id, options);
51
- if (this.isComparing() && !options?.skipComparison) {
52
- const context = this._compareContext(resource, options?.serializerContext ? { serializer: options.serializerContext } : {});
53
- // Snapshot: callers mutate the resource's nested objects in place
54
- // after this returns, but the comparison runs later via setImmediate.
55
- const snapshot = _.cloneDeep(resource);
56
- setImmediate(() => this._compare('getUrlForResource', url, () => this.lazyUrlService.getUrlForResource(snapshot, options), context));
57
- }
58
- return url;
59
- }
60
- /**
61
- * All routable rows of a type. Eager answers from its in-memory cache;
62
- * lazy fetches from the database on demand. In compare mode the eager
63
- * answer is returned and the lazy fetch runs in the background, with any
64
- * id-set divergence logged — counts and id samples only, never row
65
- * bodies (a large site has hundreds of thousands).
66
- */
67
- async getRoutableResources(type, options = {}) {
68
- if (this.isLazy()) {
69
- if (!this.fetchRoutableResources) {
70
- throw new errors.IncorrectUsageError({
71
- message: 'getRoutableResources requires an injected fetchRoutableResources in lazy mode'
72
- });
73
- }
74
- return this.fetchRoutableResources(type, options);
75
- }
76
- const eagerRows = (this.urlService.resources.getAllByType(type) || []).map(resource => resource.data);
77
- // Single-flight per type: rapid invalidation cycles must not stack
78
- // concurrent full-table comparison walks.
79
- if (this.isComparing() && this.fetchRoutableResources && !this.enumComparesInFlight.has(type)) {
80
- this.enumComparesInFlight.add(type);
81
- void this._compareRoutableResources(type, options, eagerRows).finally(() => {
82
- this.enumComparesInFlight.delete(type);
83
- });
84
- }
85
- return eagerRows;
86
- }
87
- async _compareRoutableResources(type, options, eagerRows) {
88
- let lazyRows;
89
- try {
90
- lazyRows = await this.fetchRoutableResources(type, options);
91
- }
92
- catch (err) {
93
- this._reportLazyError('getRoutableResources', err, { type });
94
- return;
95
- }
96
- const eagerIds = new Set(eagerRows.map(row => row.id));
97
- const lazyIds = new Set(lazyRows.map(row => row.id));
98
- const missingFromLazy = [...eagerIds].filter(id => !lazyIds.has(id));
99
- const extraInLazy = [...lazyIds].filter(id => !eagerIds.has(id));
100
- if (!missingFromLazy.length && !extraInLazy.length) {
101
- return;
102
- }
103
- this._report(new errors.InternalServerError({
104
- message: 'URL service parity mismatch',
105
- code: 'LAZY_URL_PARITY_MISMATCH',
106
- errorDetails: {
107
- method: 'getRoutableResources',
108
- type,
109
- eagerCount: eagerIds.size,
110
- lazyCount: lazyIds.size,
111
- missingFromLazy: missingFromLazy.slice(0, 10),
112
- extraInLazy: extraInLazy.slice(0, 10)
113
- }
114
- }));
115
- }
116
- ownsResource(routerIdentifier, resource) {
117
- if (this.isLazy()) {
118
- return this.lazyUrlService.ownsResource(routerIdentifier, resource);
119
- }
120
- const owns = this.urlService.owns(routerIdentifier, resource.id);
121
- if (this.isComparing()) {
122
- const context = this._compareContext(resource, { routerIdentifier });
123
- // Snapshot, as in getUrlForResource.
124
- const snapshot = _.cloneDeep(resource);
125
- setImmediate(() => this._compare('ownsResource', owns, () => this.lazyUrlService.ownsResource(routerIdentifier, snapshot), context));
126
- }
127
- return owns;
128
- }
129
- // Context for a compare report. Must be built synchronously in the calling
130
- // frame: the comparison itself runs from setImmediate, where the caller's
131
- // stack is gone — so a lazy throw reported there names only the URL service
132
- // internals, not which caller handed over the (possibly thin) resource.
133
- // `caller` recaptures those frames; `resourceKeys` fingerprints the shape
134
- // the caller passed (e.g. a Content-API-serialized post vs a full model).
135
- _compareContext(resource, extra = {}) {
136
- const caller = {};
137
- Error.captureStackTrace(caller, this._compareContext);
138
- return {
139
- type: resource.type,
140
- id: resource.id,
141
- status: resource.status,
142
- resourceKeys: Object.keys(resource),
143
- caller: caller.stack,
144
- ...extra
145
- };
146
- }
147
- /**
148
- * Reverse URL lookup. Returns a flat resource shape (e.g. `{type, id, slug}`)
149
- * rather than the legacy `{config: {type}, data: {...}}` envelope. Async to
150
- * match the lazy implementation's contract.
151
- */
152
- async resolveUrl(urlPath) {
153
- if (this.isLazy()) {
154
- return this.lazyUrlService.resolveUrl(urlPath);
155
- }
156
- const resource = this.urlService.getResource(urlPath);
157
- // The routing-level type ('posts', 'pages', 'tags', 'authors') wins
158
- // over any DB type field on resource.data so the flat Resource is
159
- // unambiguous.
160
- const eagerResult = resource
161
- ? Object.assign({}, resource.data, { type: resource.config.type })
162
- : null;
163
- if (this.isComparing()) {
164
- // Fire-and-forget: don't await lazy so the reverse lookup adds no
165
- // latency for its callers; the lazy DB read runs in the background.
166
- // Snapshot the eager result, as in getUrlForResource.
167
- const eagerSnapshot = _.cloneDeep(eagerResult);
168
- void this._compareAsync('resolveUrl', eagerSnapshot, () => this.lazyUrlService.resolveUrl(urlPath), { path: urlPath }, (a, b) => this._resolvesToSameResource(a, b));
169
- }
170
- return eagerResult;
171
- }
172
- // Returns [] when there is no lazy backend: eager looks URLs up by id and
173
- // never touches a resource's relations.
174
- getRequiredRelations() {
175
- if (this.lazyUrlService) {
176
- return this.lazyUrlService.getRequiredRelations();
177
- }
178
- return [];
179
- }
180
- // Columns a resource of this type must carry for the lazy backend to build
181
- // its URL. [] with no lazy backend: eager looks URLs up by id and never
182
- // reads these fields.
183
- getRequiredFields(routerType) {
184
- if (this.lazyUrlService) {
185
- return this.lazyUrlService.getRequiredFields(routerType);
186
- }
187
- return [];
188
- }
189
- hasFinished() {
190
- if (this.isLazy()) {
191
- return this.lazyUrlService.hasFinished();
192
- }
193
- // Track eager when comparing: lazy always reports ready and would gate traffic in early.
194
- return this.urlService.hasFinished();
195
- }
196
- // While comparing, register on both so lazy sees the same routers as eager.
197
- onRouterAddedType(...args) {
198
- if (this.isComparing()) {
199
- this._runLazyHook('onRouterAddedType', () => this.lazyUrlService.onRouterAddedType(...args));
200
- return this.urlService.onRouterAddedType(...args);
201
- }
202
- if (this.lazyUrlService) {
203
- return this.lazyUrlService.onRouterAddedType(...args);
204
- }
205
- return this.urlService.onRouterAddedType(...args);
206
- }
207
- onRouterUpdated(...args) {
208
- if (this.isComparing()) {
209
- this._runLazyHook('onRouterUpdated', () => this.lazyUrlService.onRouterUpdated(...args));
210
- return this.urlService.onRouterUpdated(...args);
211
- }
212
- if (this.lazyUrlService) {
213
- return this.lazyUrlService.onRouterUpdated(...args);
214
- }
215
- return this.urlService.onRouterUpdated(...args);
216
- }
217
- /**
218
- * Reset all router registrations. Used when routes.yaml is reloaded in
219
- * lazy mode. In eager mode the URL service handles resets via its queue.
220
- */
221
- reset() {
222
- if (this.lazyUrlService) {
223
- this._runLazyHook('reset', () => this.lazyUrlService.reset());
224
- }
225
- }
226
- // Runs a lazy router hook in compare mode. Lazy failures are swallowed and
227
- // reported so they can never block the authoritative eager hook (or, for
228
- // reset, break a routes reload).
229
- _runLazyHook(method, fn) {
230
- try {
231
- fn();
232
- }
233
- catch (err) {
234
- this._reportLazyError(method, err, {});
235
- }
236
- }
237
- // Runs lazy alongside eager and logs any divergence; eager's value is always
238
- // returned. Lazy errors are swallowed so a comparison can't break a request.
239
- _compare(method, eagerValue, getLazyValue, context, isEqual = (a, b) => a === b) {
240
- let lazyValue;
241
- try {
242
- lazyValue = getLazyValue();
243
- }
244
- catch (err) {
245
- this._reportLazyError(method, err, context);
246
- return;
247
- }
248
- this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
249
- }
250
- async _compareAsync(method, eagerValue, getLazyValue, context, isEqual = (a, b) => a === b) {
251
- let lazyValue;
252
- try {
253
- lazyValue = await getLazyValue();
254
- }
255
- catch (err) {
256
- this._reportLazyError(method, err, context);
257
- return;
258
- }
259
- this._reportMismatch(method, eagerValue, lazyValue, context, isEqual);
260
- }
261
- _isNotFound(value) {
262
- return typeof value === 'string' && value.endsWith('/404/');
263
- }
264
- // resolveUrl's contract is which resource a path maps to, not the exact
265
- // serialized record. Eager (raw-knex) and lazy (model.toJSON) shape the same
266
- // DB row differently — lazy carries a `parent` key eager omits, eager keeps
267
- // __GHOST_URL__ placeholders lazy expands, timestamps differ in precision —
268
- // so comparing whole records reports the same resolved resource as a
269
- // mismatch. Compare resolved identity instead.
270
- _resolvesToSameResource(a, b) {
271
- if (a === null || b === null) {
272
- return a === b;
273
- }
274
- const ra = a;
275
- const rb = b;
276
- return ra.id === rb.id && ra.type === rb.type;
277
- }
278
- // Divergences that are not lazy regressions, so logging them only buries
279
- // the ones that are. Each branch is a class confirmed from production
280
- // compare data.
281
- _isExpectedDivergence(method, eagerValue, lazyValue, context) {
282
- // Eager leaves tags/authors with no published posts out of its URL map
283
- // (the shouldHavePosts gate) so they resolve to /404/, while lazy has no
284
- // cheap way to run that check and returns the real URL. Eager cache
285
- // staleness (a tag gaining its first post after boot) looks the same.
286
- // Whether to keep lazy's behaviour is still open, but either way it is
287
- // not a lazy bug, so exclude it to surface the divergences that are.
288
- if (method === 'getUrlForResource'
289
- && (context.type === 'tags' || context.type === 'authors')
290
- && this._isNotFound(eagerValue) && !this._isNotFound(lazyValue)) {
291
- return true;
292
- }
293
- // A site's owner starts with the default `ghost-user` slug and is
294
- // renamed during setup. The rename emits no event the eager cache
295
- // consumes, so eager serves /author/ghost-user/ until the next boot
296
- // while lazy has the real slug from the database.
297
- if (method === 'getUrlForResource'
298
- && context.type === 'authors'
299
- && typeof eagerValue === 'string' && eagerValue.endsWith('/author/ghost-user/')
300
- && !this._isNotFound(lazyValue)) {
301
- return true;
302
- }
303
- // lazy returns /404/ where eager serves a real URL: eager cached a
304
- // resource that is no longer routable (unpublished or deleted since,
305
- // with no event to evict it). Suppress only when the resource is
306
- // provably not published — a published resource lazy refuses to route
307
- // is a real lazy bug, so that keeps logging.
308
- if (method === 'getUrlForResource'
309
- && this._isNotFound(lazyValue) && !this._isNotFound(eagerValue)
310
- && typeof context.status === 'string' && context.status !== 'published') {
311
- return true;
312
- }
313
- return false;
314
- }
315
- _reportMismatch(method, eagerValue, lazyValue, context, isEqual) {
316
- if (!isEqual(eagerValue, lazyValue)) {
317
- if (this._isExpectedDivergence(method, eagerValue, lazyValue, context)) {
318
- return;
319
- }
320
- const { caller, ...details } = context;
321
- const report = new errors.InternalServerError({
322
- message: 'URL service parity mismatch',
323
- code: 'LAZY_URL_PARITY_MISMATCH',
324
- errorDetails: { method, eager: eagerValue, lazy: lazyValue, ...details }
325
- });
326
- this._applyCallerStack(report, caller);
327
- this._report(report);
328
- }
329
- }
330
- _reportLazyError(method, err, context) {
331
- const { caller, ...details } = context;
332
- const report = new errors.InternalServerError({
333
- message: 'Lazy URL service threw during comparison',
334
- code: 'LAZY_URL_COMPARE_ERROR',
335
- err,
336
- errorDetails: { method, ...details }
337
- });
338
- // @tryghost/errors copies the wrapped error's enumerable props over the
339
- // new error, so a thrown error carrying its own errorDetails (e.g. the
340
- // thin-resource report) silently clobbers the compare context passed
341
- // above. Re-merge after construction so both survive in the logs.
342
- const innerDetails = err.errorDetails;
343
- report.errorDetails = { method, ...details, ...innerDetails };
344
- this._applyCallerStack(report, caller);
345
- this._report(report);
346
- }
347
- // The report's own stack is setImmediate scaffolding — the caller frames
348
- // captured at call time are the stack worth logging.
349
- _applyCallerStack(report, caller) {
350
- if (typeof caller !== 'string') {
351
- return;
352
- }
353
- const frames = caller.split('\n').slice(1).join('\n');
354
- report.stack = `${report.name}: ${report.message}\n${frames}`;
355
- }
356
- _report(error) {
357
- logging.error(error);
358
- }
359
- }
360
- exports.UrlServiceFacade = UrlServiceFacade;
361
- // `export class` already emits `exports.UrlServiceFacade`. We additionally
362
- // re-attach `module.exports = UrlServiceFacade` AND keep the named export, so
363
- // both `const UrlServiceFacade = require('./url-service-facade')` and
364
- // `const { UrlServiceFacade } = require('./url-service-facade')` work.
365
- module.exports = UrlServiceFacade;
366
- module.exports.UrlServiceFacade = UrlServiceFacade;