ghost 6.53.0 → 6.54.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 (596) hide show
  1. package/components/ghost-storage-base-3.0.0.tgz +0 -0
  2. package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
  3. package/components/tryghost-adapter-base-scheduling-0.1.1.tgz +0 -0
  4. package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
  5. package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
  6. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  7. package/components/tryghost-i18n-0.0.0.tgz +0 -0
  8. package/components/tryghost-kg-converters-1.2.3.tgz +0 -0
  9. package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
  10. package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
  11. package/core/boot.js +9 -6
  12. package/core/built/admin/assets/Combination-CJG7Flp-.js +41 -0
  13. package/core/built/admin/assets/Growth-CSaHoSVD.js +3 -0
  14. package/core/built/admin/assets/Newsletters-C5cs6FGo.js +1 -0
  15. package/core/built/admin/assets/Overview-j4NaeUNm.js +1 -0
  16. package/core/built/admin/assets/{PolarAngleAxis-pciQ0MOy.js → PolarAngleAxis-BGjf7sBu.js} +1 -1
  17. package/core/built/admin/assets/{RadialBarChart-DWmw2PcX.js → RadialBarChart-zeFf9mBz.js} +1 -1
  18. package/core/built/admin/assets/Web-_fD935OX.js +2 -0
  19. package/core/built/admin/assets/account-migration-DLvVA5yq.js +1 -0
  20. package/core/built/admin/assets/activitypub/{account-migration-BPZG37DJ.js → account-migration-DHbZcxGM.js} +13 -13
  21. package/core/built/admin/assets/activitypub/activitypub.js +7 -7
  22. package/core/built/admin/assets/activitypub/{avatar-ZYco0ITb.js → avatar-B3DEDB1l.js} +10 -10
  23. package/core/built/admin/assets/activitypub/{avatar-flipboard-D541lc4F.js → avatar-flipboard-Cqrg0XMx.js} +3 -3
  24. package/core/built/admin/assets/activitypub/{bluesky-sharing-NOHUtHjl.js → bluesky-sharing-zfxUey_L.js} +38 -37
  25. package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-B-GOFDPZ.js → chunk-KS7C4IRE-D4HEcEwi.js} +716 -710
  26. package/core/built/admin/assets/activitypub/{content-formatters-bwrp4iEO.js → content-formatters-BpQARh28.js} +229 -181
  27. package/core/built/admin/assets/activitypub/{copy-C7m7ovHw.js → copy-D9UWIAan.js} +2 -2
  28. package/core/built/admin/assets/activitypub/{dist-B5ira32t.js → dist-Ba_QxHHl.js} +5 -5
  29. package/core/built/admin/assets/activitypub/{domain-iqE4YXwD.js → domain-CmwSZLHK.js} +33 -33
  30. package/core/built/admin/assets/activitypub/{edit-profile-B8MFYh28.js → edit-profile-CXdwNmVG.js} +46 -45
  31. package/core/built/admin/assets/activitypub/{explore-BbdKodp3.js → explore-DfbjzGM1.js} +20 -20
  32. package/core/built/admin/assets/activitypub/{feed-Cl3A5owG.js → feed-DSEBJOqf.js} +15 -15
  33. package/core/built/admin/assets/activitypub/{field-DL6WyR-c.js → field-CY7XVcx6.js} +13 -13
  34. package/core/built/admin/assets/activitypub/{hash-6G3DzwL9.js → hash-BC9Q0N4c.js} +2 -2
  35. package/core/built/admin/assets/activitypub/{inbox-YmhuBice.js → inbox-DQ4ZnC1m.js} +2 -2
  36. package/core/built/admin/assets/activitypub/inbox-DtQ0kytT.js +1221 -0
  37. package/core/built/admin/assets/activitypub/{label-DGbLjDvE.js → label-Dw96GQ7u.js} +6 -6
  38. package/core/built/admin/assets/activitypub/layout-IdVq3lRy.js +1 -0
  39. package/core/built/admin/assets/activitypub/{moderation-qnBtk1zS.js → moderation-Dg_-I-8C.js} +32 -31
  40. package/core/built/admin/assets/activitypub/{note-C7ZRoSJv.js → note-Cy1-uKMY.js} +21 -21
  41. package/core/built/admin/assets/activitypub/{notifications-DiEA4b1o.js → notifications-B_fvoLIM.js} +17 -17
  42. package/core/built/admin/assets/activitypub/{onboarding-_JVgEwgV.js → onboarding-BFDkzeR2.js} +2 -2
  43. package/core/built/admin/assets/activitypub/{onboarding-DG4zu3qM.js → onboarding-D6rcBdPe.js} +5 -5
  44. package/core/built/admin/assets/activitypub/{preferences-B3FtocnP.js → preferences-ByJseZp6.js} +1607 -1606
  45. package/core/built/admin/assets/activitypub/{profile-D8ZJfYXB.js → profile-BFtbnIjz.js} +264 -263
  46. package/core/built/admin/assets/activitypub/{reply-C1vggenw.js → reply-CpdMeAO5.js} +2 -2
  47. package/core/built/admin/assets/activitypub/{routes-BOii-V9n.js → routes-Dh3xXfce.js} +1311 -1295
  48. package/core/built/admin/assets/activitypub/{separator-1gNokN5S.js → separator-BI1FLsYM.js} +4 -4
  49. package/core/built/admin/assets/activitypub/{settings--9Sb0nbl.js → settings-DTgqJPxM.js} +14 -14
  50. package/core/built/admin/assets/activitypub/{site-fkMmqv0V.js → site-CdL9ZLqm.js} +10 -10
  51. package/core/built/admin/assets/activitypub/{step-1-QhivSMw2.js → step-1-DIDg4aHb.js} +15 -15
  52. package/core/built/admin/assets/activitypub/{step-2-pMk9YwqD.js → step-2-BfGe8BYt.js} +7 -7
  53. package/core/built/admin/assets/activitypub/{step-3-DvmVC1fo.js → step-3-mE9spAqw.js} +12 -12
  54. package/core/built/admin/assets/activitypub/{tabs-Dc0FEEYD.js → tabs-ogqwoBlb.js} +241 -237
  55. package/core/built/admin/assets/activitypub/{topic-filter-CuDyM5bD.js → topic-filter-DLHBZT93.js} +4 -4
  56. package/core/built/admin/assets/activitypub/use-activity-pub-queries-VzbN99u_.js +1957 -0
  57. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-Drx1kdAK.js → use-navigate-with-base-path-CAwXTmqH.js} +165 -175
  58. package/core/built/admin/assets/activitypub/{use-reply-chain-data-BHkCHcW5.js → use-reply-chain-data-CxonteFD.js} +8 -8
  59. package/core/built/admin/assets/activitypub/users-BLeiOTHc.js +1464 -0
  60. package/core/built/admin/assets/activitypub/{x-BHCEe-c2.js → x-C8tAnXly.js} +2 -2
  61. package/core/built/admin/assets/{audience-adiFehTD.js → audience-BKRdsFmy.js} +1 -1
  62. package/core/built/admin/assets/{automated-emails-UWM02CMr.js → automated-emails-Cn87L5Md.js} +1 -1
  63. package/core/built/admin/assets/automations-B7-y3dXt.js +1 -0
  64. package/core/built/admin/assets/automations-BP_aHamJ.js +1 -0
  65. package/core/built/admin/assets/{avatar-flipboard-Lz3F9fNT.js → avatar-flipboard-C6Kj3Awu.js} +1 -1
  66. package/core/built/admin/assets/bluesky-sharing-Bh36XPh4.js +1 -0
  67. package/core/built/admin/assets/box-BWbhXh21.js +1 -0
  68. package/core/built/admin/assets/breadcrumb-D34YWnaw.js +1 -0
  69. package/core/built/admin/assets/card-DGoDJzrn.js +1 -0
  70. package/core/built/admin/assets/{chart-ChpW-NzQ.js → chart-BuRT2Y6p.js} +14 -14
  71. package/core/built/admin/assets/chart-helpers-DbklaJw9.js +1 -0
  72. package/core/built/admin/assets/checkbox-DA4-1UiI.js +1 -0
  73. package/core/built/admin/assets/chunk.524.45e0875f41b71e46e849.js +25 -0
  74. package/core/built/admin/assets/chunk.582.51defd4b009f0de8d4d5.js +27 -0
  75. package/core/built/admin/assets/{chunk.831.0703a12e49f51cec3f3a.js → chunk.589.096f4d0bb775e5229aa9.js} +78 -85
  76. package/core/built/admin/assets/{chunk.432.c2caa0b20f03e71d1a35.js → chunk.962.b73442b07be930bb5d85.js} +2216 -3188
  77. package/core/built/admin/assets/{chunk.432.c2caa0b20f03e71d1a35.js.LICENSE.txt → chunk.962.b73442b07be930bb5d85.js.LICENSE.txt} +0 -63
  78. package/core/built/admin/assets/code-editor-view-BKBrkffZ.js +1 -0
  79. package/core/built/admin/assets/comments-CzVNIT8b.js +1 -0
  80. package/core/built/admin/assets/container-BYz2Ed39.js +1 -0
  81. package/core/built/admin/assets/data-list-16rkJYYi.js +1 -0
  82. package/core/built/admin/assets/date-range-select-B5vSsrpx.js +1 -0
  83. package/core/built/admin/assets/disabled-sources-indicator-NkOIoMue.js +1 -0
  84. package/core/built/admin/assets/{dist-CsekE4FL.js → dist-CQJY6SVY.js} +1 -1
  85. package/core/built/admin/assets/dist-DGzPJ_bO.js +4 -0
  86. package/core/built/admin/assets/{dist-BbGrW05X.js → dist-DyViJwIy.js} +1 -1
  87. package/core/built/admin/assets/dist-Ndy-PuGN.js +1 -0
  88. package/core/built/admin/assets/dist-P_ohUlTi.js +1 -0
  89. package/core/built/admin/assets/domain-B7_n5nfx.js +1 -0
  90. package/core/built/admin/assets/dropzone-DcuJEEUf.js +5 -0
  91. package/core/built/admin/assets/edit-profile-rvDGrS8J.js +1 -0
  92. package/core/built/admin/assets/editor-BCiIyFjm.js +8 -0
  93. package/core/built/admin/assets/empty-indicator-oX1u1Pkn.js +1 -0
  94. package/core/built/admin/assets/esm-CFX93H5M.js +2 -0
  95. package/core/built/admin/assets/explore-BZAZOg5X.js +1 -0
  96. package/core/built/admin/assets/feed-CFV8xDw9.js +1 -0
  97. package/core/built/admin/assets/field-BW-M2Zwp.js +1 -0
  98. package/core/built/admin/assets/{filter-sources-v39ihhhV.js → filter-sources-Fx8wiYYw.js} +1 -1
  99. package/core/built/admin/assets/filters-CGX_pf2N.js +1 -0
  100. package/core/built/admin/assets/filters-DtcXMZDU.js +1 -0
  101. package/core/built/admin/assets/framework-provider-CmtnPXIT.js +4 -0
  102. package/core/built/admin/assets/{ghost-b57f077997b739a8ae9a70772dc016cb.js → ghost-b1dce91ad3232cc259ee5fbbf9526243.js} +294 -290
  103. package/core/built/admin/assets/ghost-dark-c8d0ab7f515b2010c173e23e9b32ea8a.css +1 -0
  104. package/core/built/admin/assets/gift-link-modal-Bk8qpnMw.js +1 -0
  105. package/core/built/admin/assets/{gift-links-CY3GwiVi.js → gift-links-nBxJjZiQ.js} +1 -1
  106. package/core/built/admin/assets/growth-Ck2s3JjR.js +1 -0
  107. package/core/built/admin/assets/hooks-BApwwBE0.js +2 -0
  108. package/core/built/admin/assets/{inbox-TEISAmth.js → inbox-BRp1_aaC.js} +35 -35
  109. package/core/built/admin/assets/index-BBf502Wg.js +69 -0
  110. package/core/built/admin/assets/index-BCCZKxP2.css +1 -0
  111. package/core/built/admin/assets/inline-EMAm3okk.js +1 -0
  112. package/core/built/admin/assets/input-group-DjuYnNCd.js +1 -0
  113. package/core/built/admin/assets/{react-dom-C6aoWSfl.js → jsx-runtime-DX3WBWVO.js} +1 -1
  114. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +5 -2
  115. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +2 -2
  116. package/core/built/admin/assets/{koenig-lexical-Di7b48ED.js → koenig-lexical-CM7UFNdL.js} +2 -2
  117. package/core/built/admin/assets/kpi-card-BrGH6FsT.js +1 -0
  118. package/core/built/admin/assets/kpi-card-CIGyijjx.js +1 -0
  119. package/core/built/admin/assets/kpi-helpers-_HRRsklE.js +1 -0
  120. package/core/built/admin/assets/kpi-tabs-BSvh9eoq.js +1 -0
  121. package/core/built/admin/assets/label-BOR9XdKl.js +1 -0
  122. package/core/built/admin/assets/label-picker-BdNAWuUX.js +1 -0
  123. package/core/built/admin/assets/{labels-GbxlXWtf.js → labels-Cyayfd-e.js} +1 -1
  124. package/core/built/admin/assets/layout-Bw4GnxXp.js +1 -0
  125. package/core/built/admin/assets/limit-service-9fwD33lU.js +20 -0
  126. package/core/built/admin/assets/link-helpers-tOlxmuH5.js +1 -0
  127. package/core/built/admin/assets/list-page-B6ANtzkd.js +1 -0
  128. package/core/built/admin/assets/mailchimp-Ds_NQ04w.svg +9 -0
  129. package/core/built/admin/assets/member-custom-fields-DvFyamLu.js +1 -0
  130. package/core/built/admin/assets/member-detail-Cg2vTqvJ.js +1 -0
  131. package/core/built/admin/assets/members-DsPxYAVm.js +10 -0
  132. package/core/built/admin/assets/modals-Bf_fyvHZ.js +31 -0
  133. package/core/built/admin/assets/moderation-Dlp7M5sa.js +1 -0
  134. package/core/built/admin/assets/multi-select-combobox-CH1o8XXQ.js +1 -0
  135. package/core/built/admin/assets/newsletter-CQ5CAYUI.js +1 -0
  136. package/core/built/admin/assets/{newsletters-DZm3yLj8.js → newsletters-D1jbtV-h.js} +1 -1
  137. package/core/built/admin/assets/note-BrAZ7Z0o.js +1 -0
  138. package/core/built/admin/assets/notifications-BnbHLOPC.js +1 -0
  139. package/core/built/admin/assets/nql-lang-B7faUf8D.js +19 -0
  140. package/core/built/admin/assets/{offers-D11QtPXX.js → offers-CrY6nSRR.js} +1 -1
  141. package/core/built/admin/assets/onboarding-route-eU146kuA.js +1 -0
  142. package/core/built/admin/assets/overview-BpbaK-xu.js +1 -0
  143. package/core/built/admin/assets/page-header-C0jVxtS8.js +1 -0
  144. package/core/built/admin/assets/{pages-BnObVRrf.js → pages-BbNWFcGj.js} +1 -1
  145. package/core/built/admin/assets/pintura-CXOmq-5H.svg +15 -0
  146. package/core/built/admin/assets/post-analytics-Iw7dk1mT.js +1 -0
  147. package/core/built/admin/assets/post-analytics-header-D9pJ4iNm.js +1 -0
  148. package/core/built/admin/assets/{post-analytics-provider-DSfBCtnj.js → post-analytics-provider-BDqcfBor.js} +1 -1
  149. package/core/built/admin/assets/post-share-modal-DBfKl2GZ.js +1 -0
  150. package/core/built/admin/assets/{posts-CwH8VJvB.js → posts-EWpZIjkr.js} +1 -1
  151. package/core/built/admin/assets/preferences-D4EANGoc.js +5 -0
  152. package/core/built/admin/assets/profile-dJ44Bn_W.js +1 -0
  153. package/core/built/admin/assets/{referrers-nxR52jPI.js → referrers-BCfFYSHc.js} +1 -1
  154. package/core/built/admin/assets/router-provider-C1R4AZ2o.js +1 -0
  155. package/core/built/admin/assets/select--2uVN9_-.js +1 -0
  156. package/core/built/admin/assets/settings-Bz3RZRmE.js +1 -0
  157. package/core/built/admin/assets/settings-SD9bxIpt.js +4 -0
  158. package/core/built/admin/assets/share-modal-CkdTTMJd.js +1 -0
  159. package/core/built/admin/assets/social-urls-BvFZwYcE.js +8 -0
  160. package/core/built/admin/assets/sort-button-DZp9Y2r1.js +1 -0
  161. package/core/built/admin/assets/source-icon-CHr9qJ2Y.js +1 -0
  162. package/core/built/admin/assets/stack-xiN0eQ6Z.js +1 -0
  163. package/core/built/admin/assets/stats-DGoXZxWE.js +1 -0
  164. package/core/built/admin/assets/stats-view-D2-1L0ms.js +1 -0
  165. package/core/built/admin/assets/step-1-RdahbwyG.js +1 -0
  166. package/core/built/admin/assets/step-2-D83PeRwd.js +1 -0
  167. package/core/built/admin/assets/step-3-p_Op4wv4.js +2 -0
  168. package/core/built/admin/assets/switch-qp518gFQ.js +1 -0
  169. package/core/built/admin/assets/table-B8LgTQJZ.js +1 -0
  170. package/core/built/admin/assets/tabs-D3ZOYkM7.js +1 -0
  171. package/core/built/admin/assets/tags-B4l891z2.js +1 -0
  172. package/core/built/admin/assets/text-DwtjwGkh.js +1 -0
  173. package/core/built/admin/assets/textarea-CTlb3Uu6.js +1 -0
  174. package/core/built/admin/assets/{tiers-EghDzCKc.js → tiers-BgYLWRJv.js} +1 -1
  175. package/core/built/admin/assets/toggle-group-DNXDkAwf.js +1 -0
  176. package/core/built/admin/assets/topic-filter-DZGjEcz3.js +1 -0
  177. package/core/built/admin/assets/{use-analytics-data-0l_OHtnR.js → use-analytics-data-CaT8RirX.js} +1 -1
  178. package/core/built/admin/assets/use-filter-params-DeQ8sX4N.js +1 -0
  179. package/core/built/admin/assets/use-growth-stats-9Db2CuJe.js +1 -0
  180. package/core/built/admin/assets/{use-post-referrers-DwI31Ril.js → use-post-referrers-BV54YcQS.js} +1 -1
  181. package/core/built/admin/assets/use-reply-chain-data-DuYZGDan.js +1 -0
  182. package/core/built/admin/assets/use-simple-pagination-8fTua9Nt.js +1 -0
  183. package/core/built/admin/assets/{use-tinybird-query-DvZBBMG7.js → use-tinybird-query-Dz156HvZ.js} +1 -1
  184. package/core/built/admin/assets/{use-tinybird-token-CQtebkvU.js → use-tinybird-token-C9RUdL_n.js} +1 -1
  185. package/core/built/admin/assets/{users-Pc8IRjk9.js → users-CcSCQFcQ.js} +1 -1
  186. package/core/built/admin/assets/virtual-list-BCvP0YF-.js +1 -0
  187. package/core/built/admin/assets/web-DTZnnUGr.js +1 -0
  188. package/core/built/admin/index.html +18 -22
  189. package/core/frontend/apps/private-blogging/index.js +1 -1
  190. package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
  191. package/core/frontend/helpers/split.js +2 -1
  192. package/core/frontend/meta/asset-url.js +1 -1
  193. package/core/frontend/meta/author-image.js +1 -1
  194. package/core/frontend/meta/blog-logo.js +1 -1
  195. package/core/frontend/meta/canonical-url.js +1 -1
  196. package/core/frontend/meta/cover-image.js +1 -1
  197. package/core/frontend/meta/get-meta.js +1 -1
  198. package/core/frontend/meta/og-image.js +1 -1
  199. package/core/frontend/meta/paginated-url.js +1 -1
  200. package/core/frontend/meta/twitter-image.js +1 -1
  201. package/core/frontend/meta/url.js +1 -1
  202. package/core/frontend/services/assets-minification/admin-auth-assets.js +1 -1
  203. package/core/frontend/services/llms/handler.js +1 -1
  204. package/core/frontend/services/proxy.js +31 -2
  205. package/core/frontend/services/routing/collection-router.js +1 -1
  206. package/core/frontend/services/routing/controllers/email-post.js +1 -1
  207. package/core/frontend/services/routing/controllers/entry/markdown.js +1 -1
  208. package/core/frontend/services/routing/controllers/entry/markdown.ts +1 -1
  209. package/core/frontend/services/routing/controllers/entry.js +1 -1
  210. package/core/frontend/services/routing/controllers/entry.ts +1 -1
  211. package/core/frontend/services/routing/controllers/previews.js +6 -7
  212. package/core/frontend/services/routing/controllers/unsubscribe.js +7 -3
  213. package/core/frontend/services/routing/email-router.js +1 -1
  214. package/core/frontend/services/routing/events.js +12 -0
  215. package/core/frontend/services/routing/middleware/page-param.js +1 -1
  216. package/core/frontend/services/routing/parent-router.js +1 -1
  217. package/core/frontend/services/routing/preview-router.js +1 -1
  218. package/core/frontend/services/routing/router-manager.js +4 -8
  219. package/core/frontend/services/routing/rss-router.js +1 -1
  220. package/core/frontend/services/routing/static-pages-router.js +1 -1
  221. package/core/frontend/services/routing/static-routes-router.js +1 -1
  222. package/core/frontend/services/routing/taxonomy-router.js +1 -1
  223. package/core/frontend/services/rss/generate-feed.js +1 -1
  224. package/core/frontend/services/sitemap/base-site-map-generator.js +23 -4
  225. package/core/frontend/services/sitemap/site-map-index-generator.js +1 -1
  226. package/core/frontend/services/sitemap/site-map-manager.js +14 -8
  227. package/core/frontend/services/sitemap/utils.js +1 -1
  228. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +1 -1
  229. package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +1 -1
  230. package/core/frontend/utils/images.js +1 -1
  231. package/core/frontend/web/middleware/admin-toolbar.js +1 -1
  232. package/core/frontend/web/middleware/handle-image-sizes.js +2 -2
  233. package/core/frontend/web/middleware/redirect-ghost-to-admin.js +1 -1
  234. package/core/frontend/web/routers/serve-favicon.js +1 -1
  235. package/core/frontend/web/routers/serve-public-file.js +1 -1
  236. package/core/frontend/web/site.js +5 -5
  237. package/core/server/adapters/cache/AdapterCacheMemoryTTL.js +2 -2
  238. package/core/server/adapters/cache/MemoryCache.js +2 -2
  239. package/core/server/adapters/cache/Redis.js +1 -1
  240. package/core/server/adapters/cache/index.js +1 -1
  241. package/core/server/adapters/lib/redis/AdapterCacheRedis.js +2 -2
  242. package/core/server/adapters/route-settings/FileStore.js +2 -8
  243. package/core/server/adapters/route-settings/FileStore.ts +1 -6
  244. package/core/server/adapters/route-settings/S3RouteSettingsStore.js +206 -0
  245. package/core/server/adapters/route-settings/S3RouteSettingsStore.ts +199 -0
  246. package/core/server/adapters/route-settings/utils.js +13 -0
  247. package/core/server/adapters/route-settings/utils.ts +7 -0
  248. package/core/server/adapters/scheduling/deps.d.ts +0 -2
  249. package/core/server/adapters/scheduling/error-capture.js +76 -0
  250. package/core/server/adapters/scheduling/error-capture.ts +91 -0
  251. package/core/server/adapters/scheduling/utils.js +18 -34
  252. package/core/server/adapters/scheduling/utils.ts +36 -0
  253. package/core/server/adapters/storage/LocalFilesStorage.js +1 -1
  254. package/core/server/adapters/storage/LocalImagesStorage.js +1 -1
  255. package/core/server/adapters/storage/LocalMediaStorage.js +1 -1
  256. package/core/server/adapters/storage/LocalStorageBase.js +3 -3
  257. package/core/server/adapters/storage/S3Storage.js +2 -2
  258. package/core/server/adapters/storage/S3Storage.ts +1 -1
  259. package/core/server/adapters/storage/utils.js +1 -1
  260. package/core/server/api/endpoints/authentication.js +1 -1
  261. package/core/server/api/endpoints/featurebase.js +1 -1
  262. package/core/server/api/endpoints/files.js +2 -2
  263. package/core/server/api/endpoints/images.js +2 -2
  264. package/core/server/api/endpoints/libraries.d.ts +0 -1
  265. package/core/server/api/endpoints/media.js +8 -5
  266. package/core/server/api/endpoints/member-custom-fields.js +4 -1
  267. package/core/server/api/endpoints/member-custom-fields.ts +4 -1
  268. package/core/server/api/endpoints/posts.js +17 -1
  269. package/core/server/api/endpoints/search-index-public.js +4 -3
  270. package/core/server/api/endpoints/search-index.js +5 -4
  271. package/core/server/api/endpoints/settings-public.js +1 -1
  272. package/core/server/api/endpoints/settings.js +3 -1
  273. package/core/server/api/endpoints/utils/serializers/input/utils/url.js +9 -1
  274. package/core/server/api/endpoints/utils/serializers/output/default.js +7 -0
  275. package/core/server/api/endpoints/utils/serializers/output/files.js +1 -1
  276. package/core/server/api/endpoints/utils/serializers/output/media.js +1 -1
  277. package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +7 -4
  278. package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.ts +8 -4
  279. package/core/server/api/endpoints/utils/serializers/output/members.js +16 -26
  280. package/core/server/api/endpoints/utils/serializers/output/pages.js +7 -0
  281. package/core/server/api/endpoints/utils/serializers/output/posts.js +13 -0
  282. package/core/server/api/endpoints/utils/serializers/output/utils/url.js +1 -1
  283. package/core/server/api/endpoints/utils/validators/input/members.js +2 -52
  284. package/core/server/data/db/backup.js +1 -1
  285. package/core/server/data/importer/handlers/image.js +3 -3
  286. package/core/server/data/importer/import-manager.js +6 -4
  287. package/core/server/data/importer/importers/content-file-importer.js +2 -2
  288. package/core/server/data/migrations/utils/schema.js +4 -2
  289. package/core/server/data/migrations/versions/6.54/2026-07-19-16-59-00-add-content-import-permission.js +10 -0
  290. package/core/server/data/migrations/versions/6.54/2026-07-20-13-59-21-drop-email-tracked-sent-count-from-automation-action-revisions.js +7 -0
  291. package/core/server/data/migrations/versions/6.54/2026-07-21-01-58-20-add-automation-run-steps-poller-index.js +3 -0
  292. package/core/server/data/migrations/versions/6.54/2026-07-21-21-08-15-add-automated-email-recipients-mailgun-message-id-index.js +3 -0
  293. package/core/server/data/migrations/versions/6.54/2026-07-22-10-41-08-add-click-tracking-to-automated-email-recipients.js +13 -0
  294. package/core/server/data/migrations/versions/6.54/2026-07-22-15-10-30-add-email-clicked-count-to-automation-action-revisions.js +7 -0
  295. package/core/server/data/migrations/versions/6.54/2026-07-23-10-24-18-add-automation-action-revision-to-redirects.js +9 -0
  296. package/core/server/data/schema/commands.js +37 -3
  297. package/core/server/data/schema/fixtures/fixtures.json +7 -0
  298. package/core/server/data/schema/lib/default-index-name.js +20 -0
  299. package/core/server/data/schema/schema.js +40 -17
  300. package/core/server/data/seeders/data-generator.js +4 -2
  301. package/core/server/lib/image/image-size.js +5 -7
  302. package/core/server/lib/image/image-utils.js +2 -2
  303. package/core/server/lib/image/index.js +4 -4
  304. package/core/server/lib/lexical.js +3 -3
  305. package/core/server/lib/mobiledoc.js +2 -2
  306. package/core/server/lib/validate-password.js +1 -1
  307. package/core/server/models/action.js +9 -0
  308. package/core/server/models/base/plugins/generate-slug.js +1 -1
  309. package/core/server/models/collection.js +1 -1
  310. package/core/server/models/custom-theme-setting.js +1 -1
  311. package/core/server/models/member.js +73 -0
  312. package/core/server/models/newsletter.js +1 -1
  313. package/core/server/models/post.js +19 -5
  314. package/core/server/models/posts-meta.js +1 -1
  315. package/core/server/models/redirect.js +1 -1
  316. package/core/server/models/settings.js +1 -1
  317. package/core/server/models/snippet.js +1 -1
  318. package/core/server/models/tag.js +1 -1
  319. package/core/server/models/user.js +1 -1
  320. package/core/server/models/welcome-email-automated-email.js +1 -1
  321. package/core/server/services/activitypub/activity-pub-service-wrapper.js +1 -1
  322. package/core/server/services/adapter-manager/adapter-manager.js +5 -1
  323. package/core/server/services/adapter-manager/adapter-manager.ts +7 -2
  324. package/core/server/services/adapter-manager/index.js +18 -15
  325. package/core/server/services/adapter-manager/index.ts +23 -17
  326. package/core/server/services/adapter-manager/utils.js +124 -14
  327. package/core/server/services/adapter-manager/utils.ts +130 -14
  328. package/core/server/services/api-version-compatibility/mw-version-rewrites.js +1 -1
  329. package/core/server/services/audience-feedback/index.js +1 -1
  330. package/core/server/services/auth/passwordreset.js +1 -1
  331. package/core/server/services/auth/session/express-session.js +1 -1
  332. package/core/server/services/auth/session/index.js +2 -2
  333. package/core/server/services/auth/session/session-service.js +1 -1
  334. package/core/server/services/automations/automations-api.js +2 -19
  335. package/core/server/services/automations/automations-api.ts +2 -31
  336. package/core/server/services/automations/automations-repository.ts +15 -0
  337. package/core/server/services/automations/database-automations-repository.js +30 -2
  338. package/core/server/services/automations/database-automations-repository.ts +33 -2
  339. package/core/server/services/automations/poll.js +16 -12
  340. package/core/server/services/automations/poll.ts +17 -15
  341. package/core/server/services/automations/service.js +1 -1
  342. package/core/server/services/automations/service.ts +3 -13
  343. package/core/server/services/automations/welcome-email-automation-poll.js +2 -1
  344. package/core/server/services/comments/index.js +1 -1
  345. package/core/server/services/custom-redirects/index.js +2 -2
  346. package/core/server/services/email-analytics/email-analytics-service-wrapper.js +14 -13
  347. package/core/server/services/email-analytics/email-analytics-service.js +1 -1
  348. package/core/server/services/email-analytics/events/start-email-analytics-job-event.js +11 -15
  349. package/core/server/services/email-analytics/events/start-email-analytics-job-event.ts +15 -0
  350. package/core/server/services/email-analytics/index.js +2 -3
  351. package/core/server/services/email-analytics/index.ts +1 -2
  352. package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.js +1 -6
  353. package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.ts +0 -9
  354. package/core/server/services/email-analytics/jobs/fetch-latest/index.js +1 -1
  355. package/core/server/services/email-analytics/jobs/index.js +0 -2
  356. package/core/server/services/email-service/email-service-wrapper.js +1 -1
  357. package/core/server/services/email-service/email-service.js +20 -4
  358. package/core/server/services/explore-ping/explore-ping-service.js +24 -39
  359. package/core/server/services/explore-ping/explore-ping-service.ts +198 -0
  360. package/core/server/services/explore-ping/index.js +11 -11
  361. package/core/server/services/explore-ping/index.ts +42 -0
  362. package/core/server/services/gifts/gift-reminder-scheduler.js +1 -1
  363. package/core/server/services/gifts/gift-reminder-scheduler.ts +1 -1
  364. package/core/server/services/gifts/gift-service-wrapper.js +1 -1
  365. package/core/server/services/identity-tokens/identity-token-service-wrapper.js +1 -1
  366. package/core/server/services/indexnow-ping/constants.js +30 -0
  367. package/core/server/services/indexnow-ping/constants.ts +32 -0
  368. package/core/server/services/indexnow-ping/index.js +29 -253
  369. package/core/server/services/indexnow-ping/index.ts +37 -0
  370. package/core/server/services/indexnow-ping/indexnow-ping-service.js +229 -0
  371. package/core/server/services/indexnow-ping/indexnow-ping-service.ts +319 -0
  372. package/core/server/services/invites/index.js +1 -1
  373. package/core/server/services/lib/mailgun-client.js +3 -3
  374. package/core/server/services/link-redirection/index.js +2 -2
  375. package/core/server/services/link-redirection/link-redirect-repository.js +1 -1
  376. package/core/server/services/link-redirection/link-redirect.js +12 -13
  377. package/core/server/services/link-redirection/link-redirect.ts +22 -0
  378. package/core/server/services/link-redirection/link-redirects-service.js +1 -1
  379. package/core/server/services/link-tracking/full-post-link.js +2 -2
  380. package/core/server/services/link-tracking/index.js +1 -1
  381. package/core/server/services/mail/ghost-mailer.js +1 -1
  382. package/core/server/services/mail/index.js +1 -1
  383. package/core/server/services/media-inliner/external-media-inliner.js +2 -2
  384. package/core/server/services/media-inliner/service.js +4 -3
  385. package/core/server/services/member-attribution/index.js +1 -1
  386. package/core/server/services/member-welcome-emails/service.js +1 -1
  387. package/core/server/services/members/api.js +8 -2
  388. package/core/server/services/members/csv/index.js +10 -0
  389. package/core/server/services/members/csv/index.ts +3 -0
  390. package/core/server/services/members/csv/parse.js +94 -0
  391. package/core/server/services/members/csv/parse.ts +111 -0
  392. package/core/server/services/members/csv/types.js +2 -0
  393. package/core/server/services/members/csv/types.ts +39 -0
  394. package/core/server/services/members/csv/unparse.js +69 -0
  395. package/core/server/services/members/csv/unparse.ts +79 -0
  396. package/core/server/services/members/exporter/query.js +35 -8
  397. package/core/server/services/members/importer/config.js +35 -0
  398. package/core/server/services/members/importer/config.ts +39 -0
  399. package/core/server/services/members/importer/members-csv-importer.js +31 -13
  400. package/core/server/services/members/members-api/members-api.js +4 -2
  401. package/core/server/services/members/members-api/repositories/event-repository.js +13 -2
  402. package/core/server/services/members/members-api/services/member-bread-service.js +42 -19
  403. package/core/server/services/members/middleware.js +1 -1
  404. package/core/server/services/members/service.js +15 -2
  405. package/core/server/services/members/stripe-connect.js +1 -1
  406. package/core/server/services/members-custom-fields/actions.js +3 -0
  407. package/core/server/services/members-custom-fields/actions.ts +6 -2
  408. package/core/server/services/members-custom-fields/config.js +34 -0
  409. package/core/server/services/members-custom-fields/config.ts +38 -0
  410. package/core/server/services/members-custom-fields/definitions-service.js +156 -21
  411. package/core/server/services/members-custom-fields/definitions-service.ts +170 -25
  412. package/core/server/services/members-custom-fields/index.js +16 -3
  413. package/core/server/services/members-custom-fields/index.ts +17 -3
  414. package/core/server/services/members-custom-fields/values-service.js +56 -10
  415. package/core/server/services/members-custom-fields/values-service.ts +62 -10
  416. package/core/server/services/mentions/mention-sending-service.js +22 -2
  417. package/core/server/services/mentions/service.js +21 -5
  418. package/core/server/services/newsletters/index.js +1 -1
  419. package/core/server/services/oembed/oembed-service.js +122 -33
  420. package/core/server/services/oembed/service.js +4 -2
  421. package/core/server/services/offers/service.js +1 -1
  422. package/core/server/services/post-scheduling/index.js +4 -3
  423. package/core/server/services/post-scheduling/index.ts +4 -3
  424. package/core/server/services/post-scheduling/post-scheduling.js +1 -1
  425. package/core/server/services/post-scheduling/post-scheduling.ts +1 -1
  426. package/core/server/services/posts/post-email-handler.js +13 -6
  427. package/core/server/services/posts/post-scheduling.js +1 -1
  428. package/core/server/services/posts/posts-service-instance.js +1 -1
  429. package/core/server/services/posts/posts-service.js +2 -2
  430. package/core/server/services/posts-public/service.js +1 -1
  431. package/core/server/services/public-config/site.js +1 -1
  432. package/core/server/services/recommendations/recommendation-service-wrapper.js +1 -1
  433. package/core/server/services/recommendations/service/libraries.d.ts +0 -2
  434. package/core/server/services/route-settings/dynamic-routing-service.js +2 -3
  435. package/core/server/services/route-settings/index.js +1 -1
  436. package/core/server/services/route-settings/route-settings-parser.js +134 -124
  437. package/core/server/services/route-settings/route-settings-parser.ts +160 -125
  438. package/core/server/services/route-settings/validation-errors.js +178 -0
  439. package/core/server/services/route-settings/validation-errors.ts +196 -0
  440. package/core/server/services/settings/settings-service.js +2 -2
  441. package/core/server/services/settings-helpers/index.js +1 -1
  442. package/core/server/services/slack-notifications/service.js +1 -1
  443. package/core/server/services/slack-ping/constants.js +16 -0
  444. package/core/server/services/slack-ping/constants.ts +15 -0
  445. package/core/server/services/slack-ping/index.js +26 -204
  446. package/core/server/services/slack-ping/index.ts +35 -0
  447. package/core/server/services/slack-ping/slack-ping-service.js +214 -0
  448. package/core/server/services/slack-ping/slack-ping-service.ts +296 -0
  449. package/core/server/services/staff/index.js +1 -1
  450. package/core/server/services/stats/posts-stats-service.js +1 -1
  451. package/core/server/services/stats/service.js +1 -1
  452. package/core/server/services/stripe/service.js +1 -1
  453. package/core/server/services/tags-public/service.js +1 -1
  454. package/core/server/services/themes/validate.js +1 -1
  455. package/core/server/services/update-check/index.js +1 -1
  456. package/core/server/services/url/lazy-url-service.js +66 -6
  457. package/core/server/services/url/lazy-url-service.ts +73 -6
  458. package/core/server/services/url/url-generator.js +1 -1
  459. package/core/server/services/url/url-service-facade.js +68 -3
  460. package/core/server/services/url/url-service-facade.ts +79 -4
  461. package/core/server/services/url/url-service.js +1 -1
  462. package/core/server/services/url/urls.js +1 -1
  463. package/core/server/services/webhooks/index.js +19 -2
  464. package/core/server/services/webhooks/listen.js +6 -11
  465. package/core/server/services/webhooks/payload.js +1 -3
  466. package/core/server/services/webhooks/serialize.js +16 -1
  467. package/core/server/web/admin/app.js +1 -1
  468. package/core/server/web/admin/middleware/redirect-admin-urls.js +1 -1
  469. package/core/server/web/api/endpoints/admin/routes.js +1 -0
  470. package/core/server/web/api/middleware/cors.js +1 -1
  471. package/core/server/web/api/middleware/upload.js +4 -0
  472. package/core/server/web/gift-preview/controller.js +1 -1
  473. package/core/server/web/shared/middleware/redirect-amp-urls.js +1 -1
  474. package/core/server/web/shared/middleware/uncapitalise.js +2 -2
  475. package/core/server/web/shared/middleware/url-redirects.js +1 -1
  476. package/core/shared/config/defaults.json +8 -1
  477. package/core/shared/config/overrides.json +4 -0
  478. package/core/shared/url-utils.js +19 -15
  479. package/core/shared/url-utils.ts +20 -0
  480. package/ghost.js +0 -1
  481. package/package.json +26 -31
  482. package/pnpm-lock.yaml +681 -2789
  483. package/pnpm-workspace.yaml +31 -17
  484. package/components/tryghost-adapter-base-scheduling-0.0.0.tgz +0 -0
  485. package/components/tryghost-adapter-base-sso-0.0.0.tgz +0 -0
  486. package/core/built/admin/assets/Combination--5EZGaDs.js +0 -43
  487. package/core/built/admin/assets/Growth-C0NoKcHQ.js +0 -3
  488. package/core/built/admin/assets/Newsletters-Ed8kDjdy.js +0 -1
  489. package/core/built/admin/assets/Overview-CArPgRZt.js +0 -1
  490. package/core/built/admin/assets/Web-CjjYoVM3.js +0 -2
  491. package/core/built/admin/assets/account-migration-mfa5mNI9.js +0 -1
  492. package/core/built/admin/assets/activitypub/inbox-B3rjKMTv.js +0 -1221
  493. package/core/built/admin/assets/activitypub/layout-jcurg3rU.js +0 -1
  494. package/core/built/admin/assets/activitypub/use-activity-pub-queries-yqGRcnLn.js +0 -2569
  495. package/core/built/admin/assets/activitypub/users-CfyHIsTG.js +0 -1316
  496. package/core/built/admin/assets/automations-DkUnxNdv.js +0 -1
  497. package/core/built/admin/assets/automations-vjXofmV_.js +0 -1
  498. package/core/built/admin/assets/bluesky-sharing-T-pO3RcP.js +0 -1
  499. package/core/built/admin/assets/breadcrumb-CekPYTCn.js +0 -1
  500. package/core/built/admin/assets/button--i2uMDDn.js +0 -4
  501. package/core/built/admin/assets/card-CSWz_BXq.js +0 -1
  502. package/core/built/admin/assets/chart-helpers-C_4Ka_vT.js +0 -1
  503. package/core/built/admin/assets/chunk.524.8ce2c741473e2f0929dc.js +0 -25
  504. package/core/built/admin/assets/chunk.582.453459d6228d2955408b.js +0 -27
  505. package/core/built/admin/assets/code-editor-view-BfkwQto2.js +0 -1
  506. package/core/built/admin/assets/command-K75Jzs0G.js +0 -1
  507. package/core/built/admin/assets/comments-BEIi1DH-.js +0 -1
  508. package/core/built/admin/assets/container-BNXvlcjA.js +0 -1
  509. package/core/built/admin/assets/data-list-BmK5QMab.js +0 -1
  510. package/core/built/admin/assets/date-range-select-5sKzwbt7.js +0 -1
  511. package/core/built/admin/assets/disabled-sources-indicator-L_ssJTNr.js +0 -1
  512. package/core/built/admin/assets/dist-BBIJuyPg.js +0 -1
  513. package/core/built/admin/assets/dist-BHE0dp6o.js +0 -1
  514. package/core/built/admin/assets/dist-DJ6c4_De.js +0 -1
  515. package/core/built/admin/assets/dist-DW0QXQO3.js +0 -1
  516. package/core/built/admin/assets/dist-DxFLceO5.js +0 -1
  517. package/core/built/admin/assets/dist-QcACAOAD.js +0 -1
  518. package/core/built/admin/assets/dist-duxRDcQu.js +0 -177
  519. package/core/built/admin/assets/domain-CLaPZ9Rc.js +0 -1
  520. package/core/built/admin/assets/dropzone-E0bSX4_a.js +0 -5
  521. package/core/built/admin/assets/edit-profile-DqNPKaA4.js +0 -1
  522. package/core/built/admin/assets/editor-CkLXQDRp.js +0 -8
  523. package/core/built/admin/assets/empty-indicator-BTtOlgIo.js +0 -1
  524. package/core/built/admin/assets/esm-TSfGW7nz.js +0 -2
  525. package/core/built/admin/assets/explore-BEstLxKr.js +0 -1
  526. package/core/built/admin/assets/feed-C58p2voG.js +0 -1
  527. package/core/built/admin/assets/field-BSbDUHPv.js +0 -1
  528. package/core/built/admin/assets/filters-BgYv8Gt7.js +0 -1
  529. package/core/built/admin/assets/filters-DvenFkmK.js +0 -1
  530. package/core/built/admin/assets/framework-provider-jQktxm9P.js +0 -4
  531. package/core/built/admin/assets/ghost-dark-8590dea6cd695ff90f27d262c4464538.css +0 -1
  532. package/core/built/admin/assets/gift-link-modal-BPjXkEFt.js +0 -1
  533. package/core/built/admin/assets/growth-D4JNiVTe.js +0 -1
  534. package/core/built/admin/assets/hooks-Cjxexi_I.js +0 -2
  535. package/core/built/admin/assets/index-BacwlcOy.js +0 -67
  536. package/core/built/admin/assets/index-DxRNHH0o.css +0 -1
  537. package/core/built/admin/assets/inline-C22rnPro.js +0 -1
  538. package/core/built/admin/assets/input-group-CKfrILLP.js +0 -1
  539. package/core/built/admin/assets/jsx-runtime-CtMZ-PAS.js +0 -1
  540. package/core/built/admin/assets/kpi-card-DZxQKAIb.js +0 -1
  541. package/core/built/admin/assets/kpi-card-FidQ5zg3.js +0 -1
  542. package/core/built/admin/assets/kpi-helpers-C5T8Vaf1.js +0 -1
  543. package/core/built/admin/assets/kpi-tabs-_VRK6hGV.js +0 -1
  544. package/core/built/admin/assets/label-Cza7hPgn.js +0 -1
  545. package/core/built/admin/assets/label-picker-DgC_zLaw.js +0 -1
  546. package/core/built/admin/assets/layout-BxsXnKFX.js +0 -1
  547. package/core/built/admin/assets/limit-service-CTurluJQ.js +0 -20
  548. package/core/built/admin/assets/link-helpers-C3Gqy2cz.js +0 -1
  549. package/core/built/admin/assets/list-page-BtqCnow_.js +0 -1
  550. package/core/built/admin/assets/member-custom-fields-9uXFBqSt.js +0 -1
  551. package/core/built/admin/assets/member-detail-Dq6ZDIxo.js +0 -1
  552. package/core/built/admin/assets/members-CUWD9jal.js +0 -10
  553. package/core/built/admin/assets/modals-CIdwHVga.js +0 -31
  554. package/core/built/admin/assets/moderation-DTt04UDA.js +0 -1
  555. package/core/built/admin/assets/newsletter-cC8vcc9h.js +0 -1
  556. package/core/built/admin/assets/note-DZU0IY9e.js +0 -1
  557. package/core/built/admin/assets/notifications-NoHpBHJ4.js +0 -1
  558. package/core/built/admin/assets/nql-lang-CvCSOQG3.js +0 -19
  559. package/core/built/admin/assets/onboarding-route-Deg2hNm8.js +0 -1
  560. package/core/built/admin/assets/overview-BQPt_9w3.js +0 -1
  561. package/core/built/admin/assets/page-header-D6bEqPo2.js +0 -1
  562. package/core/built/admin/assets/post-analytics-header-BPw_B2_D.js +0 -1
  563. package/core/built/admin/assets/post-analytics-kp2_--XO.js +0 -1
  564. package/core/built/admin/assets/post-share-modal-Cave3Z4W.js +0 -1
  565. package/core/built/admin/assets/preferences-C7h3j_uy.js +0 -5
  566. package/core/built/admin/assets/profile-B2x4q2oB.js +0 -1
  567. package/core/built/admin/assets/router-provider-5fsX1CYn.js +0 -1
  568. package/core/built/admin/assets/select-PhPjIBBT.js +0 -1
  569. package/core/built/admin/assets/settings-D5P9Gwgo.js +0 -6
  570. package/core/built/admin/assets/settings-zUOzQV5I.js +0 -1
  571. package/core/built/admin/assets/share-modal-EfpfbuWF.js +0 -1
  572. package/core/built/admin/assets/social-urls-DgAF08Xc.js +0 -18
  573. package/core/built/admin/assets/sort-button-WOQYJm2H.js +0 -1
  574. package/core/built/admin/assets/source-icon-hm8Cj-Gx.js +0 -1
  575. package/core/built/admin/assets/stats-CnQDb62m.js +0 -1
  576. package/core/built/admin/assets/stats-view-CCfTJgSe.js +0 -1
  577. package/core/built/admin/assets/step-1-BGlhY4DS.js +0 -1
  578. package/core/built/admin/assets/step-2-D3sDEyFH.js +0 -1
  579. package/core/built/admin/assets/step-3-CvZoUdsc.js +0 -2
  580. package/core/built/admin/assets/switch-CNjkOmwz.js +0 -1
  581. package/core/built/admin/assets/table-C5nZYPnW.js +0 -1
  582. package/core/built/admin/assets/tabs-dtOcExye.js +0 -1
  583. package/core/built/admin/assets/tags-Dhm0FB_g.js +0 -1
  584. package/core/built/admin/assets/textarea-CeKw3wmV.js +0 -1
  585. package/core/built/admin/assets/toggle-group-DfhZIMTW.js +0 -1
  586. package/core/built/admin/assets/topic-filter-Cxe1Il41.js +0 -1
  587. package/core/built/admin/assets/typeof-Ca-duI-z.js +0 -1
  588. package/core/built/admin/assets/use-filter-params-Bub6BqNy.js +0 -1
  589. package/core/built/admin/assets/use-growth-stats-BJORomv0.js +0 -1
  590. package/core/built/admin/assets/use-reply-chain-data-B89YjVoo.js +0 -1
  591. package/core/built/admin/assets/use-simple-pagination-BL2SYXKi.js +0 -1
  592. package/core/built/admin/assets/virtual-list-DNJOSK9I.js +0 -1
  593. package/core/built/admin/assets/web-BHUE3YQc.js +0 -1
  594. package/core/server/adapters/scheduling/scheduling-base.js +0 -5
  595. package/core/server/adapters/sso/SSOBase.js +0 -5
  596. package/core/server/adapters/storage/index.js +0 -17
