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
@@ -2,7 +2,7 @@ const moment = require('moment-timezone');
2
2
  const path = require('path');
3
3
  const fs = require('fs-extra');
4
4
  const metrics = require('@tryghost/metrics');
5
- const membersCSV = require('@tryghost/members-csv');
5
+ const membersCSV = require('../csv');
6
6
  const errors = require('@tryghost/errors');
7
7
  const tpl = require('@tryghost/tpl');
8
8
  const emailTemplate = require('./email-template');
@@ -61,9 +61,10 @@ module.exports = class MembersCSVImporter {
61
61
  /**
62
62
  * @param {MembersCSVImporterOptions} options
63
63
  */
64
- constructor({storagePath, getTimezone, getMembersRepository, getDefaultTier, getTierByName, getGiftService, sendEmail, isSet, addJob, knex, urlFor, context, stripeUtils}) {
64
+ constructor({storagePath, getTimezone, getInlineThreshold, getMembersRepository, getDefaultTier, getTierByName, getGiftService, sendEmail, isSet, addJob, knex, urlFor, context, stripeUtils}) {
65
65
  this._storagePath = storagePath;
66
66
  this._getTimezone = getTimezone;
67
+ this._getInlineThreshold = getInlineThreshold;
67
68
  this._getMembersRepository = getMembersRepository;
68
69
  this._getDefaultTier = getDefaultTier;
69
70
  this._getTierByName = getTierByName;
@@ -87,11 +88,10 @@ module.exports = class MembersCSVImporter {
87
88
  *
88
89
  * @param {string} inputFilePath - The path to the CSV to prepare
89
90
  * @param {Object.<string, string>} [headerMapping] - An object whose keys are headers in the input CSV and values are the header to replace it with
90
- * @param {Array<string>} [defaultLabels] - A list of labels to apply to every member
91
91
  *
92
92
  * @returns {Promise<{filePath: string, batches: number, metadata: Object.<string, any>}>} - A promise resolving to the data including filePath of "prepared" CSV
93
93
  */
94
- async prepare(inputFilePath, headerMapping, defaultLabels) {
94
+ async prepare(inputFilePath, headerMapping) {
95
95
  headerMapping = headerMapping || DEFAULT_CSV_HEADER_MAPPING;
96
96
  // @NOTE: investigate why is it "1" and do we even need this concept anymore?
97
97
  const batchSize = 1;
@@ -108,10 +108,16 @@ module.exports = class MembersCSVImporter {
108
108
  }
109
109
 
110
110
  // completely rely on explicit user input for header mappings
111
- const rows = await membersCSV.parse(inputFilePath, headerMapping, defaultLabels);
112
- const columns = Object.keys(rows[0]);
111
+ const rows = await membersCSV.parse(inputFilePath, headerMapping);
112
+ // Columns come from every row, not the first. A row with fewer fields than
113
+ // the header parses to fewer keys, so reading the schema off one row drops
114
+ // the columns it happened to omit for the whole file. A file of headers and
115
+ // no rows has no columns, and serialising nothing has nothing to say.
116
+ const columns = [...new Set(rows.flatMap(row => Object.keys(row)))];
113
117
  const numberOfBatches = Math.ceil(rows.length / batchSize);
114
- const mappedCSV = membersCSV.unparse(rows, columns);
118
+ // No formula escaping: this file is only ever read back by the import, not
119
+ // opened by a person, and escaping would be stored on the member verbatim.
120
+ const mappedCSV = columns.length ? membersCSV.unparse(rows, columns, {escapeFormulae: false}) : '';
115
121
 
116
122
  const hasStripeData = !!(rows.find(function rowHasStripeData(row) {
117
123
  return !!row.stripe_customer_id;
@@ -133,8 +139,17 @@ module.exports = class MembersCSVImporter {
133
139
  *
134
140
  * @param {string} filePath - the path to a "prepared" CSV file
135
141
  */
136
- async perform(filePath) {
142
+ async perform(filePath, globalLabels = []) {
137
143
  const performStart = Date.now();
144
+ // Copied per row, not shared: the member model stamps ids and trims names onto
145
+ // label objects in place, one of these is the caller's import label that
146
+ // process() looks up again once the import finishes, and each row runs in its
147
+ // own transaction that can roll back.
148
+ const cloneGlobalLabels = () => (globalLabels || [])
149
+ .map(label => (typeof label === 'string' ? {name: label} : {...label}))
150
+ // A caller can hand in a nameless label; the model would drop it, but the
151
+ // round-trip this replaced dropped it before the model ever saw it.
152
+ .filter(label => label.name);
138
153
  const rows = await membersCSV.parse(filePath, DEFAULT_CSV_HEADER_MAPPING);
139
154
 
140
155
  const defaultTier = await this._getDefaultTier();
@@ -182,7 +197,10 @@ module.exports = class MembersCSVImporter {
182
197
  note: row.note,
183
198
  subscribed: row.subscribed,
184
199
  created_at: createdAt,
185
- labels: row.labels
200
+ // Form labels are applied here rather than folded into the parsed
201
+ // rows, so a name that contains a comma does not go through the
202
+ // prepared file's comma-separated labels column and split in two.
203
+ labels: [...row.labels, ...cloneGlobalLabels()]
186
204
  };
187
205
  const existingMember = await membersRepository.get({email: memberValues.email}, {
188
206
  ...options,
@@ -419,12 +437,12 @@ module.exports = class MembersCSVImporter {
419
437
  */
420
438
  async process({pathToCSV, headerMapping, globalLabels, importLabel, user, LabelModel, forceInline, verificationTrigger}) {
421
439
  const meta = {};
422
- const job = await this.prepare(pathToCSV, headerMapping, globalLabels);
440
+ const job = await this.prepare(pathToCSV, headerMapping);
423
441
 
424
442
  meta.originalImportSize = job.batches;
425
443
 
426
- if ((job.batches <= 500 && !job.metadata.hasStripeData) || forceInline) {
427
- const result = await this.perform(job.filePath);
444
+ if ((job.batches <= this._getInlineThreshold() && !job.metadata.hasStripeData) || forceInline) {
445
+ const result = await this.perform(job.filePath, globalLabels);
428
446
  const importLabelModel = result.imported ? await LabelModel.findOne(importLabel) : null;
429
447
  await verificationTrigger.testImportThreshold();
430
448
 
@@ -442,7 +460,7 @@ module.exports = class MembersCSVImporter {
442
460
  this._addJob({
443
461
  job: async () => {
444
462
  try {
445
- const result = await this.perform(job.filePath);
463
+ const result = await this.perform(job.filePath, globalLabels);
446
464
  const importLabelModel = result.imported ? await LabelModel.findOne(importLabel) : null;
447
465
  const emailContent = this.generateCompletionEmail(result, {
448
466
  emailRecipient,
@@ -81,10 +81,11 @@ module.exports = function MembersAPI({
81
81
  sentry,
82
82
  settingsHelpers,
83
83
  urlUtils,
84
+ urlService,
84
85
  commentsService,
85
86
  emailAddressService,
86
87
  giftService,
87
- customFieldsService
88
+ customFieldValues
88
89
  }) {
89
90
  const tokenService = new TokenService({
90
91
  privateKey,
@@ -123,6 +124,7 @@ module.exports = function MembersAPI({
123
124
  });
124
125
 
125
126
  const eventRepository = new EventRepository({
127
+ urlService,
126
128
  DonationPaymentEvent,
127
129
  EmailRecipient,
128
130
  MemberSubscribeEvent,
@@ -167,7 +169,7 @@ module.exports = function MembersAPI({
167
169
  nextPaymentCalculator,
168
170
  commentsService,
169
171
  giftService,
170
- customFieldsService
172
+ customFieldValues
171
173
  });
172
174
 
173
175
  const geolocationService = new GeolocationService();
@@ -34,6 +34,7 @@ module.exports = class EventRepository {
34
34
  Comment,
35
35
  labsService,
36
36
  memberAttributionService,
37
+ urlService,
37
38
  MemberEmailChangeEvent,
38
39
  AutomatedEmailRecipient,
39
40
  Gift
@@ -47,6 +48,7 @@ module.exports = class EventRepository {
47
48
  this._EmailRecipient = EmailRecipient;
48
49
  this._Comment = Comment;
49
50
  this._labsService = labsService;
51
+ this._urlService = urlService;
50
52
  this._MemberCreatedEvent = MemberCreatedEvent;
51
53
  this._SubscriptionCreatedEvent = SubscriptionCreatedEvent;
52
54
  this._MemberLinkClickEvent = MemberLinkClickEvent;
@@ -59,6 +61,12 @@ module.exports = class EventRepository {
59
61
  this._knex = db.knex;
60
62
  }
61
63
 
64
+ // the URL service needs the attributed post's relations (e.g. tags for a
65
+ // tag-filtered collection) to build its URL; prefix them onto the eager-load
66
+ #attributionPostRelations(prefix) {
67
+ return this._urlService.getRequiredRelations().map(relation => `${prefix}.${relation}`);
68
+ }
69
+
62
70
  async getEventTimeline(options = {}) {
63
71
  if (!options.limit) {
64
72
  options.limit = 10;
@@ -213,6 +221,7 @@ module.exports = class EventRepository {
213
221
  withRelated: [
214
222
  'member',
215
223
  'subscriptionCreatedEvent.postAttribution',
224
+ ...this.#attributionPostRelations('subscriptionCreatedEvent.postAttribution'),
216
225
  'subscriptionCreatedEvent.userAttribution',
217
226
  'subscriptionCreatedEvent.tagAttribution',
218
227
  'subscriptionCreatedEvent.memberCreatedEvent',
@@ -343,6 +352,7 @@ module.exports = class EventRepository {
343
352
  withRelated: [
344
353
  'member',
345
354
  'postAttribution',
355
+ ...this.#attributionPostRelations('postAttribution'),
346
356
  'userAttribution',
347
357
  'tagAttribution',
348
358
  'signupStatusEvent'
@@ -402,6 +412,7 @@ module.exports = class EventRepository {
402
412
  withRelated: [
403
413
  'member',
404
414
  'postAttribution',
415
+ ...this.#attributionPostRelations('postAttribution'),
405
416
  'userAttribution',
406
417
  'tagAttribution'
407
418
  ],
@@ -591,7 +602,7 @@ module.exports = class EventRepository {
591
602
  async getCommentEvents(options = {}, filter) {
592
603
  options = {
593
604
  ...options,
594
- withRelated: ['member', 'post', 'post.tags', 'post.authors', 'parent'],
605
+ withRelated: ['member', 'post', ...this.#attributionPostRelations('post'), 'parent'],
595
606
  filter: 'member_id:-null+custom:true',
596
607
  useBasicCount: true,
597
608
  mongoTransformer: chainTransformers(
@@ -625,7 +636,7 @@ module.exports = class EventRepository {
625
636
  async getClickEvents(options = {}, filter) {
626
637
  options = {
627
638
  ...options,
628
- withRelated: ['member', 'link', 'link.post', 'link.post.tags', 'link.post.authors'],
639
+ withRelated: ['member', 'link', 'link.post', ...this.#attributionPostRelations('link.post')],
629
640
  filter: 'custom:true',
630
641
  useBasicCount: true,
631
642
  mongoTransformer: chainTransformers(
@@ -6,7 +6,8 @@ const moment = require('moment');
6
6
  const messages = {
7
7
  stripeNotConnected: 'Missing Stripe connection.',
8
8
  memberAlreadyExists: 'Member already exists.',
9
- memberNotFound: 'Member not found.'
9
+ memberNotFound: 'Member not found.',
10
+ customFieldsOnAdd: 'Custom field values cannot be set while creating a member. Create the member, then set values with an edit.'
10
11
  };
11
12
 
12
13
  // Stored in the action's `context.action_name`; Admin maps it to a display label.
@@ -36,15 +37,6 @@ const CUSTOM_FIELDS_EDITED_ACTION = 'custom_fields_edited';
36
37
  * @prop {{getActiveByMembers: (memberIds: string[]) => Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>}} service
37
38
  */
38
39
 
39
- /**
40
- * @typedef {object} ICustomFieldsServiceWrapper
41
- * @prop {{
42
- * getValuesForMembers: (memberIds: string[]) => Promise<Map<string, Record<string, unknown>>>,
43
- * planWrite: (values: unknown) => Promise<object[]>,
44
- * applyWrite: (memberId: string, writes: object[]) => Promise<void>
45
- * }} values
46
- */
47
-
48
40
  module.exports = class MemberBREADService {
49
41
  /**
50
42
  * @param {object} deps
@@ -58,9 +50,9 @@ module.exports = class MemberBREADService {
58
50
  * @param {import('../../../settings-helpers/settings-helpers')} deps.settingsHelpers
59
51
  * @param {import('./next-payment-calculator')} deps.nextPaymentCalculator
60
52
  * @param {IGiftServiceWrapper} deps.giftService
61
- * @param {ICustomFieldsServiceWrapper} deps.customFieldsService
53
+ * @param {import('../../../members-custom-fields/values-service').CustomFieldValuesService} deps.customFieldValues Required: boot builds it before the members service
62
54
  */
63
- constructor({memberRepository, labsService, emailService, stripeService, offersAPI, memberAttributionService, emailSuppressionList, settingsHelpers, nextPaymentCalculator, commentsService, giftService, customFieldsService}) {
55
+ constructor({memberRepository, labsService, emailService, stripeService, offersAPI, memberAttributionService, emailSuppressionList, settingsHelpers, nextPaymentCalculator, commentsService, giftService, customFieldValues}) {
64
56
  this.offersAPI = offersAPI;
65
57
  /** @private */
66
58
  this.memberRepository = memberRepository;
@@ -83,7 +75,7 @@ module.exports = class MemberBREADService {
83
75
  /** @private */
84
76
  this.giftService = giftService;
85
77
  /** @private */
86
- this.customFieldsService = customFieldsService;
78
+ this.customFieldValues = customFieldValues;
87
79
  }
88
80
 
89
81
  /**
@@ -112,7 +104,7 @@ module.exports = class MemberBREADService {
112
104
  return null;
113
105
  }
114
106
 
115
- return this.customFieldsService.values.getValuesForMembers(memberIds);
107
+ return this.customFieldValues.getValuesForMembers(memberIds);
116
108
  }
117
109
 
118
110
  /**
@@ -444,7 +436,36 @@ module.exports = class MemberBREADService {
444
436
  return member;
445
437
  }
446
438
 
439
+ /**
440
+ * @private
441
+ * The write-side flag gate, paired with `fetchCustomFieldValues` on the read
442
+ * side. The schema declares `custom_fields` for every site, so the key arrives
443
+ * whether or not the feature is on and this is what decides it goes no further.
444
+ * @param {object} data
445
+ */
446
+ dropCustomFieldsWhenDisabled(data) {
447
+ if (!this.labsService.isSet('membersCustomFields')) {
448
+ delete data.custom_fields;
449
+ }
450
+ }
451
+
447
452
  async add(data, options) {
453
+ this.dropCustomFieldsWhenDisabled(data);
454
+
455
+ // Values cannot be set on create, only on a subsequent edit. `namesValues`
456
+ // both judges the body and rejects a malformed one, so a body refused here is
457
+ // refused on edit for the same reason.
458
+ if (this.customFieldValues.namesValues(data.custom_fields)) {
459
+ throw new errors.ValidationError({
460
+ message: tpl(messages.customFieldsOnAdd),
461
+ property: 'custom_fields'
462
+ });
463
+ }
464
+
465
+ // Not a member column, so it comes off before the repository sees it. Only
466
+ // an absent key or one naming no values gets this far.
467
+ delete data.custom_fields;
468
+
448
469
  if (!this.stripeService.configured && (data.comped || data.stripe_customer_id)) {
449
470
  const property = data.comped ? 'comped' : 'stripe_customer_id';
450
471
  throw new errors.ValidationError({
@@ -527,10 +548,12 @@ module.exports = class MemberBREADService {
527
548
  async edit(data, options) {
528
549
  delete data.last_seen_at;
529
550
 
551
+ this.dropCustomFieldsWhenDisabled(data);
552
+
530
553
  // Values live in their own table, so they come off the member data before
531
- // the repository sees it — `custom_fields` is not a member column. It only
532
- // reaches here at all when the flag is on (the input validator strips it
533
- // otherwise), so its presence is the signal to write.
554
+ // the repository sees it — `custom_fields` is not a member column. The gate
555
+ // above has already dropped it when the feature is off, so by this point its
556
+ // presence is the signal to write.
534
557
  const customFields = data.custom_fields;
535
558
  const writeCustomFields = customFields !== undefined;
536
559
  delete data.custom_fields;
@@ -540,7 +563,7 @@ module.exports = class MemberBREADService {
540
563
  // plan to apply once below, so the values aren't resolved and validated
541
564
  // twice.
542
565
  const plannedCustomFields = writeCustomFields
543
- ? await this.customFieldsService.values.planWrite(customFields)
566
+ ? await this.customFieldValues.planWrite(customFields)
544
567
  : null;
545
568
 
546
569
  let model;
@@ -589,7 +612,7 @@ module.exports = class MemberBREADService {
589
612
  }
590
613
 
591
614
  if (plannedCustomFields) {
592
- await this.customFieldsService.values.applyWrite(model.id, plannedCustomFields);
615
+ await this.customFieldValues.applyWrite(model.id, plannedCustomFields);
593
616
 
594
617
  // Custom fields aren't a member column or relation, so an edit touching
595
618
  // only them leaves `model._changed` empty and the save fires nothing.
@@ -4,7 +4,7 @@ const logging = require('@tryghost/logging');
4
4
  const membersService = require('./service');
5
5
  const emailSuppressionList = require('../email-suppression-list');
6
6
  const models = require('../../models');
7
- const urlUtils = require('../../../shared/url-utils');
7
+ const urlUtils = require('../../../shared/url-utils').default;
8
8
  const spamPrevention = require('../../web/shared/middleware/api/spam-prevention');
9
9
  const {
10
10
  formattedMemberResponse,
@@ -5,10 +5,11 @@ const MembersSSR = require('./members-ssr');
5
5
  const db = require('../../data/db');
6
6
  const MembersConfigProvider = require('./members-config-provider');
7
7
  const makeMembersCSVImporter = require('./importer');
8
+ const {resolveInlineThreshold} = require('./importer/config');
8
9
  const MembersStats = require('./stats/members-stats');
9
10
  const memberJobs = require('./jobs');
10
11
  const logging = require('@tryghost/logging');
11
- const urlUtils = require('../../../shared/url-utils');
12
+ const urlUtils = require('../../../shared/url-utils').default;
12
13
  const labsService = require('../../../shared/labs');
13
14
  const settingsCache = require('../../../shared/settings-cache');
14
15
  const config = require('../../../shared/config');
@@ -50,6 +51,9 @@ const initMembersCSVImporter = ({stripeAPIService}) => {
50
51
  return makeMembersCSVImporter({
51
52
  storagePath: config.getContentPath('data'),
52
53
  getTimezone: () => settingsCache.get('timezone'),
54
+ // A getter rather than a value because the threshold is an operator
55
+ // setting that can change between requests
56
+ getInlineThreshold: () => resolveInlineThreshold(config.get('members:importer:inlineThreshold')),
53
57
  getMembersRepository: async () => {
54
58
  const api = await module.exports.api;
55
59
  return api.members;
@@ -151,6 +155,14 @@ module.exports = {
151
155
  module.exports.verificationTrigger = verificationTrigger;
152
156
 
153
157
  const membersCSVImporter = initMembersCSVImporter({stripeAPIService: stripeService.api});
158
+ // Constructed here rather than required statically: the exporter needs the
159
+ // custom fields services, which boot builds before this one.
160
+ const customFields = require('../members-custom-fields');
161
+ module.exports.export = require('./exporter/query')({
162
+ definitions: customFields.definitions,
163
+ values: customFields.values
164
+ });
165
+
154
166
  module.exports.processImport = async (options) => {
155
167
  return await membersCSVImporter.process({...options, verificationTrigger});
156
168
  };
@@ -196,7 +208,8 @@ module.exports = {
196
208
  processImport: null,
197
209
 
198
210
  stats: membersStats,
199
- export: require('./exporter/query')
211
+
212
+ export: null
200
213
  };
201
214
 
202
215
  module.exports.middleware = require('./middleware');
@@ -4,7 +4,7 @@ const {Buffer} = require('buffer');
4
4
  const {randomBytes} = require('crypto');
5
5
 
6
6
  const config = require('../../../shared/config');
7
- const urlUtils = require('../../../shared/url-utils');
7
+ const urlUtils = require('../../../shared/url-utils').default;
8
8
 
9
9
  const messages = {
10
10
  incorrectState: 'State did not match.'
@@ -24,6 +24,9 @@ async function recordCustomFieldAction({ Action, context, verb, subject, details
24
24
  await Action.add({
25
25
  event: COMMANDS[verb],
26
26
  resource_type: 'member_custom_field',
27
+ // The field's id: this column holds 24 characters, and a key minted from
28
+ // a publisher-chosen name is bounded by the far wider key column, so only
29
+ // the id fits every field.
27
30
  resource_id: subject,
28
31
  actor_type: context.actor.type,
29
32
  actor_id: context.actor.id,
@@ -29,8 +29,9 @@ export type CustomFieldVerb = keyof typeof COMMANDS;
29
29
  // `details` is stored in the action's `context` column (Ghost's slot for "diffs,
30
30
  // meta"). We always pass the field's `primary_name` so the log reads as a human
31
31
  // label, not a bare key — this is what keeps the timeline legible after a hard
32
- // delete, when the row itself is gone.
33
- export type CustomFieldActionDetails = {primary_name: string; previous_name?: string};
32
+ // delete, when the row itself is gone. `key` rides alongside it because the key is
33
+ // how a field is addressed publicly — its id never leaves the API.
34
+ export type CustomFieldActionDetails = {primary_name: string; key: string; previous_name?: string};
34
35
 
35
36
  export type RecordCustomFieldAction =
36
37
  (input: {context: RequestContext; verb: CustomFieldVerb; subject: string; details: CustomFieldActionDetails}) => Promise<void>;
@@ -47,6 +48,9 @@ export async function recordCustomFieldAction(
47
48
  await Action.add({
48
49
  event: COMMANDS[verb],
49
50
  resource_type: 'member_custom_field',
51
+ // The field's id: this column holds 24 characters, and a key minted from
52
+ // a publisher-chosen name is bounded by the far wider key column, so only
53
+ // the id fits every field.
50
54
  resource_id: subject,
51
55
  actor_type: context.actor.type,
52
56
  actor_id: context.actor.id,
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveMaxDefinitions = resolveMaxDefinitions;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Resolving Ghost's config into the values this service runs on.
7
+ *
8
+ * Config arrives untyped: defaults.json supplies the shipped value, and any
9
+ * higher layer — an operator's config file, an environment variable, argv — can
10
+ * replace it with anything at all. Turning that into something usable is a
11
+ * config concern, so it happens here, at the boundary. The service is handed an
12
+ * answer and never learns where it came from.
13
+ */
14
+ // A ceiling in the form the service needs it. A numeric string is accepted
15
+ // because a value supplied through an environment variable can arrive as one.
16
+ const Ceiling = zod_1.z
17
+ .union([zod_1.z.number(), zod_1.z.string().trim().min(1)])
18
+ .transform(Number)
19
+ .pipe(zod_1.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER));
20
+ // defaults.json is the single source of the shipped ceiling: it is the lowest
21
+ // config layer and the file an operator actually reads and edits, so it is read
22
+ // rather than restated. Parsed eagerly because a missing or malformed value
23
+ // there is our bug rather than an operator's, and should fail loudly at boot
24
+ // instead of quietly becoming the fallback for everything below.
25
+ const shippedCeiling = Ceiling.parse(require('../../../shared/config/defaults.json').members.customFields.maxDefinitions);
26
+ // A higher layer can override the shipped value with anything. Something
27
+ // unreadable falls back to the shipped ceiling rather than throwing, because an
28
+ // operator can correct this live and a bad value must not take the feature down
29
+ // with it — and rather than meaning "no ceiling", because a typo must not
30
+ // silently remove the safeguard.
31
+ const MaxDefinitions = Ceiling.catch(shippedCeiling);
32
+ function resolveMaxDefinitions(configured) {
33
+ return MaxDefinitions.parse(configured);
34
+ }
@@ -0,0 +1,38 @@
1
+ import {z} from 'zod';
2
+
3
+ /**
4
+ * Resolving Ghost's config into the values this service runs on.
5
+ *
6
+ * Config arrives untyped: defaults.json supplies the shipped value, and any
7
+ * higher layer — an operator's config file, an environment variable, argv — can
8
+ * replace it with anything at all. Turning that into something usable is a
9
+ * config concern, so it happens here, at the boundary. The service is handed an
10
+ * answer and never learns where it came from.
11
+ */
12
+
13
+ // A ceiling in the form the service needs it. A numeric string is accepted
14
+ // because a value supplied through an environment variable can arrive as one.
15
+ const Ceiling = z
16
+ .union([z.number(), z.string().trim().min(1)])
17
+ .transform(Number)
18
+ .pipe(z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER));
19
+
20
+ // defaults.json is the single source of the shipped ceiling: it is the lowest
21
+ // config layer and the file an operator actually reads and edits, so it is read
22
+ // rather than restated. Parsed eagerly because a missing or malformed value
23
+ // there is our bug rather than an operator's, and should fail loudly at boot
24
+ // instead of quietly becoming the fallback for everything below.
25
+ const shippedCeiling: number = Ceiling.parse(
26
+ require('../../../shared/config/defaults.json').members.customFields.maxDefinitions
27
+ );
28
+
29
+ // A higher layer can override the shipped value with anything. Something
30
+ // unreadable falls back to the shipped ceiling rather than throwing, because an
31
+ // operator can correct this live and a bad value must not take the feature down
32
+ // with it — and rather than meaning "no ceiling", because a typo must not
33
+ // silently remove the safeguard.
34
+ const MaxDefinitions = Ceiling.catch(shippedCeiling);
35
+
36
+ export function resolveMaxDefinitions(configured: unknown): number {
37
+ return MaxDefinitions.parse(configured);
38
+ }