ghost 6.55.0 → 6.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/bin/validate-adapters.js +74 -0
  2. package/bin/validate-adapters.ts +87 -0
  3. package/bin/warm-compile-cache.sh +60 -0
  4. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  5. package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
  6. package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
  7. package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
  8. package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
  9. package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
  10. package/components/tryghost-nql-string-0.0.0.tgz +0 -0
  11. package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
  12. package/core/app.js +1 -1
  13. package/core/boot.js +21 -25
  14. package/core/bridge.js +10 -12
  15. package/core/built/admin/assets/Combination-BDvCdg1t.js +41 -0
  16. package/core/built/admin/assets/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
  17. package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
  18. package/core/built/admin/assets/about-C7O-AgRZ.js +1 -0
  19. package/core/built/admin/assets/{account-migration-sjHLx5dy.js → account-migration-Cv_ol1ko.js} +1 -1
  20. package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
  21. package/core/built/admin/assets/activitypub/{account-migration-DoYXtnZ8.js → account-migration-DHFbePl6.js} +7 -7
  22. package/core/built/admin/assets/activitypub/activitypub.js +6 -6
  23. package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-CT4tS2_S.js} +2 -2
  24. package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-ByYWKipo.js} +2 -2
  25. package/core/built/admin/assets/activitypub/{bluesky-sharing-Bxeh6MKW.js → bluesky-sharing-srVnSHDz.js} +16 -16
  26. package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-C2sos5Ht.js} +3 -3
  27. package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-B1UBTOoZ.js} +2 -2
  28. package/core/built/admin/assets/activitypub/{dist-D8sFfdpC.js → dist-MPsb2WZ1.js} +12 -12
  29. package/core/built/admin/assets/activitypub/{domain-CWueFW6q.js → domain-DzcM9Dzo.js} +9 -9
  30. package/core/built/admin/assets/activitypub/{edit-profile-BGxPU7N5.js → edit-profile-CsQ_4xAk.js} +945 -945
  31. package/core/built/admin/assets/activitypub/{explore-DBK-u0Ch.js → explore-CJe0DZBK.js} +8 -8
  32. package/core/built/admin/assets/activitypub/{feed-Bcir5RQZ.js → feed-CKV3qWON.js} +33 -33
  33. package/core/built/admin/assets/activitypub/{field-BV-CIT3d.js → field-DckHfDJf.js} +3 -3
  34. package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-Dlp8RM55.js} +2 -2
  35. package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-CsPy4R0M.js} +2 -2
  36. package/core/built/admin/assets/activitypub/{inbox-yC0nfvzl.js → inbox-DAsjzUUe.js} +85 -85
  37. package/core/built/admin/assets/activitypub/{label-DTRS9TDk.js → label-DWyo9E5T.js} +3 -3
  38. package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +1 -0
  39. package/core/built/admin/assets/activitypub/{moderation-Dwl9t0pb.js → moderation-B0Inc03M.js} +11 -11
  40. package/core/built/admin/assets/activitypub/{note-B-zKsZ7r.js → note-CSE54lER.js} +7 -7
  41. package/core/built/admin/assets/activitypub/{notifications-DjJZD54t.js → notifications-RSwbKqdh.js} +9 -9
  42. package/core/built/admin/assets/activitypub/{preferences-CoU8vs6d.js → preferences-CE231n2_.js} +20 -20
  43. package/core/built/admin/assets/activitypub/{profile-BAsaJekx.js → profile-aaQ1j1Eo.js} +237 -237
  44. package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cjb0Vs3m.js} +2 -2
  45. package/core/built/admin/assets/activitypub/{routes-CLH3OVTP.js → routes-Djb0mc9k.js} +1139 -1150
  46. package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-D8pROI5n.js} +3 -3
  47. package/core/built/admin/assets/activitypub/{settings-DA6pNJ-Q.js → settings-DMc32IQl.js} +13 -13
  48. package/core/built/admin/assets/activitypub/{site-CQD42Pw5.js → site-DVIcsohX.js} +81 -81
  49. package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-9Yey9usF.js} +5 -5
  50. package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DdYmKRXV.js} +6 -6
  51. package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-hdSkcjZU.js} +10 -10
  52. package/core/built/admin/assets/activitypub/{tabs-Cg0saow1.js → tabs-CUqseYzQ.js} +317 -315
  53. package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-Coni6OXM.js} +3 -3
  54. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
  55. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-B046AM38.js} +115 -96
  56. package/core/built/admin/assets/activitypub/{use-reply-chain-data-Br4aOWOh.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
  57. package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-BRfRYd16.js} +2 -2
  58. package/core/built/admin/assets/add-integration-modal-m9Dv6f77.js +1 -0
  59. package/core/built/admin/assets/add-newsletter-modal-CPSVZL2H.js +1 -0
  60. package/core/built/admin/assets/add-recommendation-modal-Ci9Z5yat.js +1 -0
  61. package/core/built/admin/assets/analytics-DqSCVV8U.js +1 -0
  62. package/core/built/admin/assets/announcement-bar-modal-DU3avV7d.js +1 -0
  63. package/core/built/admin/assets/api-keys-BK0LylyK.js +1 -0
  64. package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
  65. package/core/built/admin/assets/{audience-DjEWNhp3.js → audience-DTJtXXWY.js} +1 -1
  66. package/core/built/admin/assets/automated-emails-PayPCT8L.js +1 -0
  67. package/core/built/admin/assets/{automations-DhKeS_U5.js → automations-162q5HjL.js} +1 -1
  68. package/core/built/admin/assets/{automations-Bx3VcFIL.js → automations-dtQ_MRZ7.js} +1 -1
  69. package/core/built/admin/assets/{bluesky-sharing-ClMd0-Ft.js → bluesky-sharing-B8Es3AuV.js} +1 -1
  70. package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
  71. package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
  72. package/core/built/admin/assets/{breadcrumb-BD0U2ygg.js → breadcrumb-tg5zL--z.js} +1 -1
  73. package/core/built/admin/assets/build-CsiH26uS.js +1 -0
  74. package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
  75. package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
  76. package/core/built/admin/assets/{chart-helpers-fjQKRVyY.js → chart-helpers-CZElTGYL.js} +1 -1
  77. package/core/built/admin/assets/checkbox-Cyo-x7mC.js +1 -0
  78. package/core/built/admin/assets/chunk.524.cd5498dd4e195f679266.js +25 -0
  79. package/core/built/admin/assets/chunk.582.6f92ec1f03734bf2ea3f.js +27 -0
  80. package/core/built/admin/assets/{chunk.481.6c7395a49484bb4583aa.js → chunk.873.b8fc05a8ab877fb819ed.js} +62 -60
  81. package/core/built/admin/assets/code-editor-view-Dq1rQ4tS.js +1 -0
  82. package/core/built/admin/assets/color-picker-field-BonBgxhU.js +1 -0
  83. package/core/built/admin/assets/color-utils-BsrwXuDz.js +1 -0
  84. package/core/built/admin/assets/combobox-uX5r_paI.js +1 -0
  85. package/core/built/admin/assets/command-DxwBO2pF.js +1 -0
  86. package/core/built/admin/assets/comments-Dn-RO-nU.js +1 -0
  87. package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
  88. package/core/built/admin/assets/confirmation-modal-B6D6mk6R.js +1 -0
  89. package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
  90. package/core/built/admin/assets/content-api-modal-BxQ-7T6u.js +1 -0
  91. package/core/built/admin/assets/copy-field-BAxL4Zqn.js +1 -0
  92. package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
  93. package/core/built/admin/assets/custom-integration-modal---d_l7lc.js +1 -0
  94. package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
  95. package/core/built/admin/assets/{date-range-select-BFp58EMk.js → date-range-select-PB2n1HZf.js} +1 -1
  96. package/core/built/admin/assets/debounce-Dg2fW5Rg.js +3 -0
  97. package/core/built/admin/assets/design-and-theme-modal-DYRT207a.js +23 -0
  98. package/core/built/admin/assets/dirty-confirm-dialog-BnDJ39ao.js +1 -0
  99. package/core/built/admin/assets/disabled-sources-indicator-iMHrkyV3.js +1 -0
  100. package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
  101. package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
  102. package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS02.js} +1 -1
  103. package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-D5vhMdXf.js} +1 -1
  104. package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
  105. package/core/built/admin/assets/dist-WeOIvSYM.js +23 -0
  106. package/core/built/admin/assets/{dist-DyViJwIy.js → dist-cFzKLG7y.js} +3 -3
  107. package/core/built/admin/assets/dist-j1xxxnY8.js +1 -0
  108. package/core/built/admin/assets/dist-myQaY0ka.js +1 -0
  109. package/core/built/admin/assets/domain-BR8FIdjD.js +1 -0
  110. package/core/built/admin/assets/{dropzone-DlK-K2DQ.js → dropzone-hNxmUvwD.js} +1 -1
  111. package/core/built/admin/assets/{edit-profile-BKT-REJw.js → edit-profile-CENrkY0l.js} +1 -1
  112. package/core/built/admin/assets/editor-B18D0lm8.js +8 -0
  113. package/core/built/admin/assets/embed-signup-form-modal-BsCYUMvQ.js +9 -0
  114. package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
  115. package/core/built/admin/assets/{explore-ClUpJZOp.js → explore-BmrnljWw.js} +1 -1
  116. package/core/built/admin/assets/{feed-DF92dUMP.js → feed-Dy4li6i8.js} +1 -1
  117. package/core/built/admin/assets/{field-Do7lieBA.js → field-Dh_lwk13.js} +1 -1
  118. package/core/built/admin/assets/filter-sources-D2zDuJH5.js +1 -0
  119. package/core/built/admin/assets/filters-3JBQcNtJ.js +1 -0
  120. package/core/built/admin/assets/filters-CFt_BDe4.js +1 -0
  121. package/core/built/admin/assets/first-promoter-modal-D6Rjxugw.js +1 -0
  122. package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
  123. package/core/built/admin/assets/{ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
  124. package/core/built/admin/assets/{ghost-c4057474943effc568c5659ccc4909d7.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +420 -398
  125. package/core/built/admin/assets/{ghost-dark-db36d795d21c61426837df40af270966.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
  126. package/core/built/admin/assets/{gift-link-modal-f9VIMNDv.js → gift-link-modal-zMtlJhLQ.js} +1 -1
  127. package/core/built/admin/assets/{gift-links-CsUhefWG.js → gift-links-CVuqLhsZ.js} +1 -1
  128. package/core/built/admin/assets/global-data-provider-B949LDXJ.js +1 -0
  129. package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
  130. package/core/built/admin/assets/{growth-CVw1My4S.js → growth-C38Xtp8D.js} +2 -2
  131. package/core/built/admin/assets/{growth-B3I6gNLm.js → growth-CFws67_i.js} +1 -1
  132. package/core/built/admin/assets/history-modal-ILAOkbxH.js +1 -0
  133. package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
  134. package/core/built/admin/assets/html-field-9a9Id_2q.js +1 -0
  135. package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
  136. package/core/built/admin/assets/images-CrQKVc9Q.js +1 -0
  137. package/core/built/admin/assets/{inbox-CFBSu4Ed.js → inbox-BrSRzyvz.js} +6 -6
  138. package/core/built/admin/assets/index-BHy2DZX-.js +67 -0
  139. package/core/built/admin/assets/index-Ds2iUoF6.css +1 -0
  140. package/core/built/admin/assets/index.es-B9tvf-t4.js +1 -0
  141. package/core/built/admin/assets/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
  142. package/core/built/admin/assets/input-group-6vEPvX9S.js +1 -0
  143. package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.js} +1 -1
  144. package/core/built/admin/assets/integration-header-B4DyXV06.js +1 -0
  145. package/core/built/admin/assets/integrations-BT6_iJvu.js +1 -0
  146. package/core/built/admin/assets/invite-user-modal-5xDCEw90.js +1 -0
  147. package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
  148. package/core/built/admin/assets/koenig-editor-base-BWLEB9Vf.js +1 -0
  149. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9535 -9527
  150. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
  151. package/core/built/admin/assets/{koenig-lexical-f8vHRYvz.js → koenig-lexical-Cj62r_y9.js} +86 -86
  152. package/core/built/admin/assets/{kpi-card-DSap9rAy.js → kpi-card-DOUnkZcN.js} +1 -1
  153. package/core/built/admin/assets/{kpi-card-DU0rn9Cj.js → kpi-card-IHDo8Q56.js} +1 -1
  154. package/core/built/admin/assets/{kpi-helpers-Dz33AV6h.js → kpi-helpers-CjUdECAK.js} +1 -1
  155. package/core/built/admin/assets/{kpi-tabs-BtTTrKvJ.js → kpi-tabs-X3DkOYbI.js} +1 -1
  156. package/core/built/admin/assets/{label-DS8Mi6G5.js → label-Dq6QaOuS.js} +1 -1
  157. package/core/built/admin/assets/label-picker-8YlhS2U3.js +1 -0
  158. package/core/built/admin/assets/labels-DAPjKPy2.js +1 -0
  159. package/core/built/admin/assets/layout-TMj4P-vv.js +1 -0
  160. package/core/built/admin/assets/limit-modal-BRPsit_B.js +1 -0
  161. package/core/built/admin/assets/link-helpers-Bzkk68GD.js +1 -0
  162. package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
  163. package/core/built/admin/assets/member-custom-fields-CmvsuuV_.js +1 -0
  164. package/core/built/admin/assets/member-detail-DCvRrkmJ.js +1 -0
  165. package/core/built/admin/assets/members-DxbSKngD.js +11 -0
  166. package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
  167. package/core/built/admin/assets/{moderation-DySbgyUS.js → moderation-Da5lhhWh.js} +1 -1
  168. package/core/built/admin/assets/multi-select-combobox-CYdkH7KQ.js +1 -0
  169. package/core/built/admin/assets/navigation-modal-BIoTwtMy.js +1 -0
  170. package/core/built/admin/assets/newsletter-BptlVoJm.js +1 -0
  171. package/core/built/admin/assets/newsletter-detail-modal-BIvxqE0N.js +1 -0
  172. package/core/built/admin/assets/newsletter-emails-Bf8IZLsP.js +1 -0
  173. package/core/built/admin/assets/newsletters-BgrIZk0V.js +1 -0
  174. package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
  175. package/core/built/admin/assets/{note-C8kxrz6w.js → note-DYcLOmDb.js} +1 -1
  176. package/core/built/admin/assets/{notifications-CgtSfX7t.js → notifications-B2SPFIV9.js} +1 -1
  177. package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
  178. package/core/built/admin/assets/offers-container-modal-BZnZITMw.js +1 -0
  179. package/core/built/admin/assets/{onboarding-route-4qtziA9d.js → onboarding-route-DgdfxkQh.js} +1 -1
  180. package/core/built/admin/assets/overview-BYQJXSog.js +1 -0
  181. package/core/built/admin/assets/overview-Dt2_mR6U.js +1 -0
  182. package/core/built/admin/assets/{page-header-B3Wb9Jqv.js → page-header-CpmxJj5B.js} +1 -1
  183. package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
  184. package/core/built/admin/assets/pintura-modal-B4MuBuuO.js +1 -0
  185. package/core/built/admin/assets/portal-frame-BF0NBIji.js +1 -0
  186. package/core/built/admin/assets/portal-modal-DMVbm0oy.js +1 -0
  187. package/core/built/admin/assets/{post-analytics-We-ko5Cb.js → post-analytics-CXWBOGyP.js} +1 -1
  188. package/core/built/admin/assets/post-analytics-header-ChzjXVPt.js +1 -0
  189. package/core/built/admin/assets/{post-analytics-provider-B6UynTGb.js → post-analytics-provider-Cgw4U_fw.js} +1 -1
  190. package/core/built/admin/assets/{post-share-modal-DFfO93_0.js → post-share-modal-B9Vmk_XM.js} +1 -1
  191. package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
  192. package/core/built/admin/assets/preferences-BfzJmnsd.js +5 -0
  193. package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
  194. package/core/built/admin/assets/preview-modal-Dw_HtD6S.js +1 -0
  195. package/core/built/admin/assets/profile-BT5vtv9b.js +1 -0
  196. package/core/built/admin/assets/radio-group-I7ou7PoJ.js +1 -0
  197. package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
  198. package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-DSQfprxH.js} +1 -1
  199. package/core/built/admin/assets/select-D9oU1IoM.js +1 -0
  200. package/core/built/admin/assets/settings-CfZFbyu_.js +4 -0
  201. package/core/built/admin/assets/{settings-WXNeWyU1.js → settings-CpvHWix7.js} +1 -1
  202. package/core/built/admin/assets/settings-breadcrumbs-K0USP2nw.js +1 -0
  203. package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
  204. package/core/built/admin/assets/{share-modal-DsgW3wbg.js → share-modal-ClIyPzc5.js} +1 -1
  205. package/core/built/admin/assets/slack-modal-1XXqOfiw.js +1 -0
  206. package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
  207. package/core/built/admin/assets/{sort-button-Bb_FCl7e.js → sort-button-CdBsm40v.js} +1 -1
  208. package/core/built/admin/assets/{source-icon-BeaRxUn9.js → source-icon-CEYmVLXR.js} +1 -1
  209. package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
  210. package/core/built/admin/assets/{stats-Bjj_kmJ1.js → stats-D8nZCTy5.js} +1 -1
  211. package/core/built/admin/assets/{stats-view-BxPbW6K1.js → stats-view-CugFVf0g.js} +1 -1
  212. package/core/built/admin/assets/{step-1-B1e3-1F8.js → step-1-DOYjrORn.js} +1 -1
  213. package/core/built/admin/assets/{step-2-C5WaeBJo.js → step-2-vA6RsarH.js} +1 -1
  214. package/core/built/admin/assets/{step-3-B46iVPwu.js → step-3-BtIBxMId.js} +2 -2
  215. package/core/built/admin/assets/stripe-button-UFP4QRXd.js +1 -0
  216. package/core/built/admin/assets/stripe-connect-modal-C_L3uM6n.js +1 -0
  217. package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
  218. package/core/built/admin/assets/{table-CB-4_HBJ.js → table-TMCgCQK7.js} +1 -1
  219. package/core/built/admin/assets/tabs-Dd6oOKbv.js +1 -0
  220. package/core/built/admin/assets/tag-detail-D3l-UJPR.js +1 -0
  221. package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
  222. package/core/built/admin/assets/tags-WdKQGWR8.js +1 -0
  223. package/core/built/admin/assets/testimonials-modal-CqWp-xXJ.js +1 -0
  224. package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
  225. package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
  226. package/core/built/admin/assets/tier-detail-modal-BkcR2u0A.js +1 -0
  227. package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
  228. package/core/built/admin/assets/toggle-group-Guorw93z.js +1 -0
  229. package/core/built/admin/assets/{topic-filter-DAFT87tQ.js → topic-filter-CbgoWIHn.js} +1 -1
  230. package/core/built/admin/assets/transistor-modal-DuNnnpal.js +1 -0
  231. package/core/built/admin/assets/unsplash-modal-y2bVwTHH.js +1 -0
  232. package/core/built/admin/assets/{use-analytics-data-S0FTd43s.js → use-analytics-data-B-b4wNSC.js} +1 -1
  233. package/core/built/admin/assets/use-check-theme-limit-error-m09J38Mq.js +2 -0
  234. package/core/built/admin/assets/use-feature-flag-ud0Qz8vt.js +1 -0
  235. package/core/built/admin/assets/{use-filter-params-Gpoq2E1L.js → use-filter-params-BnbBDc7T.js} +1 -1
  236. package/core/built/admin/assets/use-filterable-api-iMpDZBxD.js +1 -0
  237. package/core/built/admin/assets/use-form-vRjI0Kwk.js +1 -0
  238. package/core/built/admin/assets/{use-growth-stats-CIaiHOVb.js → use-growth-stats-DPU5rt3W.js} +1 -1
  239. package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
  240. package/core/built/admin/assets/use-limiter-BkN3E4gw.js +2 -0
  241. package/core/built/admin/assets/use-pintura-config-C_z8HTG2.js +1 -0
  242. package/core/built/admin/assets/use-pintura-editor-DNgbkhGp.js +1 -0
  243. package/core/built/admin/assets/{use-post-referrers-Cq2vBPrA.js → use-post-referrers-C2mXyrp-.js} +1 -1
  244. package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
  245. package/core/built/admin/assets/{use-reply-chain-data-DBDRtGu-.js → use-reply-chain-data-DaK8q2qQ.js} +1 -1
  246. package/core/built/admin/assets/use-setting-group-YlYcoNyP.js +1 -0
  247. package/core/built/admin/assets/use-settings-navigation-Bf1g1rEh.js +1 -0
  248. package/core/built/admin/assets/{use-simple-pagination-CjYNxdwe.js → use-simple-pagination-D9ckKo5-.js} +1 -1
  249. package/core/built/admin/assets/{use-tinybird-query-falHAtuK.js → use-tinybird-query-GSa32hVw.js} +1 -1
  250. package/core/built/admin/assets/{use-tinybird-token-D7-8ND1y.js → use-tinybird-token-CKLOZFsw.js} +1 -1
  251. package/core/built/admin/assets/use-url-input-z-p0Kewm.js +5 -0
  252. package/core/built/admin/assets/user-detail-modal-BMxOA4Sv.js +1 -0
  253. package/core/built/admin/assets/{users-Dkv8NQZe.js → users-B6hIds6n.js} +1 -1
  254. package/core/built/admin/assets/validator-Cr0sMKuW.js +7 -0
  255. package/core/built/admin/assets/{virtual-list-CvnC9zF2.js → virtual-list-RHeEL05L.js} +1 -1
  256. package/core/built/admin/assets/{web-jFYWCKDn.js → web-Bw2QMO5u.js} +2 -2
  257. package/core/built/admin/assets/web-xJtcwMy4.js +1 -0
  258. package/core/built/admin/assets/zapier-modal-BP2f4-vY.js +1 -0
  259. package/core/built/admin/index.html +20 -18
  260. package/core/frontend/helpers/authors.js +1 -1
  261. package/core/frontend/helpers/link.js +3 -8
  262. package/core/frontend/helpers/tags.js +1 -1
  263. package/core/frontend/meta/author-url.js +2 -2
  264. package/core/frontend/meta/url.js +4 -4
  265. package/core/frontend/services/data/fetch-data.js +10 -5
  266. package/core/frontend/services/proxy.js +1 -1
  267. package/core/frontend/services/routing/api-adapter.js +115 -0
  268. package/core/frontend/services/routing/api-adapter.ts +157 -0
  269. package/core/frontend/services/routing/collection-router.js +5 -3
  270. package/core/frontend/services/routing/controllers/index.js +1 -1
  271. package/core/frontend/services/routing/controllers/rss.js +26 -31
  272. package/core/frontend/services/routing/controllers/rss.ts +66 -0
  273. package/core/frontend/services/routing/controllers/static.js +8 -7
  274. package/core/frontend/services/routing/events.js +20 -6
  275. package/core/frontend/services/routing/parent-router.js +17 -4
  276. package/core/frontend/services/routing/permalink-adapter.js +13 -0
  277. package/core/frontend/services/routing/permalink-adapter.ts +10 -0
  278. package/core/frontend/services/routing/registry.js +59 -33
  279. package/core/frontend/services/routing/router-manager.js +24 -22
  280. package/core/frontend/services/routing/static-routes-router.js +4 -4
  281. package/core/frontend/services/routing/taxonomy-router.js +6 -2
  282. package/core/frontend/services/sitemap/base-site-map-generator.js +1 -28
  283. package/core/frontend/services/sitemap/site-map-manager.js +22 -57
  284. package/core/server/adapters/route-settings/utils.js +10 -2
  285. package/core/server/adapters/route-settings/utils.ts +12 -2
  286. package/core/server/api/endpoints/email-post.js +1 -1
  287. package/core/server/api/endpoints/previews.js +1 -1
  288. package/core/server/api/endpoints/search-index-public.js +3 -3
  289. package/core/server/api/endpoints/search-index.js +4 -4
  290. package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -2
  291. package/core/server/api/endpoints/utils/serializers/input/comments.js +1 -1
  292. package/core/server/api/endpoints/utils/serializers/input/pages.js +3 -3
  293. package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
  294. package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
  295. package/core/server/api/endpoints/utils/serializers/input/users.js +2 -2
  296. package/core/server/api/endpoints/utils/serializers/input/utils/url.js +31 -13
  297. package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +2 -2
  298. package/core/server/api/endpoints/utils/serializers/output/mappers/tags.js +2 -2
  299. package/core/server/api/endpoints/utils/serializers/output/mappers/users.js +8 -0
  300. package/core/server/api/endpoints/utils/serializers/output/utils/url.js +18 -11
  301. package/core/server/data/migrations/versions/6.56/2026-08-03-19-27-51-add-automation-run-step-id-to-automated-email-recipients.js +8 -0
  302. package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
  303. package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +74 -0
  304. package/core/server/data/migrations/versions/6.57/2026-08-04-18-54-14-reset-automation-email-analytics.js +25 -0
  305. package/core/server/data/schema/commands.js +21 -5
  306. package/core/server/data/schema/fixtures/fixtures.json +2 -2
  307. package/core/server/data/schema/schema.js +25 -5
  308. package/core/server/lib/common/to-plain.ts +1 -1
  309. package/core/server/models/post.js +12 -3
  310. package/core/server/services/adapter-manager/adapter-manager.js +1 -45
  311. package/core/server/services/adapter-manager/adapter-manager.ts +2 -48
  312. package/core/server/services/adapter-manager/adapter-paths.js +24 -0
  313. package/core/server/services/adapter-manager/adapter-paths.ts +21 -0
  314. package/core/server/services/adapter-manager/base-classes.js +22 -0
  315. package/core/server/services/adapter-manager/base-classes.ts +22 -0
  316. package/core/server/services/adapter-manager/index.js +4 -27
  317. package/core/server/services/adapter-manager/index.ts +4 -31
  318. package/core/server/services/adapter-manager/utils.js +50 -0
  319. package/core/server/services/adapter-manager/utils.ts +57 -0
  320. package/core/server/services/audience-feedback/audience-feedback-controller.js +1 -1
  321. package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
  322. package/core/server/services/automations/automations-repository.ts +3 -1
  323. package/core/server/services/automations/database-automations-repository.js +33 -14
  324. package/core/server/services/automations/database-automations-repository.ts +41 -15
  325. package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
  326. package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
  327. package/core/server/services/automations/poll.js +3 -1
  328. package/core/server/services/automations/poll.ts +4 -1
  329. package/core/server/services/automations/service.js +7 -3
  330. package/core/server/services/automations/service.ts +7 -3
  331. package/core/server/services/comments/comments-service-emails.js +1 -1
  332. package/core/server/services/comments/comments-service.js +1 -1
  333. package/core/server/services/email-analytics/email-analytics-service-wrapper.js +11 -2
  334. package/core/server/services/email-analytics/email-analytics-service.js +1 -1
  335. package/core/server/services/email-service/email-service-wrapper.js +2 -1
  336. package/core/server/services/email-service/mailgun-email-provider.js +15 -0
  337. package/core/server/services/gift-links/CONTEXT.md +9 -0
  338. package/core/server/services/gifts/CONTEXT.md +55 -0
  339. package/core/server/services/gifts/README.md +28 -0
  340. package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
  341. package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
  342. package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
  343. package/core/server/services/gifts/gift-checkout-offer.js +74 -0
  344. package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
  345. package/core/server/services/gifts/gift-codec.js +63 -0
  346. package/core/server/services/gifts/gift-codec.ts +62 -0
  347. package/core/server/services/gifts/gift-controller.js +6 -31
  348. package/core/server/services/gifts/gift-controller.ts +6 -69
  349. package/core/server/services/gifts/gift-email-service.ts +3 -2
  350. package/core/server/services/gifts/gift-schema.js +34 -0
  351. package/core/server/services/gifts/gift-schema.ts +39 -0
  352. package/core/server/services/gifts/gift-service-wrapper.js +13 -11
  353. package/core/server/services/gifts/gift-service.js +265 -52
  354. package/core/server/services/gifts/gift-service.ts +431 -97
  355. package/core/server/services/gifts/gift.ts +16 -39
  356. package/core/server/services/indexnow-ping/indexnow-ping-service.js +1 -1
  357. package/core/server/services/indexnow-ping/indexnow-ping-service.ts +2 -4
  358. package/core/server/services/lib/mailgun-client.js +3 -3
  359. package/core/server/services/link-tracking/link-click-repository.js +6 -1
  360. package/core/server/services/link-tracking/link-click-tracking-service.js +12 -3
  361. package/core/server/services/media-inliner/external-media-inliner.js +12 -8
  362. package/core/server/services/member-attribution/url-translator.js +5 -7
  363. package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +4 -3
  364. package/core/server/services/member-welcome-emails/service.js +79 -15
  365. package/core/server/services/members/api.js +2 -3
  366. package/core/server/services/members/import-export/import/completion-email.js +8 -4
  367. package/core/server/services/members/import-export/import/completion-email.ts +9 -4
  368. package/core/server/services/members/import-export/import/importer.js +25 -4
  369. package/core/server/services/members/import-export/import/importer.ts +27 -6
  370. package/core/server/services/members/import-export/import/row.ts +5 -1
  371. package/core/server/services/members/import-export/index.js +6 -2
  372. package/core/server/services/members/import-export/index.ts +10 -4
  373. package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
  374. package/core/server/services/members/members-api/members-api.js +13 -7
  375. package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
  376. package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
  377. package/core/server/services/members/members-api/services/payments-service.js +6 -68
  378. package/core/server/services/members-custom-fields/queries.js +4 -13
  379. package/core/server/services/members-custom-fields/queries.ts +4 -13
  380. package/core/server/services/members-custom-fields/schema.js +19 -21
  381. package/core/server/services/members-custom-fields/schema.ts +18 -20
  382. package/core/server/services/members-custom-fields/storage.js +94 -27
  383. package/core/server/services/members-custom-fields/storage.ts +114 -39
  384. package/core/server/services/members-custom-fields/values-service.js +98 -103
  385. package/core/server/services/members-custom-fields/values-service.ts +116 -114
  386. package/core/server/services/mentions/resource-service.js +1 -1
  387. package/core/server/services/mentions/service.js +2 -2
  388. package/core/server/services/oembed/oembed-service.js +44 -3
  389. package/core/server/services/posts/posts-service.js +11 -0
  390. package/core/server/services/route-settings/dynamic-routing-service.js +10 -62
  391. package/core/server/services/route-settings/route-settings-parser.js +25 -25
  392. package/core/server/services/route-settings/route-settings-parser.ts +27 -27
  393. package/core/server/services/route-settings/validation-errors.js +68 -4
  394. package/core/server/services/route-settings/validation-errors.ts +79 -4
  395. package/core/server/services/slack-ping/slack-ping-service.js +2 -2
  396. package/core/server/services/slack-ping/slack-ping-service.ts +3 -5
  397. package/core/server/services/stats/content-stats-service.js +3 -3
  398. package/core/server/services/stats/posts-stats-service.js +29 -25
  399. package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
  400. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
  401. package/core/server/services/stripe/stripe-api.js +10 -0
  402. package/core/server/services/url/config.js +90 -135
  403. package/core/server/services/url/index.js +6 -49
  404. package/core/server/services/url/lazy-find-resource.js +14 -11
  405. package/core/server/services/url/lazy-find-resource.ts +14 -11
  406. package/core/server/services/url/lazy-url-service.js +174 -94
  407. package/core/server/services/url/lazy-url-service.ts +233 -97
  408. package/core/server/services/url/routable-resources.js +62 -71
  409. package/core/server/services/url/router-filter.js +12 -4
  410. package/core/server/services/url/router-filter.ts +12 -4
  411. package/core/server/services/webhooks/index.js +1 -1
  412. package/core/server/services/webhooks/serialize.js +2 -1
  413. package/core/server/web/gift-preview/controller.js +10 -25
  414. package/core/shared/config/env/config.testing-mysql.json +1 -2
  415. package/core/shared/config/env/config.testing.json +1 -2
  416. package/core/shared/events/index.js +1 -2
  417. package/core/shared/labs.js +4 -4
  418. package/package.json +10 -10
  419. package/pnpm-lock.yaml +27 -20
  420. package/pnpm-workspace.yaml +3 -2
  421. package/tsconfig.json +1 -0
  422. package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
  423. package/core/built/admin/assets/Combination-CJG7Flp-.js +0 -41
  424. package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +0 -1
  425. package/core/built/admin/assets/automated-emails-DRy7qY-J.js +0 -7
  426. package/core/built/admin/assets/checkbox-CWa28lgO.js +0 -1
  427. package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +0 -25
  428. package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +0 -27
  429. package/core/built/admin/assets/code-editor-view-CCr0rBWS.js +0 -1
  430. package/core/built/admin/assets/comments-DyUSMUdj.js +0 -1
  431. package/core/built/admin/assets/disabled-sources-indicator-CUxMfiPa.js +0 -1
  432. package/core/built/admin/assets/dist-2Tnz2Yfq.js +0 -1
  433. package/core/built/admin/assets/dist-CjY-f5Z1.js +0 -1
  434. package/core/built/admin/assets/dist-DDY6GRVz.js +0 -1
  435. package/core/built/admin/assets/dist-DG78yroX.js +0 -23
  436. package/core/built/admin/assets/domain-C0bV2UV8.js +0 -1
  437. package/core/built/admin/assets/editor-Dp865O-4.js +0 -8
  438. package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
  439. package/core/built/admin/assets/filter-sources-BV2rnpfA.js +0 -1
  440. package/core/built/admin/assets/filters-CUXJ2Nm9.js +0 -1
  441. package/core/built/admin/assets/filters-DfbIWc_l.js +0 -1
  442. package/core/built/admin/assets/index-9Cw8z3rA.css +0 -1
  443. package/core/built/admin/assets/index-BGG7NckO.js +0 -67
  444. package/core/built/admin/assets/input-group-BSgG_lVw.js +0 -1
  445. package/core/built/admin/assets/label-picker-1DvSuLA1.js +0 -1
  446. package/core/built/admin/assets/labels-CDSUt4J_.js +0 -1
  447. package/core/built/admin/assets/layout-yQEi2qDM.js +0 -1
  448. package/core/built/admin/assets/link-helpers-7EYqvvHs.js +0 -1
  449. package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +0 -1
  450. package/core/built/admin/assets/member-detail-BAq7gDaa.js +0 -1
  451. package/core/built/admin/assets/members-CQpVFJu4.js +0 -10
  452. package/core/built/admin/assets/modals-ReORvWnu.js +0 -31
  453. package/core/built/admin/assets/newsletter-CJQvtHqN.js +0 -1
  454. package/core/built/admin/assets/newsletters-B3QQXfGT.js +0 -1
  455. package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
  456. package/core/built/admin/assets/overview-Bf2sbznq.js +0 -1
  457. package/core/built/admin/assets/overview-CBfePxcr.js +0 -1
  458. package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +0 -1
  459. package/core/built/admin/assets/preferences-DJPfHEGu.js +0 -5
  460. package/core/built/admin/assets/profile-DqBQp-gT.js +0 -1
  461. package/core/built/admin/assets/select-C5_xEt5b.js +0 -1
  462. package/core/built/admin/assets/settings-BwiVIXau.js +0 -4
  463. package/core/built/admin/assets/social-urls-BQPFEk1_.js +0 -8
  464. package/core/built/admin/assets/switch-z5Y6Uzt6.js +0 -1
  465. package/core/built/admin/assets/tabs-CCEgKYzq.js +0 -1
  466. package/core/built/admin/assets/tags-Cg2VJGmG.js +0 -1
  467. package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
  468. package/core/built/admin/assets/toggle-group-b3IlxvhV.js +0 -1
  469. package/core/built/admin/assets/web-B_5GZRvH.js +0 -1
  470. package/core/server/services/gifts/gift-repository.js +0 -2
  471. package/core/server/services/gifts/gift-repository.ts +0 -29
  472. package/core/server/services/route-settings/activation-bridge.js +0 -174
  473. package/core/server/services/route-settings/activation-bridge.ts +0 -250
  474. package/core/server/services/url/local-file-cache.js +0 -75
  475. package/core/server/services/url/queue.js +0 -260
  476. package/core/server/services/url/resource.js +0 -102
  477. package/core/server/services/url/resources.js +0 -480
  478. package/core/server/services/url/url-generator.js +0 -276
  479. package/core/server/services/url/url-service-facade.js +0 -366
  480. package/core/server/services/url/url-service-facade.ts +0 -519
  481. package/core/server/services/url/url-service.js +0 -387
  482. package/core/server/services/url/urls.js +0 -140
  483. package/core/shared/events/url-resource-updated-event.js +0 -33
@@ -82,7 +82,7 @@ interface StripeSubscriptions {
82
82
 
83
83
  // Gifts: reassigning a redeemed gift to the imported member.
84
84
  export interface GiftService {
85
- reassignRedeemer(giftId: string, memberId: string, options: object): Promise<void>;
85
+ reassignRedeemer(giftId: string, memberId: string, options: {transacting?: Knex.Transaction}): Promise<void>;
86
86
  }
87
87
 
88
88
  // The completion email concern: who it goes to, the links it carries, and sending
@@ -99,7 +99,7 @@ type CustomFieldPlan = unknown;
99
99
  // The custom fields collaborator as the import needs it. activeFields is the field set a
100
100
  // custom_fields.* column is read against, empty when the feature is off; planWrite
101
101
  // validates a row's values (throwing so the row fails whole) and applyWrite persists
102
- // them, both on the row's transaction.
102
+ // them, touching only the parts the row named, both on the row's transaction.
103
103
  export interface CustomFieldsImport {
104
104
  activeFields(): Promise<CsvField[]>;
105
105
  planWrite(values: Record<string, unknown>): Promise<CustomFieldPlan[]>;
@@ -332,7 +332,7 @@ class MembersCSVImporter {
332
332
  // before any member write -- so there is no reason to hold a transaction
333
333
  // across it, and doing so would deadlock the single-connection SQLite pool.
334
334
  const customFieldPlan = activeCustomFields.length > 0
335
- ? await this._customFields.planWrite(fieldValuesFromCsvRow(activeCustomFields, row, stripFormulaGuard))
335
+ ? await namingTheColumn(() => this._customFields.planWrite(fieldValuesFromCsvRow(activeCustomFields, row, stripFormulaGuard)))
336
336
  : [];
337
337
 
338
338
  trx = await this._knex.transaction(undefined, {doNotRejectOnRollback: false});
@@ -432,15 +432,16 @@ class MembersCSVImporter {
432
432
  imported += 1;
433
433
  } catch (error) {
434
434
  const errorList: unknown[] = Array.isArray(error) ? error : [error];
435
- const errorMessage = errorList
435
+ const reasons = errorList
436
436
  .map(e => (typeof e === 'object' && e !== null && 'message' in e ? e.message : undefined))
437
- .join(', ');
437
+ .filter((message): message is string => typeof message === 'string');
438
+ const errorMessage = reasons.join('\n');
438
439
  // trx is unset if the row failed before the transaction opened (a bad
439
440
  // custom field value or gift combination).
440
441
  if (trx) {
441
442
  await trx.rollback();
442
443
  }
443
- importErrors.push({...row, error: errorMessage});
444
+ importErrors.push({...row, error: errorMessage, errors: reasons});
444
445
  }
445
446
  }
446
447
 
@@ -457,6 +458,26 @@ class MembersCSVImporter {
457
458
  }
458
459
  }
459
460
 
461
+ // A row error is read next to a spreadsheet, and a value rejection states only what the
462
+ // value should be — the same sentence Admin shows under a single input, where the field is
463
+ // already on screen. `property` is the dotted path a default export writes as its column
464
+ // header, so prefixing with it names the column a publisher has to go and fix.
465
+ //
466
+ // A rejection about the whole request carries the bare namespace and names no field after
467
+ // it, so it is left alone rather than made to point at a column that does not exist.
468
+ async function namingTheColumn<T>(plan: () => Promise<T>): Promise<T> {
469
+ try {
470
+ return await plan();
471
+ } catch (error) {
472
+ const {property, message} = (error ?? {}) as {property?: unknown; message?: unknown};
473
+ const namesAField = typeof property === 'string' && property.includes('.');
474
+ if (!namesAField || typeof message !== 'string') {
475
+ throw error;
476
+ }
477
+ throw new errors.DataImportError({message: `${property}: ${message}`});
478
+ }
479
+ }
480
+
460
481
  function wrapGiftError(error: unknown) {
461
482
  const err = error as {message?: unknown};
462
483
  const message = (err && typeof err.message === 'string' && err.message) || tpl(messages.giftReassignFailed);
@@ -42,7 +42,11 @@ export type MemberImportRow = z.infer<typeof memberImportRowSchema>;
42
42
 
43
43
  // A row that failed to import, carrying the raw message that stopped it. The message is
44
44
  // left raw here; turning it into human copy is a presentation concern.
45
- export type ImportErrorRow = MemberImportRow & {error: string};
45
+ // A row can fail for more than one reason. `errors` is the list; `error` is that list
46
+ // written out for the error report's single cell. Two fields because a reason may quote a
47
+ // cell the publisher wrote, and a CSV cell legally holds commas and newlines, so no
48
+ // separator survives being split back apart.
49
+ export type ImportErrorRow = MemberImportRow & {error: string; errors: string[]};
46
50
 
47
51
  // The persisted import label as plain data -- taken off the Bookshelf model before it
48
52
  // leaves the kernel, so the result and the email carry only what they show.
@@ -40,7 +40,11 @@ function makeImporter(deps) {
40
40
  // Gifts is initialised at boot and always present at request time; the getter
41
41
  // resolves it lazily so the ready service is picked up whenever a row uses it.
42
42
  const gifts = {
43
- reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer(giftId, memberId, options)
43
+ reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer({
44
+ giftId,
45
+ memberId,
46
+ transacting: options.transacting
47
+ })
44
48
  };
45
49
  // Gated by the same labs flag as the export, so the two halves round-trip or stay
46
50
  // silent together: off, activeFields resolves empty and every custom_fields.* column
@@ -48,7 +52,7 @@ function makeImporter(deps) {
48
52
  const customFields = {
49
53
  activeFields: async () => (labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : []),
50
54
  planWrite: values => deps.customFields.values.planWrite(values),
51
- applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, executor)
55
+ applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, { executor })
52
56
  };
53
57
  return new importer_1.default({
54
58
  knex: deps.knex,
@@ -18,7 +18,9 @@ interface ImporterServices {
18
18
  getMembersRepository(): Promise<Omit<MembersRepository, 'getImportLabel'>>;
19
19
  getDefaultTier(): Promise<Tier>;
20
20
  getTierByName(name: string): Promise<Tier | null>;
21
- getGiftService(): GiftService;
21
+ getGiftService(): {
22
+ reassignRedeemer(input: {giftId: string; memberId: string; transacting?: Knex.Transaction}): Promise<void>;
23
+ };
22
24
  sendEmail: EmailNotifications['send'];
23
25
  urlFor: EmailNotifications['urlFor'];
24
26
  addJob(job: {job: () => Promise<void>; offloaded: boolean; name: string}): void;
@@ -31,7 +33,7 @@ interface ImporterServices {
31
33
  definitions: {browse(): Promise<CsvField[]>};
32
34
  values: {
33
35
  planWrite(values: Record<string, unknown>): Promise<unknown[]>;
34
- applyWrite(memberId: string, plan: unknown[], executor: Knex): Promise<void>;
36
+ applyWrite(memberId: string, plan: unknown[], options?: {executor?: Knex}): Promise<void>;
35
37
  };
36
38
  };
37
39
  }
@@ -72,7 +74,11 @@ export function makeImporter(deps: ImporterServices) {
72
74
  // Gifts is initialised at boot and always present at request time; the getter
73
75
  // resolves it lazily so the ready service is picked up whenever a row uses it.
74
76
  const gifts: GiftService = {
75
- reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer(giftId, memberId, options)
77
+ reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer({
78
+ giftId,
79
+ memberId,
80
+ transacting: options.transacting
81
+ })
76
82
  };
77
83
 
78
84
  // Gated by the same labs flag as the export, so the two halves round-trip or stay
@@ -81,7 +87,7 @@ export function makeImporter(deps: ImporterServices) {
81
87
  const customFields: CustomFieldsImport = {
82
88
  activeFields: async () => (labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : []),
83
89
  planWrite: values => deps.customFields.values.planWrite(values),
84
- applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, executor)
90
+ applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, {executor})
85
91
  };
86
92
 
87
93
  return new MembersCSVImporter({
@@ -525,7 +525,7 @@ module.exports = class RouterController {
525
525
  * @param {string} options.cancelUrl URL to redirect to after cancelled checkout
526
526
  * @param {string} [options.email] Email address of the customer
527
527
  * @param {object} [options.member] Currently authenticated member OR member associated with the email address
528
- * @param {object} [options.gift] Active gift subscription for the member
528
+ * @param {number|null} [options.giftTrialDays] Stable continuation decision from gift subscriptions
529
529
  * @param {boolean} options.isAuthenticated
530
530
  * @param {object} options.metadata Metadata to be passed to Stripe
531
531
  * @returns
@@ -680,9 +680,11 @@ module.exports = class RouterController {
680
680
 
681
681
  /**
682
682
  * @param {object} options
683
- * @param {object} options.tier
684
- * @param {'month'|'year'} options.cadence
685
- * @param {string} options.email
683
+ * @param {string} options.tierId
684
+ * @param {string} [options.offerId]
685
+ * @param {string} [options.cadence]
686
+ * @param {number} [options.duration]
687
+ * @param {string} [options.email]
686
688
  * @param {string} options.successUrl
687
689
  * @param {string} options.cancelUrl
688
690
  * @param {object} options.metadata
@@ -698,10 +700,26 @@ module.exports = class RouterController {
698
700
  }
699
701
 
700
702
  try {
701
- const paymentLink = await this._paymentsService.getGiftPaymentLink(options);
702
-
703
- return {url: paymentLink};
703
+ return await this._giftService.service.startCheckout({
704
+ tierId: options.tierId,
705
+ offerId: options.offerId,
706
+ cadence: options.cadence,
707
+ duration: options.duration,
708
+ metadata: options.metadata,
709
+ successUrl: options.successUrl,
710
+ cancelUrl: options.cancelUrl,
711
+ buyer: {
712
+ memberId: options.member?.id ?? null,
713
+ email: options.member?.get('email') ?? options.email ?? null,
714
+ name: options.member?.get('name') ?? null,
715
+ isAuthenticated: options.isAuthenticated
716
+ }
717
+ });
704
718
  } catch (err) {
719
+ if (err instanceof BadRequestError || err instanceof NoPermissionError) {
720
+ throw err;
721
+ }
722
+
705
723
  logging.error(err);
706
724
  this._sentry?.captureException?.(err);
707
725
  throw new BadRequestError({
@@ -788,7 +806,7 @@ module.exports = class RouterController {
788
806
  let tier;
789
807
  let cadence;
790
808
  let offer;
791
- let gift;
809
+ let giftTrialDays;
792
810
 
793
811
  if (req.body.continueFromGift) {
794
812
  if (!isAuthenticated || !member) {
@@ -796,25 +814,16 @@ module.exports = class RouterController {
796
814
  message: tpl(messages.signInRequired)
797
815
  });
798
816
  }
799
- if (member.get('status') !== 'gift') {
800
- throw new BadRequestError({
801
- message: tpl(messages.badRequest),
802
- context: 'Member does not have an active gift subscription'
803
- });
804
- }
805
-
806
- gift = await this._giftService.service.getActiveByMember(member.id);
807
- if (!gift) {
808
- throw new BadRequestError({
809
- message: tpl(messages.badRequest),
810
- context: 'No active gift subscription found for member'
811
- });
812
- }
817
+ const continuation = await this._giftService.service.preparePaidContinuation({
818
+ memberId: member.id,
819
+ memberStatus: member.get('status')
820
+ });
813
821
 
814
822
  ({tier, cadence} = await this._getSubscriptionCheckoutData({
815
- tierId: gift.tierId,
816
- cadence: gift.cadence
823
+ tierId: continuation.tierId,
824
+ cadence: continuation.cadence
817
825
  }));
826
+ giftTrialDays = continuation.trialDays;
818
827
  } else {
819
828
  ({tier, cadence, offer} = await this._getSubscriptionCheckoutData(req.body));
820
829
  }
@@ -825,7 +834,7 @@ module.exports = class RouterController {
825
834
  tier,
826
835
  cadence,
827
836
  offer,
828
- gift
837
+ giftTrialDays
829
838
  });
830
839
 
831
840
  // Add welcome_page_url to the response if available and member is authenticated
@@ -842,19 +851,12 @@ module.exports = class RouterController {
842
851
  });
843
852
  }
844
853
 
845
- if (req.body.offerId) {
846
- throw new BadRequestError({
847
- message: tpl(messages.badRequest),
848
- context: 'Offers cannot be applied to gift subscriptions'
849
- });
850
- }
851
-
852
- const data = await this._getSubscriptionCheckoutData(req.body);
853
-
854
854
  response = await this._createGiftCheckoutSession({
855
855
  ...options,
856
- ...data,
857
- duration: 1, // gifts are currently 1 month or 1 year only
856
+ tierId: req.body.tierId,
857
+ offerId: req.body.offerId,
858
+ cadence: req.body.cadence,
859
+ duration: req.body.duration,
858
860
  successUrl: siteUrl,
859
861
  cancelUrl: options.cancelUrl || siteUrl
860
862
  });
@@ -67,8 +67,7 @@ module.exports = function MembersAPI({
67
67
  MemberFeedback,
68
68
  Automation,
69
69
  WelcomeEmailAutomationRun,
70
- AutomatedEmailRecipient,
71
- Gift
70
+ AutomatedEmailRecipient
72
71
  },
73
72
  tiersService,
74
73
  stripeAPIService,
@@ -142,7 +141,7 @@ module.exports = function MembersAPI({
142
141
  memberAttributionService,
143
142
  MemberEmailChangeEvent,
144
143
  AutomatedEmailRecipient,
145
- Gift
144
+ giftSubscriptions: giftService
146
145
  });
147
146
 
148
147
  const nextPaymentCalculator = new NextPaymentCalculator();
@@ -192,8 +191,7 @@ module.exports = function MembersAPI({
192
191
  Offer,
193
192
  offersAPI,
194
193
  stripeAPIService,
195
- settingsCache,
196
- giftService
194
+ settingsCache
197
195
  });
198
196
 
199
197
  const memberController = new MemberController({
@@ -283,7 +281,10 @@ module.exports = function MembersAPI({
283
281
  }
284
282
 
285
283
  if (giftToken) {
286
- await giftService.service.redeem(giftToken, member.id);
284
+ await giftService.service.redeem({
285
+ token: giftToken,
286
+ memberId: member.id
287
+ });
287
288
  }
288
289
 
289
290
  await MemberLoginEvent.add({member_id: member.id});
@@ -316,7 +317,12 @@ module.exports = function MembersAPI({
316
317
  {name, email, labels, newsletters, attribution, geolocation, status: 'gift'},
317
318
  {transacting}
318
319
  );
319
- await giftService.service.redeem(giftToken, created.id, {transacting, newMember: true});
320
+ await giftService.service.redeem({
321
+ token: giftToken,
322
+ memberId: created.id,
323
+ transacting,
324
+ newMember: true
325
+ });
320
326
  return created;
321
327
  });
322
328
  } else {
@@ -37,7 +37,7 @@ module.exports = class EventRepository {
37
37
  urlService,
38
38
  MemberEmailChangeEvent,
39
39
  AutomatedEmailRecipient,
40
- Gift
40
+ giftSubscriptions
41
41
  }) {
42
42
  this._DonationPaymentEvent = DonationPaymentEvent;
43
43
  this._MemberSubscribeEvent = MemberSubscribeEvent;
@@ -57,7 +57,7 @@ module.exports = class EventRepository {
57
57
  this._memberAttributionService = memberAttributionService;
58
58
  this._MemberEmailChangeEvent = MemberEmailChangeEvent;
59
59
  this._AutomatedEmailRecipient = AutomatedEmailRecipient;
60
- this._Gift = Gift;
60
+ this._giftSubscriptions = giftSubscriptions;
61
61
  this._knex = db.knex;
62
62
  }
63
63
 
@@ -462,101 +462,11 @@ module.exports = class EventRepository {
462
462
  }
463
463
 
464
464
  async getGiftPurchaseEvents(options = {}, filter) {
465
- options = {
466
- ...options,
467
- withRelated: ['buyer', 'tier'],
468
- filter: 'buyer_member_id:-null+custom:true',
469
- useBasicCount: true,
470
- mongoTransformer: chainTransformers(
471
- // First set the filter manually
472
- replaceCustomFilterTransformer(filter),
473
-
474
- // Map the used keys in that filter
475
- ...mapKeys({
476
- 'data.created_at': 'purchased_at',
477
- 'data.member_id': 'buyer_member_id'
478
- })
479
- )
480
- };
481
-
482
- if (options.order) {
483
- options.order = options.order.replace(/created_at/g, 'purchased_at');
484
- }
485
-
486
- const {data: models, meta} = await this._Gift.findPage(options);
487
-
488
- const data = models.map((model) => {
489
- const json = model.toJSON(options);
490
-
491
- return {
492
- type: 'gift_purchase_event',
493
- data: {
494
- id: json.id,
495
- member: json.buyer || null,
496
- member_id: json.buyer_member_id,
497
- tier_name: json.tier?.name,
498
- cadence: json.cadence,
499
- duration: json.duration,
500
- amount: json.amount,
501
- currency: json.currency,
502
- created_at: json.purchased_at
503
- }
504
- };
505
- });
506
-
507
- return {
508
- data,
509
- meta
510
- };
465
+ return this._giftSubscriptions.service.browsePurchaseEvents(options, filter);
511
466
  }
512
467
 
513
468
  async getGiftRedemptionEvents(options = {}, filter) {
514
- options = {
515
- ...options,
516
- withRelated: ['redeemer', 'tier'],
517
- filter: 'redeemer_member_id:-null+custom:true',
518
- useBasicCount: true,
519
- mongoTransformer: chainTransformers(
520
- // First set the filter manually
521
- replaceCustomFilterTransformer(filter),
522
-
523
- // Map the used keys in that filter
524
- ...mapKeys({
525
- 'data.created_at': 'redeemed_at',
526
- 'data.member_id': 'redeemer_member_id'
527
- })
528
- )
529
- };
530
-
531
- if (options.order) {
532
- options.order = options.order.replace(/created_at/g, 'redeemed_at');
533
- }
534
-
535
- const {data: models, meta} = await this._Gift.findPage(options);
536
-
537
- const data = models.map((model) => {
538
- const json = model.toJSON(options);
539
-
540
- return {
541
- type: 'gift_redemption_event',
542
- data: {
543
- id: json.id,
544
- member: json.redeemer || null,
545
- member_id: json.redeemer_member_id,
546
- tier_name: json.tier?.name,
547
- cadence: json.cadence,
548
- duration: json.duration,
549
- amount: json.amount,
550
- currency: json.currency,
551
- created_at: json.redeemed_at
552
- }
553
- };
554
- });
555
-
556
- return {
557
- data,
558
- meta
559
- };
469
+ return this._giftSubscriptions.service.browseRedemptionEvents(options, filter);
560
470
  }
561
471
 
562
472
  async getGiftEndedEvents(options = {}, filter) {
@@ -34,7 +34,7 @@ const CUSTOM_FIELDS_EDITED_ACTION = 'custom_fields_edited';
34
34
 
35
35
  /**
36
36
  * @typedef {object} IGiftServiceWrapper
37
- * @prop {{getActiveByMembers: (memberIds: string[]) => Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>}} service
37
+ * @prop {{getMemberPresentations: (memberIds: string[]) => Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>}} service
38
38
  */
39
39
 
40
40
  module.exports = class MemberBREADService {
@@ -347,7 +347,7 @@ module.exports = class MemberBREADService {
347
347
  * @private
348
348
  * Fetches active redeemed gifts for any gift-status members in the input list.
349
349
  * @param {import('bookshelf').Model[]} members - Bookshelf member models
350
- * @returns {Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>} keyed by member.id → active Gift
350
+ * @returns {Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>} keyed by member.id → stable gift presentation
351
351
  */
352
352
  async fetchActiveGiftsForMembers(members) {
353
353
  const giftMemberIds = members
@@ -359,7 +359,7 @@ module.exports = class MemberBREADService {
359
359
  }
360
360
 
361
361
  try {
362
- return await this.giftService.service.getActiveByMembers(giftMemberIds);
362
+ return await this.giftService.service.getMemberPresentations(giftMemberIds);
363
363
  } catch (e) {
364
364
  logging.error(`Failed to load active gifts for members - ${giftMemberIds.join(', ')}.`);
365
365
  logging.error(e);
@@ -14,7 +14,6 @@ class PaymentsService {
14
14
  * @param {import('../../../offers/application/offers-api')} deps.offersAPI
15
15
  * @param {import('../../../stripe/stripe-api')} deps.stripeAPIService
16
16
  * @param {{get(key: string): any}} deps.settingsCache
17
- * @param {{service: import('../../../gifts/gift-service').GiftService}} deps.giftService
18
17
  */
19
18
  constructor(deps) {
20
19
  /** @private */
@@ -31,9 +30,6 @@ class PaymentsService {
31
30
  this.stripeAPIService = deps.stripeAPIService;
32
31
  /** @private */
33
32
  this.settingsCache = deps.settingsCache;
34
- /** @private */
35
- this.giftService = deps.giftService;
36
-
37
33
  DomainEvents.subscribe(OfferCreatedEvent, async (event) => {
38
34
  await this.getCouponForOffer(event.data.offer.id);
39
35
  });
@@ -65,7 +61,7 @@ class PaymentsService {
65
61
  * @param {Tier.Cadence} params.cadence
66
62
  * @param {Offer} [params.offer]
67
63
  * @param {Member} [params.member]
68
- * @param {import('../../../gifts/gift').Gift} [params.gift]
64
+ * @param {number|null} [params.giftTrialDays]
69
65
  * @param {Object.<string, any>} [params.metadata]
70
66
  * @param {string} params.successUrl
71
67
  * @param {string} params.cancelUrl
@@ -73,7 +69,7 @@ class PaymentsService {
73
69
  *
74
70
  * @returns {Promise<URL>}
75
71
  */
76
- async getPaymentLink({tier, cadence, offer, member, gift, metadata, successUrl, cancelUrl, email}) {
72
+ async getPaymentLink({tier, cadence, offer, member, giftTrialDays, metadata, successUrl, cancelUrl, email}) {
77
73
  let coupon = null;
78
74
  let trialDays = null;
79
75
  if (offer) {
@@ -94,12 +90,10 @@ class PaymentsService {
94
90
  }
95
91
  }
96
92
 
97
- // If continuing from a gift subscription, add gift remaining days as trial
98
- if (trialDays === null && gift) {
99
- const remainingDays = this.giftService.service.getRemainingActiveDays(gift);
100
- if (remainingDays > 0) {
101
- trialDays = Math.min(remainingDays, 730); // Stripe max trial period
102
- }
93
+ // Gift subscriptions owns the continuation calculation. This adapter only
94
+ // translates the stable decision into the recurring Stripe checkout.
95
+ if (trialDays === null && giftTrialDays !== undefined) {
96
+ trialDays = giftTrialDays;
103
97
  }
104
98
 
105
99
  let customer = null;
@@ -163,62 +157,6 @@ class PaymentsService {
163
157
  return session.url;
164
158
  }
165
159
 
166
- /**
167
- * @param {object} params
168
- * @param {import('../../../tiers/tier')} params.tier
169
- * @param {'month'|'year'} params.cadence
170
- * @param {number} params.duration
171
- * @param {string} params.successUrl
172
- * @param {string} params.cancelUrl
173
- * @param {object} params.metadata
174
- * @param {object} [params.member]
175
- * @param {boolean} params.isAuthenticated
176
- * @param {string} [params.email]
177
- *
178
- * @returns {Promise<string>}
179
- */
180
- async getGiftPaymentLink({tier, cadence, duration, metadata, successUrl, cancelUrl, member, isAuthenticated, email}) {
181
- let customer = null;
182
- if (member && isAuthenticated) {
183
- customer = await this.getCustomerForMember(member);
184
- }
185
-
186
- const amount = tier.getPrice(cadence);
187
- const currency = tier.currency.toLowerCase();
188
-
189
- const token = this.giftService.service.generateToken();
190
-
191
- const successUrlObj = new URL(successUrl);
192
- successUrlObj.searchParams.set('stripe', 'gift-purchase-success');
193
- successUrlObj.searchParams.set('gift_token', token);
194
- successUrlObj.searchParams.set('gift_tier', tier.id.toHexString());
195
- successUrlObj.searchParams.set('gift_cadence', cadence);
196
-
197
- const data = {
198
- amount,
199
- currency,
200
- tierName: tier.name,
201
- cadence,
202
- duration,
203
- metadata: {
204
- ...metadata,
205
- ghost_gift: 'true',
206
- gift_token: token,
207
- tier_id: tier.id.toHexString(),
208
- cadence,
209
- duration: String(duration)
210
- },
211
- successUrl: successUrlObj.toString(),
212
- cancelUrl,
213
- customer,
214
- customerEmail: !customer && email ? email : null
215
- };
216
-
217
- const session = await this.stripeAPIService.createGiftCheckoutSession(data);
218
-
219
- return session.url;
220
- }
221
-
222
160
  async getCustomerForMember(member) {
223
161
  const rows = await this.StripeCustomerModel.where({
224
162
  member_id: member.id
@@ -3,19 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.activeFields = activeFields;
4
4
  const schema_1 = require("./schema");
5
5
  const FIELDS_TABLE = 'members_custom_fields';
6
- // Shared query builders over the field definitions, used by both the definitions
7
- // service and the values service.
8
- //
9
- // The `status = 'active'` filter is the one invariant worth centralising: archived
10
- // fields must stay out of every read and every write, and that's enforced by a
11
- // filter, not a database constraint — nothing stops a value row referencing an
12
- // archived field, so a query that forgets the filter is a silent bug. Keeping the
13
- // filter in one builder means there's one place to get it right.
14
- /**
15
- * The active field definitions. Takes the executor — a knex instance or a
16
- * transaction — so the same query runs standalone or inside a write's
17
- * transaction; callers chain their own `select`/`orderBy`/`whereIn` onto it.
18
- */
6
+ // Archived fields must stay out of every read and write, and nothing in the database
7
+ // enforces that no constraint stops a value row referencing an archived field. A query
8
+ // that forgets the filter is a silent bug, so the filter lives in one place.
9
+ /** Takes the executor so the same query runs standalone or inside a write's transaction. */
19
10
  function activeFields(db) {
20
11
  return db(FIELDS_TABLE).where('status', schema_1.FIELD_STATUS.active);
21
12
  }
@@ -3,20 +3,11 @@ import {FIELD_STATUS} from './schema';
3
3
 
4
4
  const FIELDS_TABLE = 'members_custom_fields';
5
5
 
6
- // Shared query builders over the field definitions, used by both the definitions
7
- // service and the values service.
8
- //
9
- // The `status = 'active'` filter is the one invariant worth centralising: archived
10
- // fields must stay out of every read and every write, and that's enforced by a
11
- // filter, not a database constraint — nothing stops a value row referencing an
12
- // archived field, so a query that forgets the filter is a silent bug. Keeping the
13
- // filter in one builder means there's one place to get it right.
6
+ // Archived fields must stay out of every read and write, and nothing in the database
7
+ // enforces that no constraint stops a value row referencing an archived field. A query
8
+ // that forgets the filter is a silent bug, so the filter lives in one place.
14
9
 
15
- /**
16
- * The active field definitions. Takes the executor — a knex instance or a
17
- * transaction — so the same query runs standalone or inside a write's
18
- * transaction; callers chain their own `select`/`orderBy`/`whereIn` onto it.
19
- */
10
+ /** Takes the executor so the same query runs standalone or inside a write's transaction. */
20
11
  export function activeFields(db: Knex) {
21
12
  return db(FIELDS_TABLE).where('status', FIELD_STATUS.active);
22
13
  }