@@ -11,42 +11,62 @@ import type {
11
11
  TemplateRoute
12
12
  } from '@tryghost/adapter-base-route-settings';
13
13
  import {z} from 'zod';
14
- import errors from '@tryghost/errors';
15
14
  import tpl from '@tryghost/tpl';
15
+ import type {PathSegment} from './validation-errors';
16
+ import {describeValue, formatLocation, humanList, toValidationError, validationError} from './validation-errors';
16
17
 
17
18
  const messages = {
18
- validationError: 'The following definition "{at}" is invalid: {reason}',
19
- badDataError: 'Please wrap the data definition into a custom name.',
19
+ badDataError: '"{key}" is a reserved key. Please wrap the data definition into a custom name.',
20
20
  badDataHelp: 'Example:\n data:\n my-tag:\n resource: tags\n ...\n',
21
- authorDeprecatedError: 'Please choose a different name. We recommend not using author.'
21
+ authorDeprecatedError: '"author" is reserved. Please choose a different name. We recommend not using author.'
22
22
  };
23
23
 
24
- function validationError(at: string, reason: string, help?: string): errors.ValidationError {
25
- return new errors.ValidationError({
26
- message: tpl(messages.validationError, {at, reason}),
27
- ...(help ? {help} : {})
28
- });
29
- }
30
-
31
24
  const VALID_SHORTFORM_RESOURCES = ['tag', 'page', 'post', 'author'] as const;
32
25
  const VALID_LONGFORM_RESOURCES = ['tags', 'posts', 'pages', 'authors'] as const;
33
26
  const RESERVED_DATA_KEYS = ['resource', 'type', 'limit', 'order', 'include', 'filter', 'status', 'visibility', 'slug', 'redirect'];
34
27
 
35
- const DataShortFormSchema = z.string().superRefine((v, ctx) => {
36
- if (!/^[a-zA-Z0-9_]+\.[a-zA-Z0-9_-]+$/.test(v)) {
37
- ctx.addIssue({code: 'custom', message: tpl(messages.validationError, {at: v, reason: 'Incorrect Format. Please use e.g. tag.recipes'})});
38
- return;
28
+ // YAML parses a bare `filter:` as null and authors commonly quote numeric scalars
29
+ // (`limit: "100"`); the legacy validator accepted both, so treat an explicitly empty
30
+ // value as unset and coerce digit-only limit strings to numbers.
31
+ const OptionalStringField = z.string().nullish().transform(v => v ?? undefined);
32
+ const OptionalBooleanField = z.boolean().nullish().transform(v => v ?? undefined);
33
+ const LimitField = z.union([z.number(), z.literal('all'), z.string().regex(/^\d+$/).transform(Number)])
34
+ .nullish().transform(v => v ?? undefined);
35
+
36
+ const SHORTFORM_HELP = 'e.g. data: tag.recipes';
37
+
38
+ /**
39
+ * Shorthand data definitions look like `tag.recipes` — resource, dot, slug.
40
+ *
41
+ * `%s` stands in for the slug: on channel routes and collections, fetch-data
42
+ * substitutes it with the request's `slug` param at query time, which is how a
43
+ * wildcard route such as `/author/:slug/` serves a different author per request.
44
+ * There is no static slug that could replace it, so rejecting it would break
45
+ * those routes.
46
+ */
47
+ function validateDataShortForm(value: string, path: PathSegment[]): void {
48
+ if (!/^[a-zA-Z0-9_]+\.(?:%s|[a-zA-Z0-9_-]+)$/.test(value)) {
49
+ throw validationError(
50
+ formatLocation(path),
51
+ `"${value}" is not a valid data shorthand. Please use resource.slug, e.g. tag.recipes.`,
52
+ SHORTFORM_HELP
53
+ );
39
54
  }
40
- const resource = v.split('.')[0];
55
+
56
+ const resource = value.split('.')[0];
41
57
  if (!(VALID_SHORTFORM_RESOURCES as readonly string[]).includes(resource)) {
42
- ctx.addIssue({code: 'custom', message: tpl(messages.validationError, {at: v, reason: `${resource} not supported. Please use ${VALID_SHORTFORM_RESOURCES.join(', ')}.`})});
58
+ throw validationError(
59
+ formatLocation(path),
60
+ `resource "${resource}" is not supported. Please use ${humanList(VALID_SHORTFORM_RESOURCES)}.`,
61
+ SHORTFORM_HELP
62
+ );
43
63
  }
44
- });
64
+ }
45
65
 
46
66
  const DataReadEntrySchema = z.object({
47
67
  type: z.literal('read'),
48
68
  resource: z.enum(VALID_LONGFORM_RESOURCES),
49
- slug: z.string({message: 'slug is required for read data entries.'}).min(1, {message: 'slug is required for read data entries.'}),
69
+ slug: z.string().min(1),
50
70
  redirect: z.boolean().optional(),
51
71
  include: z.string().optional(),
52
72
  visibility: z.string().optional(),
@@ -56,90 +76,104 @@ const DataReadEntrySchema = z.object({
56
76
  const DataBrowseEntrySchema = z.object({
57
77
  type: z.literal('browse'),
58
78
  resource: z.enum(VALID_LONGFORM_RESOURCES),
59
- filter: z.string().optional(),
60
- limit: z.union([z.number(), z.literal('all')]).optional(),
61
- order: z.string().optional(),
62
- include: z.string().optional(),
63
- fields: z.string().optional(),
64
- visibility: z.string().optional(),
65
- status: z.string().optional(),
79
+ filter: OptionalStringField,
80
+ limit: LimitField,
81
+ order: OptionalStringField,
82
+ include: OptionalStringField,
83
+ fields: OptionalStringField,
84
+ visibility: OptionalStringField,
85
+ status: OptionalStringField,
66
86
  page: z.number().optional()
67
87
  });
68
88
 
69
89
  const DataLongFormEntrySchema = z.discriminatedUnion('type', [DataReadEntrySchema, DataBrowseEntrySchema]);
70
90
 
71
- function parseDataEntry(key: string, value: unknown): DataEntry {
91
+ const DATA_ENTRY_HELP = 'e.g.\n data:\n my-tag:\n resource: tags\n type: read\n slug: recipes\n';
92
+
93
+ function parseDataEntry(value: unknown, path: PathSegment[]): DataEntry {
72
94
  if (typeof value === 'string') {
73
- const result = DataShortFormSchema.safeParse(value);
74
- if (!result.success) {
75
- throw toValidationError(result.error);
76
- }
95
+ validateDataShortForm(value, path);
77
96
  return value as DataShortForm;
78
97
  }
79
98
 
80
- if (typeof value === 'object' && value !== null) {
99
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
81
100
  const result = DataLongFormEntrySchema.safeParse(value);
82
101
  if (!result.success) {
83
102
  const issue = result.error.issues[0];
84
- if (issue.message.includes('discriminator')) {
85
- const typeVal = (value as Record<string, unknown>).type;
86
- if (typeVal === undefined) {
87
- throw validationError(JSON.stringify(value), 'type is required.');
103
+ const at = formatLocation(path);
104
+ const record = value as Record<string, unknown>;
105
+
106
+ // A failed `type` discriminator surfaces as a union issue on that
107
+ // key — matched on the code rather than the wording, which zod
108
+ // is free to change.
109
+ if (issue.code === 'invalid_union' && issue.path[0] === 'type') {
110
+ if (record.type === undefined) {
111
+ throw validationError(at, 'type is required. Please use read or browse.', DATA_ENTRY_HELP);
88
112
  }
89
- throw validationError(JSON.stringify(value), `${typeVal} not supported. Please use read, browse.`);
113
+ throw validationError(at, `type "${record.type}" is not supported. Please use read or browse.`, DATA_ENTRY_HELP);
90
114
  }
91
- if (issue.path?.includes('resource')) {
92
- const resVal = (value as Record<string, unknown>).resource;
93
- if (resVal === undefined) {
94
- throw validationError(JSON.stringify(value), 'resource is required.');
115
+ if (issue.path.includes('resource')) {
116
+ if (record.resource === undefined) {
117
+ throw validationError(at, `resource is required. Please use ${humanList(VALID_LONGFORM_RESOURCES)}.`, DATA_ENTRY_HELP);
95
118
  }
96
- throw validationError(JSON.stringify(value), `${resVal} not supported. Please use ${VALID_LONGFORM_RESOURCES.join(', ')}.`);
119
+ throw validationError(at, `resource "${record.resource}" is not supported. Please use ${humanList(VALID_LONGFORM_RESOURCES)}.`, DATA_ENTRY_HELP);
120
+ }
121
+ if (issue.path.includes('slug') && !record.slug) {
122
+ throw validationError(at, 'slug is required for read data entries.', DATA_ENTRY_HELP);
123
+ }
124
+ throw toValidationError(result.error, path, value);
125
+ }
126
+ // Fields set to an explicitly empty value parse to undefined — drop the
127
+ // keys so "unset" means absent in the domain model and serialized output.
128
+ const entry: Record<string, unknown> = result.data;
129
+ for (const entryKey of Object.keys(entry)) {
130
+ if (entry[entryKey] === undefined) {
131
+ delete entry[entryKey];
97
132
  }
98
- throw toValidationError(result.error);
99
133
  }
100
134
  return result.data;
101
135
  }
102
136
 
103
- throw validationError(key, 'Incorrect Format. Please use e.g. tag.recipes');
137
+ throw validationError(
138
+ formatLocation(path),
139
+ `a data entry must be a shorthand like tag.recipes, or a map with type and resource, but ${describeValue(value)} was provided.`,
140
+ DATA_ENTRY_HELP
141
+ );
104
142
  }
105
143
 
106
- function parseRouteData(data: unknown): RouteData {
144
+ function parseRouteData(data: unknown, path: PathSegment[]): RouteData {
107
145
  if (typeof data === 'string') {
108
- const result = DataShortFormSchema.safeParse(data);
109
- if (!result.success) {
110
- throw toValidationError(result.error);
111
- }
146
+ validateDataShortForm(data, path);
112
147
  return data as DataShortForm;
113
148
  }
114
149
 
115
- if (typeof data !== 'object' || data === null) {
116
- throw validationError(String(data), 'Incorrect Format. Please use e.g. tag.recipes');
150
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
151
+ throw validationError(
152
+ formatLocation(path),
153
+ `data must be a shorthand like tag.recipes, or a map of named data entries, but ${describeValue(data)} was provided.`,
154
+ DATA_ENTRY_HELP
155
+ );
117
156
  }
118
157
 
119
158
  const record = data as Record<string, unknown>;
120
159
 
121
160
  for (const key of Object.keys(record)) {
122
161
  if (RESERVED_DATA_KEYS.includes(key)) {
123
- throw new errors.ValidationError({
124
- message: tpl(messages.badDataError),
125
- help: messages.badDataHelp
126
- });
162
+ throw validationError(formatLocation([...path, key]), tpl(messages.badDataError, {key}), messages.badDataHelp);
127
163
  }
128
164
  if (key === 'author') {
129
- throw new errors.ValidationError({
130
- message: tpl(messages.authorDeprecatedError)
131
- });
165
+ throw validationError(formatLocation([...path, key]), messages.authorDeprecatedError);
132
166
  }
133
167
  }
134
168
 
135
169
  const parsed: Record<string, DataEntry> = {};
136
170
  for (const [key, value] of Object.entries(record)) {
137
- parsed[key] = parseDataEntry(key, value);
171
+ parsed[key] = parseDataEntry(value, [...path, key]);
138
172
  }
139
173
  return parsed;
140
174
  }
141
175
 
142
- const TemplateField = z.union([z.string(), z.array(z.string())]).optional().default([])
176
+ const TemplateField = z.union([z.string(), z.array(z.string())]).nullish().default([])
143
177
  .transform(v => {
144
178
  if (!v || (Array.isArray(v) && v.length === 0)) {
145
179
  return [];
@@ -147,18 +181,20 @@ const TemplateField = z.union([z.string(), z.array(z.string())]).optional().defa
147
181
  return Array.isArray(v) ? v : [v];
148
182
  });
149
183
 
150
- const RouteObjectSchema = z.object({
184
+ // The schemas are built per route/collection so that errors raised while
185
+ // parsing nested `data` can name the exact path they came from.
186
+ const routeObjectSchema = (path: PathSegment[]) => z.object({
151
187
  controller: z.literal('channel').optional(),
152
188
  template: TemplateField,
153
189
  data: z.unknown().optional(),
154
- content_type: z.string().optional(),
155
- filter: z.string().optional(),
156
- order: z.string().optional(),
157
- limit: z.union([z.number(), z.literal('all')]).optional(),
158
- rss: z.boolean().optional()
190
+ content_type: OptionalStringField,
191
+ filter: OptionalStringField,
192
+ order: OptionalStringField,
193
+ limit: LimitField,
194
+ rss: OptionalBooleanField
159
195
  }).transform((val): Omit<Route, 'path'> => {
160
196
  const templates = val.template;
161
- const data = val.data !== undefined ? parseRouteData(val.data) : undefined;
197
+ const data = val.data !== undefined ? parseRouteData(val.data, [...path, 'data']) : undefined;
162
198
 
163
199
  if (val.controller === 'channel') {
164
200
  const route: Omit<ChannelRoute, 'path'> = {
@@ -199,16 +235,16 @@ const RouteObjectSchema = z.object({
199
235
  return route;
200
236
  });
201
237
 
202
- const RawCollectionValueSchema = z.object({
203
- permalink: z.string({message: 'Please define a permalink route.'}).optional(),
238
+ const collectionValueSchema = (path: PathSegment[]) => z.object({
239
+ permalink: z.string().optional(),
204
240
  template: TemplateField,
205
241
  data: z.unknown().optional(),
206
- filter: z.string().optional(),
207
- order: z.string().optional(),
208
- limit: z.union([z.number(), z.literal('all')]).optional(),
209
- rss: z.boolean().optional()
242
+ filter: OptionalStringField,
243
+ order: OptionalStringField,
244
+ limit: LimitField,
245
+ rss: OptionalBooleanField
210
246
  }).transform((val): Omit<CollectionConfig, 'path'> => {
211
- const data = val.data !== undefined ? parseRouteData(val.data) : undefined;
247
+ const data = val.data !== undefined ? parseRouteData(val.data, [...path, 'data']) : undefined;
212
248
  const collection: Omit<CollectionConfig, 'path'> = {
213
249
  permalink: val.permalink ?? '',
214
250
  templates: val.template
@@ -233,15 +269,35 @@ const RawCollectionValueSchema = z.object({
233
269
 
234
270
  const RouteSettingsSchema = z.object({
235
271
  routes: z.record(z.string(), z.unknown()).nullable().optional().default({}),
236
- collections: z.record(z.string(), RawCollectionValueSchema).nullable().optional().default({}),
272
+ collections: z.record(z.string(), z.unknown()).nullable().optional().default({}),
237
273
  taxonomies: z.record(z.string(), z.string()).nullable().optional().default({})
238
274
  });
239
275
 
240
- function toValidationError(error: z.ZodError): errors.ValidationError {
241
- const issue = error.issues[0];
242
- return new errors.ValidationError({
243
- message: issue.message
244
- });
276
+ /**
277
+ * Every path in routes.yaml — route paths, collection paths, permalinks and
278
+ * taxonomy permalinks — follows the same rules, so they share one message set.
279
+ *
280
+ * `allowParamNotation` is set for route and collection keys, which are mounted on
281
+ * Express verbatim: nothing rewrites `{param}` to `:param` for them the way it
282
+ * does for permalinks and taxonomies. So `/author/:slug/` is a working wildcard
283
+ * route on live sites and `/author/{slug}/` would be a dead literal path —
284
+ * rejecting :param there would break the former, and the suggested rewrite would
285
+ * silently produce the latter.
286
+ */
287
+ function validatePath(value: string, path: PathSegment[], example: string, {allowParamNotation = false}: {allowParamNotation?: boolean} = {}): void {
288
+ const at = formatLocation(path);
289
+
290
+ if (!value.startsWith('/')) {
291
+ throw validationError(at, `"${value}" is missing a leading slash. Please use e.g. ${example}.`);
292
+ }
293
+ if (!value.endsWith('/')) {
294
+ throw validationError(at, `"${value}" is missing a trailing slash. Please use e.g. ${example}.`);
295
+ }
296
+ if (!allowParamNotation && /\/:\w+/.test(value)) {
297
+ // Suggest the same path in the notation Ghost expects, rather than a
298
+ // generic example the author then has to translate.
299
+ throw validationError(at, `"${value}" uses the :param notation. Please use "${value.replace(/\/:(\w+)/g, '/{$1}')}".`);
300
+ }
245
301
  }
246
302
 
247
303
  export function parseRouteSettings(raw: unknown, yamlSource: string): RouteSettings {
@@ -249,7 +305,7 @@ export function parseRouteSettings(raw: unknown, yamlSource: string): RouteSetti
249
305
 
250
306
  const parsed = RouteSettingsSchema.safeParse(obj);
251
307
  if (!parsed.success) {
252
- throw toValidationError(parsed.error);
308
+ throw toValidationError(parsed.error, [], obj);
253
309
  }
254
310
 
255
311
  const {routes: rawRoutes, collections: rawCollections, taxonomies: rawTaxonomies} = parsed.data;
@@ -257,18 +313,11 @@ export function parseRouteSettings(raw: unknown, yamlSource: string): RouteSetti
257
313
  const routes: Route[] = [];
258
314
  if (rawRoutes) {
259
315
  for (const [path, value] of Object.entries(rawRoutes)) {
260
- if (!path.startsWith('/')) {
261
- throw validationError(path, 'A leading slash is required.');
262
- }
263
- if (!path.endsWith('/')) {
264
- throw validationError(path, 'A trailing slash is required.');
265
- }
266
- if (/\/:\w+/.test(path)) {
267
- throw validationError(path, 'Please use the following notation e.g. /{slug}/.');
268
- }
316
+ const routeLocation: PathSegment[] = ['routes', path];
317
+ validatePath(path, routeLocation, '/about/', {allowParamNotation: true});
269
318
 
270
319
  if (value === null || value === undefined) {
271
- throw validationError(path, 'Please define a template.', 'e.g. /about/: about');
320
+ throw validationError(formatLocation(routeLocation), 'Please define a template, e.g. /about/: about.');
272
321
  }
273
322
 
274
323
  if (typeof value === 'string') {
@@ -276,14 +325,14 @@ export function parseRouteSettings(raw: unknown, yamlSource: string): RouteSetti
276
325
  continue;
277
326
  }
278
327
 
279
- const routeResult = RouteObjectSchema.safeParse(value);
328
+ const routeResult = routeObjectSchema(routeLocation).safeParse(value);
280
329
  if (!routeResult.success) {
281
- throw toValidationError(routeResult.error);
330
+ throw toValidationError(routeResult.error, routeLocation, value);
282
331
  }
283
332
 
284
333
  const route = routeResult.data;
285
334
  if (route.type === 'template' && (!route.templates || route.templates.length === 0) && !route.data && !(route as Omit<TemplateRoute, 'path'>).contentType) {
286
- throw validationError(path, 'Please define a template.', 'e.g. /about/: about');
335
+ throw validationError(formatLocation(routeLocation), 'Please define a template, e.g. /about/: about.');
287
336
  }
288
337
 
289
338
  routes.push({...route, path} as Route);
@@ -293,49 +342,35 @@ export function parseRouteSettings(raw: unknown, yamlSource: string): RouteSetti
293
342
  const collections: CollectionConfig[] = [];
294
343
  if (rawCollections) {
295
344
  for (const [path, value] of Object.entries(rawCollections)) {
296
- if (!path.startsWith('/')) {
297
- throw validationError(path, 'A leading slash is required.');
298
- }
299
- if (!path.endsWith('/')) {
300
- throw validationError(path, 'A trailing slash is required.');
301
- }
302
- if (/\/:\w+/.test(path)) {
303
- throw validationError(path, 'Please use the following notation e.g. /{slug}/.');
304
- }
305
- if (!value.permalink) {
306
- throw validationError(path, 'Please define a permalink route.', 'e.g. permalink: /{slug}/');
307
- }
308
- if (!value.permalink.startsWith('/')) {
309
- throw validationError(value.permalink, 'A leading slash is required.');
310
- }
311
- if (!value.permalink.endsWith('/')) {
312
- throw validationError(value.permalink, 'A trailing slash is required.');
345
+ const collectionLocation: PathSegment[] = ['collections', path];
346
+ validatePath(path, collectionLocation, '/blog/', {allowParamNotation: true});
347
+
348
+ const collectionResult = collectionValueSchema(collectionLocation).safeParse(value);
349
+ if (!collectionResult.success) {
350
+ throw toValidationError(collectionResult.error, collectionLocation, value);
313
351
  }
314
- if (/\/:\w+/.test(value.permalink)) {
315
- throw validationError(value.permalink, 'Please use the following notation e.g. /{slug}/.');
352
+
353
+ const collection = collectionResult.data;
354
+ if (!collection.permalink) {
355
+ throw validationError(formatLocation(collectionLocation), 'Please define a permalink route, e.g. permalink: /{slug}/.');
316
356
  }
317
- collections.push({...value, path});
357
+ validatePath(collection.permalink, [...collectionLocation, 'permalink'], '/{slug}/');
358
+
359
+ collections.push({...collection, path});
318
360
  }
319
361
  }
320
362
 
321
363
  const taxonomies: TaxonomyConfig = {};
322
364
  if (rawTaxonomies) {
323
365
  for (const [key, value] of Object.entries(rawTaxonomies)) {
366
+ const taxonomyLocation: PathSegment[] = ['taxonomies', key];
324
367
  if (!['tag', 'author'].includes(key)) {
325
- throw validationError(key, 'Unknown taxonomy.');
368
+ throw validationError(formatLocation(taxonomyLocation), 'Unknown taxonomy. Please use tag or author.');
326
369
  }
327
370
  if (!value) {
328
- throw validationError(key, 'Please define a taxonomy permalink route.', 'e.g. tag: /tag/{slug}/');
329
- }
330
- if (!value.startsWith('/')) {
331
- throw validationError(value, 'A leading slash is required.');
332
- }
333
- if (!value.endsWith('/')) {
334
- throw validationError(value, 'A trailing slash is required.');
335
- }
336
- if (/\/:\w+/.test(value)) {
337
- throw validationError(value, 'Please use the following notation e.g. /{slug}/.');
371
+ throw validationError(formatLocation(taxonomyLocation), `Please define a taxonomy permalink route, e.g. ${key}: /${key}/{slug}/.`);
338
372
  }
373
+ validatePath(value, taxonomyLocation, `/${key}/{slug}/`);
339
374
  if (key === 'tag') {
340
375
  taxonomies.tag = value;
341
376
  }
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatLocation = formatLocation;
7
+ exports.describeValue = describeValue;
8
+ exports.humanList = humanList;
9
+ exports.validationError = validationError;
10
+ exports.toValidationError = toValidationError;
11
+ const errors_1 = __importDefault(require("@tryghost/errors"));
12
+ const tpl_1 = __importDefault(require("@tryghost/tpl"));
13
+ const messages = {
14
+ validationError: 'The following definition "{at}" is invalid: {reason}'
15
+ };
16
+ const DOCS_URL = 'https://ghost.org/docs/themes/routing/';
17
+ const MAX_SHOWN_LENGTH = 40;
18
+ /**
19
+ * Renders the path to the offending value the way an author would find it in
20
+ * their file, e.g. `routes['/about/'].template[1]`.
21
+ */
22
+ function formatLocation(path) {
23
+ if (path.length === 0) {
24
+ return 'routes.yaml';
25
+ }
26
+ return path.reduce((out, segment) => {
27
+ if (typeof segment === 'number') {
28
+ return `${out}[${segment}]`;
29
+ }
30
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(segment)) {
31
+ return out ? `${out}.${segment}` : segment;
32
+ }
33
+ return `${out}['${segment.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}']`;
34
+ }, '');
35
+ }
36
+ /**
37
+ * Names what the author actually wrote, so the error can contrast it with what
38
+ * was expected. Reads as the "but <this> was provided" half of a message.
39
+ */
40
+ function describeValue(value) {
41
+ if (value === null || value === undefined) {
42
+ return 'nothing';
43
+ }
44
+ if (Array.isArray(value)) {
45
+ return 'a list';
46
+ }
47
+ if (value instanceof Date) {
48
+ return 'a date';
49
+ }
50
+ if (typeof value === 'object') {
51
+ return 'a map';
52
+ }
53
+ if (typeof value === 'string') {
54
+ const shown = value.length > MAX_SHOWN_LENGTH ? `${value.slice(0, MAX_SHOWN_LENGTH)}…` : value;
55
+ return `the text "${shown}"`;
56
+ }
57
+ if (typeof value === 'number' || typeof value === 'boolean') {
58
+ return String(value);
59
+ }
60
+ return 'an unsupported value';
61
+ }
62
+ /**
63
+ * Lists options the way a sentence would, e.g. "posts, pages or tags".
64
+ */
65
+ function humanList(items) {
66
+ if (items.length < 2) {
67
+ return items.join('');
68
+ }
69
+ return `${items.slice(0, -1).join(', ')} or ${items[items.length - 1]}`;
70
+ }
71
+ /**
72
+ * What each key in routes.yaml accepts, phrased so the message reads
73
+ * "<key> must be <expectation>".
74
+ */
75
+ const FIELD_EXPECTATIONS = {
76
+ routes: 'a map of route paths (e.g. /about/: about)',
77
+ collections: 'a map of collection paths (e.g. /blog/: {permalink: /{slug}/})',
78
+ taxonomies: 'a map of tag and/or author permalinks (e.g. tag: /tag/{slug}/)',
79
+ controller: 'channel, the only supported controller',
80
+ template: 'a template name, or a list of template names (e.g. template: index)',
81
+ permalink: 'a route with a leading and trailing slash (e.g. permalink: /{slug}/)',
82
+ content_type: 'a content type (e.g. content_type: text/xml)',
83
+ filter: 'a filter string (e.g. filter: featured:true)',
84
+ order: 'an order string (e.g. order: published_at desc)',
85
+ limit: 'a number or "all" (e.g. limit: 5)',
86
+ include: 'a comma-separated string (e.g. include: authors,tags)',
87
+ fields: 'a comma-separated string (e.g. fields: title,slug)',
88
+ visibility: 'a string (e.g. visibility: public)',
89
+ status: 'a string (e.g. status: published)',
90
+ slug: 'a slug (e.g. slug: welcome)',
91
+ page: 'a number (e.g. page: 2)',
92
+ rss: 'true or false',
93
+ redirect: 'true or false'
94
+ };
95
+ const CONTAINER_EXPECTATIONS = {
96
+ routes: { subject: 'a route', expectation: 'a template name, or a map of route options (e.g. template, controller, data)' },
97
+ collections: { subject: 'a collection', expectation: 'a map of collection options (e.g. permalink, template, filter)' }
98
+ };
99
+ /**
100
+ * Keys that accept a list: saying "a list was provided" would contradict the
101
+ * expectation, so the entries are blamed instead.
102
+ */
103
+ const LIST_ENTRY_PROBLEMS = {
104
+ template: 'one or more of the entries is not a template name'
105
+ };
106
+ function lookup(table, key) {
107
+ return typeof key === 'string' && Object.prototype.hasOwnProperty.call(table, key) ? table[key] : undefined;
108
+ }
109
+ /**
110
+ * The key a failure belongs to — for a list entry that is the key holding the
111
+ * list, not the index.
112
+ */
113
+ function fieldKey(path) {
114
+ const last = path[path.length - 1];
115
+ return typeof last === 'number' ? path[path.length - 2] : last;
116
+ }
117
+ /**
118
+ * Resolves the "<subject> must be <expectation>" half of a message for the
119
+ * value at `path`. Returns null when the key is unknown, in which case the
120
+ * caller falls back to the underlying schema message.
121
+ */
122
+ function describeExpectation(path) {
123
+ if (path.length === 0) {
124
+ return { subject: 'the file', expectation: 'a map of routes, collections or taxonomies' };
125
+ }
126
+ const [section] = path;
127
+ // A whole section, e.g. `routes: hello`
128
+ if (path.length === 1) {
129
+ const expectation = lookup(FIELD_EXPECTATIONS, section);
130
+ return expectation ? { subject: String(section), expectation } : null;
131
+ }
132
+ // A single route or collection, e.g. `routes: {/about/: [a, b]}`
133
+ if (path.length === 2 && typeof section === 'string' && Object.prototype.hasOwnProperty.call(CONTAINER_EXPECTATIONS, section)) {
134
+ return CONTAINER_EXPECTATIONS[section];
135
+ }
136
+ // A taxonomy permalink, e.g. `taxonomies: {tag: 5}`
137
+ if (path.length === 2 && section === 'taxonomies') {
138
+ return { subject: `the ${String(path[1])} taxonomy`, expectation: `a permalink route (e.g. ${String(path[1])}: /${String(path[1])}/{slug}/)` };
139
+ }
140
+ const expectation = lookup(FIELD_EXPECTATIONS, fieldKey(path));
141
+ return expectation ? { subject: String(fieldKey(path)), expectation } : null;
142
+ }
143
+ function valueAtPath(value, path) {
144
+ return path.reduce((current, segment) => {
145
+ if (current === null || typeof current !== 'object') {
146
+ return undefined;
147
+ }
148
+ return current[segment];
149
+ }, value);
150
+ }
151
+ function validationError(at, reason, help) {
152
+ return new errors_1.default.ValidationError({
153
+ message: (0, tpl_1.default)(messages.validationError, { at, reason }),
154
+ ...(help ? { help } : {})
155
+ });
156
+ }
157
+ /**
158
+ * Turns a schema failure into an error that names the exact key in routes.yaml
159
+ * and says what that key accepts — schema messages on their own only say
160
+ * things like "Invalid input: expected string, received number".
161
+ *
162
+ * @param error the schema failure
163
+ * @param basePath path of the value that was handed to the schema
164
+ * @param value that same value, used to describe what the author wrote
165
+ */
166
+ function toValidationError(error, basePath = [], value) {
167
+ const issue = error.issues[0];
168
+ const issuePath = issue.path;
169
+ const path = [...basePath, ...issuePath];
170
+ const expectation = describeExpectation(path);
171
+ if (!expectation) {
172
+ return validationError(formatLocation(path), issue.message, DOCS_URL);
173
+ }
174
+ const provided = valueAtPath(value, issuePath);
175
+ const listProblem = Array.isArray(provided) ? lookup(LIST_ENTRY_PROBLEMS, fieldKey(path)) : undefined;
176
+ const found = listProblem ?? `${describeValue(provided)} was provided`;
177
+ return validationError(formatLocation(path), `${expectation.subject} must be ${expectation.expectation}, but ${found}.`, DOCS_URL);
178
+ }