ghost 6.54.1 → 6.56.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 (356) hide show
  1. package/components/tryghost-adapter-base-scheduling-0.2.0.tgz +0 -0
  2. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  3. package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
  4. package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
  5. package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
  6. package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
  7. package/components/tryghost-nql-string-0.0.0.tgz +0 -0
  8. package/core/boot.js +3 -6
  9. package/core/built/admin/assets/{Combination-CJG7Flp-.js → Combination-BCm9FL4o.js} +1 -1
  10. package/core/built/admin/assets/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
  11. package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
  12. package/core/built/admin/assets/{account-migration-BklF2I_I.js → account-migration-D3O_yVck.js} +1 -1
  13. package/core/built/admin/assets/activitypub/{account-migration-CNovr6W-.js → account-migration-D8FbP-Z-.js} +7 -7
  14. package/core/built/admin/assets/activitypub/activitypub.js +6 -6
  15. package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-11EA1VSs.js} +2 -2
  16. package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-jfbLG-xe.js} +2 -2
  17. package/core/built/admin/assets/activitypub/{bluesky-sharing-Ceslb1_M.js → bluesky-sharing-f1jTQe2P.js} +16 -16
  18. package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-nmTOPY9M.js} +3 -3
  19. package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-BCwo9ep3.js} +2 -2
  20. package/core/built/admin/assets/activitypub/{dist-DHAjBcV3.js → dist-BEfX-0aW.js} +12 -12
  21. package/core/built/admin/assets/activitypub/{domain-C1qQvJ8C.js → domain-AaDlZlrA.js} +9 -9
  22. package/core/built/admin/assets/activitypub/{edit-profile-BcH-jEfM.js → edit-profile-DPi8Tl5M.js} +945 -945
  23. package/core/built/admin/assets/activitypub/{explore-AxwBh9dI.js → explore-CxTzjILQ.js} +8 -8
  24. package/core/built/admin/assets/activitypub/{feed-BaOlbh7a.js → feed-DPv5x2MX.js} +33 -33
  25. package/core/built/admin/assets/activitypub/{field-B7mYYQpN.js → field-DbgA8lat.js} +3 -3
  26. package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-BWuZoYkA.js} +2 -2
  27. package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-35VDQ2K2.js} +2 -2
  28. package/core/built/admin/assets/activitypub/{inbox-vXez-5uJ.js → inbox-DSrF_i1O.js} +85 -85
  29. package/core/built/admin/assets/activitypub/{label-Chv5T8bw.js → label-CPjFBbo7.js} +3 -3
  30. package/core/built/admin/assets/activitypub/layout-CTgrezN7.js +1 -0
  31. package/core/built/admin/assets/activitypub/{moderation-DtSM2i1I.js → moderation-BkDC1XVZ.js} +11 -11
  32. package/core/built/admin/assets/activitypub/{note-Dckp4S-2.js → note-xEyH4xT-.js} +7 -7
  33. package/core/built/admin/assets/activitypub/{notifications-CG5T22vg.js → notifications-DClaL3ui.js} +9 -9
  34. package/core/built/admin/assets/activitypub/{preferences-D_799jdl.js → preferences-DpnOa-4Z.js} +20 -20
  35. package/core/built/admin/assets/activitypub/{profile-D2JfXBQl.js → profile-Dvi9mz-6.js} +237 -237
  36. package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cs9ySd6Z.js} +2 -2
  37. package/core/built/admin/assets/activitypub/{routes-CCHe2dQp.js → routes-DFBeTnfn.js} +1139 -1150
  38. package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-C5u_VjzY.js} +3 -3
  39. package/core/built/admin/assets/activitypub/{settings-kx-9Enkz.js → settings-CV2fFu6D.js} +13 -13
  40. package/core/built/admin/assets/activitypub/{site-x4_IjVJ0.js → site-_TgDd_EM.js} +81 -81
  41. package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-BfnNL2oF.js} +5 -5
  42. package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DLndZkO-.js} +6 -6
  43. package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-A-PZTtRE.js} +10 -10
  44. package/core/built/admin/assets/activitypub/{tabs-CyPRQQ1N.js → tabs-BUbz7tea.js} +321 -319
  45. package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-BdX_xYYT.js} +3 -3
  46. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-EwuRemHh.js} +2 -2
  47. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-cy4ACBc4.js} +92 -90
  48. package/core/built/admin/assets/activitypub/{use-reply-chain-data-BAB6fNGt.js → use-reply-chain-data-CyFcGfan.js} +5 -5
  49. package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-7zk8mV2p.js} +2 -2
  50. package/core/built/admin/assets/audience-CkBb2LlO.js +1 -0
  51. package/core/built/admin/assets/{automated-emails-of1MvVsv.js → automated-emails-6t2bIRWf.js} +2 -2
  52. package/core/built/admin/assets/{automations-B7-y3dXt.js → automations-DhKeS_U5.js} +1 -1
  53. package/core/built/admin/assets/{automations-DRFjW5fw.js → automations-cMllU545.js} +1 -1
  54. package/core/built/admin/assets/{bluesky-sharing-BbbjWdzs.js → bluesky-sharing-DPGpyD-G.js} +1 -1
  55. package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
  56. package/core/built/admin/assets/{breadcrumb-B-M9Ip_2.js → breadcrumb-DIfcF5ow.js} +1 -1
  57. package/core/built/admin/assets/build-CsiH26uS.js +1 -0
  58. package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
  59. package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
  60. package/core/built/admin/assets/{chart-helpers-xiSsW8r0.js → chart-helpers-DV0FkdcD.js} +1 -1
  61. package/core/built/admin/assets/{checkbox-EhJKHFdZ.js → checkbox-BIl077a0.js} +1 -1
  62. package/core/built/admin/assets/{chunk.361.3cdb584b973959e52ccc.js → chunk.332.05991a101d67f732dec3.js} +16 -12
  63. package/core/built/admin/assets/chunk.524.87b52f4fada529df2c4c.js +25 -0
  64. package/core/built/admin/assets/chunk.582.340bcca873e806cbd4bb.js +27 -0
  65. package/core/built/admin/assets/{code-editor-view-CZtJFGhR.js → code-editor-view-C0aWNwdl.js} +1 -1
  66. package/core/built/admin/assets/comments-qwhIjH3c.js +1 -0
  67. package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
  68. package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
  69. package/core/built/admin/assets/{date-range-select-CTlyo8zW.js → date-range-select-dXc-GsVe.js} +1 -1
  70. package/core/built/admin/assets/{disabled-sources-indicator-qs2GfLvl.js → disabled-sources-indicator-DYAXW-6V.js} +1 -1
  71. package/core/built/admin/assets/dist-BUKmD51u.js +1 -0
  72. package/core/built/admin/assets/{dist-2Tnz2Yfq.js → dist-CNlMj7ML.js} +1 -1
  73. package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
  74. package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS0.js} +1 -1
  75. package/core/built/admin/assets/{domain-CPNXKVOV.js → domain-B5ogXUBa.js} +1 -1
  76. package/core/built/admin/assets/{dropzone-BmCXnw6J.js → dropzone-BnFG7dDP.js} +1 -1
  77. package/core/built/admin/assets/{edit-profile-SGrJve5M.js → edit-profile-B07jCZy9.js} +1 -1
  78. package/core/built/admin/assets/editor-DDYSOXGN.js +8 -0
  79. package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
  80. package/core/built/admin/assets/{explore-CUYLv9G9.js → explore-Ca1y8HW_.js} +1 -1
  81. package/core/built/admin/assets/{feed-CPdJMttL.js → feed-x4iFMBP4.js} +1 -1
  82. package/core/built/admin/assets/field-ChCAabZg.js +1 -0
  83. package/core/built/admin/assets/filter-sources-LdtH1e-T.js +1 -0
  84. package/core/built/admin/assets/{filters-bXkkWro8.js → filters-CLehPo_D.js} +1 -1
  85. package/core/built/admin/assets/filters-CjVKRP8U.js +1 -0
  86. package/core/built/admin/assets/{ghost-05f9a7182ed56903dba05878a5b8422b.js → ghost-7f01131115186d1af84ead76e79646dd.js} +275 -255
  87. package/core/built/admin/assets/{ghost-3063eae29e6c9cc6d6e7131928a27da0.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
  88. package/core/built/admin/assets/{ghost-dark-c8d0ab7f515b2010c173e23e9b32ea8a.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
  89. package/core/built/admin/assets/gift-link-modal-Buqh2vXh.js +1 -0
  90. package/core/built/admin/assets/{gift-links-nBxJjZiQ.js → gift-links-CsUhefWG.js} +1 -1
  91. package/core/built/admin/assets/growth-DajOBdN0.js +1 -0
  92. package/core/built/admin/assets/growth-Dy2UOSay.js +3 -0
  93. package/core/built/admin/assets/{hooks-BApwwBE0.js → hooks-CixO01lL.js} +1 -1
  94. package/core/built/admin/assets/{inbox-Xx1CdnfO.js → inbox-BUOLDoji.js} +13 -13
  95. package/core/built/admin/assets/index-08n05-rc.js +67 -0
  96. package/core/built/admin/assets/index-DgUzgFvS.css +1 -0
  97. package/core/built/admin/assets/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
  98. package/core/built/admin/assets/input-group-uSK3XT7b.js +1 -0
  99. package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.js} +1 -1
  100. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9132 -9087
  101. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +121 -121
  102. package/core/built/admin/assets/koenig-lexical/style.css +1 -1
  103. package/core/built/admin/assets/{koenig-lexical-B61IALf5.js → koenig-lexical-BFc4yFed.js} +82 -82
  104. package/core/built/admin/assets/{kpi-card-DCdlyaSS.js → kpi-card-BN-E6WYX.js} +1 -1
  105. package/core/built/admin/assets/{kpi-card-BlZaG1Lo.js → kpi-card-CCt-qeWD.js} +1 -1
  106. package/core/built/admin/assets/{kpi-helpers-DpAuS1Nb.js → kpi-helpers-DlxTbTs4.js} +1 -1
  107. package/core/built/admin/assets/{kpi-tabs-Dme44bQS.js → kpi-tabs-BlcUttpn.js} +1 -1
  108. package/core/built/admin/assets/{label-dSs5aepE.js → label-AVrxwmsz.js} +1 -1
  109. package/core/built/admin/assets/label-picker-DVnpu_IF.js +1 -0
  110. package/core/built/admin/assets/labels-DAIqKed7.js +1 -0
  111. package/core/built/admin/assets/layout-B0YLh7R9.js +1 -0
  112. package/core/built/admin/assets/link-helpers-D9LsDOkG.js +1 -0
  113. package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
  114. package/core/built/admin/assets/member-custom-fields-C8NpZYza.js +1 -0
  115. package/core/built/admin/assets/member-detail-CIVAdKXf.js +1 -0
  116. package/core/built/admin/assets/members-oLPiSkXD.js +10 -0
  117. package/core/built/admin/assets/modals-CB7v-k3T.js +31 -0
  118. package/core/built/admin/assets/moderation-CQ3urTFE.js +1 -0
  119. package/core/built/admin/assets/newsletter-rbeDX9Wk.js +1 -0
  120. package/core/built/admin/assets/{newsletters-D1jbtV-h.js → newsletters-BXJbJgA-.js} +1 -1
  121. package/core/built/admin/assets/newsletters-vt3Ni1Ov.js +1 -0
  122. package/core/built/admin/assets/note-Dk877BTv.js +1 -0
  123. package/core/built/admin/assets/notifications-CPlLjPxO.js +1 -0
  124. package/core/built/admin/assets/{offers-CrY6nSRR.js → offers-DEniVRD5.js} +1 -1
  125. package/core/built/admin/assets/onboarding-route-DdStOtjy.js +1 -0
  126. package/core/built/admin/assets/overview--4V-kx9c.js +1 -0
  127. package/core/built/admin/assets/overview-DSLv30oz.js +1 -0
  128. package/core/built/admin/assets/{page-header-op0eN36I.js → page-header-pp4dWmOS.js} +1 -1
  129. package/core/built/admin/assets/{pages-BbNWFcGj.js → pages-B4goRPUn.js} +1 -1
  130. package/core/built/admin/assets/{post-analytics-WwRDEnrV.js → post-analytics-BMJrI81R.js} +1 -1
  131. package/core/built/admin/assets/post-analytics-header-pU2m9ZuV.js +1 -0
  132. package/core/built/admin/assets/post-analytics-provider-Btml5vJI.js +1 -0
  133. package/core/built/admin/assets/{post-share-modal-Dx-MEjhI.js → post-share-modal-AcUZkbfL.js} +1 -1
  134. package/core/built/admin/assets/{posts-EWpZIjkr.js → posts-DhXE7b6r.js} +1 -1
  135. package/core/built/admin/assets/preferences-Ru4CRPHC.js +5 -0
  136. package/core/built/admin/assets/profile-BNb1oj7D.js +1 -0
  137. package/core/built/admin/assets/{referrers-BCfFYSHc.js → referrers-pmJT4k3-.js} +1 -1
  138. package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-BmEZf6c6.js} +1 -1
  139. package/core/built/admin/assets/select-B0HteHrp.js +1 -0
  140. package/core/built/admin/assets/settings-B1C9Q3wo.js +4 -0
  141. package/core/built/admin/assets/{settings-H6HxFgfI.js → settings-BGgk7oCI.js} +1 -1
  142. package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
  143. package/core/built/admin/assets/{share-modal-BJCadJyp.js → share-modal-BeuydaBf.js} +1 -1
  144. package/core/built/admin/assets/social-urls-DA2lXrhW.js +8 -0
  145. package/core/built/admin/assets/{sort-button-DyYlfYp7.js → sort-button-Df4t2AL8.js} +1 -1
  146. package/core/built/admin/assets/{source-icon-_ubRDbg_.js → source-icon-ajoaKb41.js} +1 -1
  147. package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
  148. package/core/built/admin/assets/{stats-BQZMGMvn.js → stats-C_lfxZAw.js} +1 -1
  149. package/core/built/admin/assets/{stats-view-Doei-qCX.js → stats-view-Cxot1iD8.js} +1 -1
  150. package/core/built/admin/assets/{step-1-IKiLBPoC.js → step-1-ogAA4lDr.js} +1 -1
  151. package/core/built/admin/assets/{step-2-jJ41PsDN.js → step-2-C5Xini-7.js} +1 -1
  152. package/core/built/admin/assets/{step-3-CaqFAEqf.js → step-3-CZv1FV81.js} +2 -2
  153. package/core/built/admin/assets/{switch-t-WyENU2.js → switch-CyQcwMAv.js} +1 -1
  154. package/core/built/admin/assets/{table-DxUL78jU.js → table-DFj1wiYv.js} +1 -1
  155. package/core/built/admin/assets/tabs-D4y1gqUd.js +1 -0
  156. package/core/built/admin/assets/{tags-CNujGwkT.js → tags-Dmft-Y4O.js} +1 -1
  157. package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
  158. package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
  159. package/core/built/admin/assets/{tiers-BgYLWRJv.js → tiers-DYsa4DCo.js} +1 -1
  160. package/core/built/admin/assets/{toggle-group-BeEpQ69p.js → toggle-group-R2QMFvXW.js} +1 -1
  161. package/core/built/admin/assets/{topic-filter-Bb5MOD-1.js → topic-filter-DGDxDalo.js} +1 -1
  162. package/core/built/admin/assets/{use-analytics-data-Cds6XDjg.js → use-analytics-data-C76v4LIp.js} +1 -1
  163. package/core/built/admin/assets/use-filter-params-WrdhAISv.js +1 -0
  164. package/core/built/admin/assets/{use-growth-stats-D8uf98-G.js → use-growth-stats-aNXJOmQr.js} +1 -1
  165. package/core/built/admin/assets/{use-post-referrers-CRtgyuXB.js → use-post-referrers-CG0SZioj.js} +1 -1
  166. package/core/built/admin/assets/{use-reply-chain-data-DbKYX50v.js → use-reply-chain-data-RLdPKRmS.js} +1 -1
  167. package/core/built/admin/assets/{use-simple-pagination-DC01kpo0.js → use-simple-pagination-DgcF69KI.js} +1 -1
  168. package/core/built/admin/assets/{use-tinybird-query-C7bQRJWw.js → use-tinybird-query-C1R_Kbmy.js} +1 -1
  169. package/core/built/admin/assets/{use-tinybird-token-Az4tam7_.js → use-tinybird-token-C9fPjVM_.js} +1 -1
  170. package/core/built/admin/assets/{users-CcSCQFcQ.js → users-Dkv8NQZe.js} +1 -1
  171. package/core/built/admin/assets/{virtual-list-Dhuh9fGx.js → virtual-list-BmTUzeoF.js} +1 -1
  172. package/core/built/admin/assets/web-BqW0b0_C.js +1 -0
  173. package/core/built/admin/assets/web-Cy5GKMWM.js +2 -0
  174. package/core/built/admin/index.html +18 -18
  175. package/core/frontend/services/data/fetch-data.js +10 -5
  176. package/core/frontend/services/proxy.js +7 -8
  177. package/core/frontend/services/routing/api-adapter.js +115 -0
  178. package/core/frontend/services/routing/api-adapter.ts +157 -0
  179. package/core/frontend/services/routing/collection-router.js +5 -3
  180. package/core/frontend/services/routing/controllers/index.js +1 -1
  181. package/core/frontend/services/routing/controllers/rss.js +26 -31
  182. package/core/frontend/services/routing/controllers/rss.ts +66 -0
  183. package/core/frontend/services/routing/controllers/static.js +8 -7
  184. package/core/frontend/services/routing/parent-router.js +17 -4
  185. package/core/frontend/services/routing/permalink-adapter.js +26 -0
  186. package/core/frontend/services/routing/permalink-adapter.ts +23 -0
  187. package/core/frontend/services/routing/registry.js +59 -33
  188. package/core/frontend/services/routing/router-manager.js +14 -18
  189. package/core/frontend/services/routing/static-routes-router.js +11 -13
  190. package/core/frontend/services/routing/taxonomy-router.js +6 -2
  191. package/core/server/api/endpoints/automation-action-links.js +68 -0
  192. package/core/server/api/endpoints/automation-action-links.ts +46 -0
  193. package/core/server/api/endpoints/index.js +4 -0
  194. package/core/server/api/endpoints/settings.js +0 -2
  195. package/core/server/api/endpoints/utils/serializers/output/utils/url.js +11 -1
  196. package/core/server/data/migrations/versions/6.55/2026-07-27-11-25-44-add-to-hash-to-redirects.js +51 -0
  197. package/core/server/data/migrations/versions/6.55/2026-07-28-12-08-00-remove-routes-hash-from-settings.js +5 -0
  198. 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
  199. package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
  200. package/core/server/data/schema/commands.js +5 -0
  201. package/core/server/data/schema/default-settings/default-settings.json +0 -4
  202. package/core/server/data/schema/fixtures/fixtures.json +2 -2
  203. package/core/server/data/schema/schema.js +6 -1
  204. package/core/server/models/post.js +10 -1
  205. package/core/server/services/automations/automations-api.js +15 -0
  206. package/core/server/services/automations/automations-api.ts +19 -0
  207. package/core/server/services/automations/automations-repository.ts +20 -0
  208. package/core/server/services/automations/database-automations-repository.js +132 -30
  209. package/core/server/services/automations/database-automations-repository.ts +141 -12
  210. package/core/server/services/automations/database-date.js +10 -0
  211. package/core/server/services/automations/database-date.ts +5 -0
  212. package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
  213. package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
  214. package/core/server/services/automations/poll.js +7 -2
  215. package/core/server/services/automations/poll.ts +10 -2
  216. package/core/server/services/automations/scheduler-poll-time.js +40 -0
  217. package/core/server/services/automations/scheduler-poll-time.ts +38 -0
  218. package/core/server/services/automations/service.js +19 -3
  219. package/core/server/services/automations/service.ts +20 -3
  220. package/core/server/services/comments/comments-service-emails.js +10 -7
  221. package/core/server/services/comments/comments-service.js +9 -4
  222. package/core/server/services/email-rendering/partials/email-wrapper.hbs +3 -1
  223. package/core/server/services/email-service/email-service-wrapper.js +6 -1
  224. package/core/server/services/email-service/email-templates/partials/styles.hbs +8 -0
  225. package/core/server/services/email-service/newsletter-email-event-storage.js +41 -10
  226. package/core/server/services/gift-links/CONTEXT.md +9 -0
  227. package/core/server/services/gifts/CONTEXT.md +55 -0
  228. package/core/server/services/gifts/README.md +28 -0
  229. package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
  230. package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
  231. package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
  232. package/core/server/services/gifts/gift-checkout-offer.js +74 -0
  233. package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
  234. package/core/server/services/gifts/gift-codec.js +63 -0
  235. package/core/server/services/gifts/gift-codec.ts +62 -0
  236. package/core/server/services/gifts/gift-controller.js +6 -31
  237. package/core/server/services/gifts/gift-controller.ts +6 -69
  238. package/core/server/services/gifts/gift-email-service.ts +3 -2
  239. package/core/server/services/gifts/gift-schema.js +34 -0
  240. package/core/server/services/gifts/gift-schema.ts +39 -0
  241. package/core/server/services/gifts/gift-service-wrapper.js +13 -11
  242. package/core/server/services/gifts/gift-service.js +265 -52
  243. package/core/server/services/gifts/gift-service.ts +431 -97
  244. package/core/server/services/gifts/gift.ts +16 -39
  245. package/core/server/services/lib/dynamic-redirect-manager.js +12 -3
  246. package/core/server/services/link-redirection/link-redirect-repository.js +41 -4
  247. package/core/server/services/link-redirection/link-redirect.js +2 -0
  248. package/core/server/services/link-redirection/link-redirect.ts +3 -1
  249. package/core/server/services/link-redirection/link-redirects-service.js +38 -2
  250. package/core/server/services/link-tracking/click-event.js +3 -0
  251. package/core/server/services/link-tracking/index.js +38 -6
  252. package/core/server/services/link-tracking/link-click-repository.js +35 -9
  253. package/core/server/services/link-tracking/link-click-tracking-service.js +54 -4
  254. package/core/server/services/member-attribution/outbound-link-tagger.js +3 -1
  255. package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +20 -4
  256. package/core/server/services/member-welcome-emails/service.js +15 -3
  257. package/core/server/services/members/api.js +1 -2
  258. package/core/server/services/members/import-export/csv/formula.js +16 -0
  259. package/core/server/services/members/import-export/csv/formula.ts +14 -0
  260. package/core/server/services/members/import-export/csv/index.js +3 -1
  261. package/core/server/services/members/import-export/csv/index.ts +1 -0
  262. package/core/server/services/members/import-export/import/completion-email.js +22 -3
  263. package/core/server/services/members/import-export/import/completion-email.ts +29 -7
  264. package/core/server/services/members/import-export/import/importer.js +25 -4
  265. package/core/server/services/members/import-export/import/importer.ts +44 -5
  266. package/core/server/services/members/import-export/index.js +18 -1
  267. package/core/server/services/members/import-export/index.ts +32 -3
  268. package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
  269. package/core/server/services/members/members-api/members-api.js +13 -7
  270. package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
  271. package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
  272. package/core/server/services/members/members-api/services/payments-service.js +6 -68
  273. package/core/server/services/members/service.js +8 -1
  274. package/core/server/services/members-custom-fields/schema.js +8 -1
  275. package/core/server/services/members-custom-fields/schema.ts +8 -0
  276. package/core/server/services/members-custom-fields/values-service.js +136 -8
  277. package/core/server/services/members-custom-fields/values-service.ts +152 -10
  278. package/core/server/services/mentions/service.js +5 -1
  279. package/core/server/services/oembed/oembed-service.js +44 -3
  280. package/core/server/services/posts/posts-service.js +11 -0
  281. package/core/server/services/route-settings/dynamic-routing-service.js +21 -52
  282. package/core/server/services/route-settings/index.js +1 -16
  283. package/core/server/services/route-settings/route-settings-parser.js +25 -25
  284. package/core/server/services/route-settings/route-settings-parser.ts +27 -27
  285. package/core/server/services/route-settings/validation-errors.js +68 -4
  286. package/core/server/services/route-settings/validation-errors.ts +79 -4
  287. package/core/server/services/settings/settings-service.js +0 -18
  288. package/core/server/services/stats/posts-stats-service.js +27 -23
  289. package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
  290. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
  291. package/core/server/services/stripe/stripe-api.js +10 -0
  292. package/core/server/services/url/url-service-facade.js +1 -1
  293. package/core/server/services/url/url-service-facade.ts +8 -1
  294. package/core/server/web/api/endpoints/admin/routes.js +1 -0
  295. package/core/server/web/gift-preview/controller.js +10 -25
  296. package/core/shared/labs.js +2 -2
  297. package/package.json +8 -7
  298. package/pnpm-lock.yaml +28 -18
  299. package/pnpm-workspace.yaml +6 -4
  300. package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
  301. package/core/built/admin/assets/Growth-BTP1LthE.js +0 -3
  302. package/core/built/admin/assets/Newsletters-CSEX0myc.js +0 -1
  303. package/core/built/admin/assets/Overview-BjACEsJF.js +0 -1
  304. package/core/built/admin/assets/Web-CxxxjCcv.js +0 -2
  305. package/core/built/admin/assets/activitypub/layout-CziktJXo.js +0 -1
  306. package/core/built/admin/assets/audience-X_XsZivI.js +0 -1
  307. package/core/built/admin/assets/chunk.524.8365cd9280405c0f6216.js +0 -25
  308. package/core/built/admin/assets/chunk.582.61d078937e6102769f2c.js +0 -27
  309. package/core/built/admin/assets/comments-CvyXxOyN.js +0 -1
  310. package/core/built/admin/assets/constants-c-JNF9c2.js +0 -1
  311. package/core/built/admin/assets/dist-CjY-f5Z1.js +0 -1
  312. package/core/built/admin/assets/editor-BOG2YPUm.js +0 -8
  313. package/core/built/admin/assets/field-D0XISCK2.js +0 -1
  314. package/core/built/admin/assets/filter-sources-Bieps75g.js +0 -1
  315. package/core/built/admin/assets/filters-CuB-A7ma.js +0 -1
  316. package/core/built/admin/assets/gift-link-modal-su8MsRdg.js +0 -1
  317. package/core/built/admin/assets/growth-Ba6S5FQX.js +0 -1
  318. package/core/built/admin/assets/index-D8PbzuT9.css +0 -1
  319. package/core/built/admin/assets/index-ukW_0Uie.js +0 -67
  320. package/core/built/admin/assets/input-group-CGOXcy4l.js +0 -1
  321. package/core/built/admin/assets/label-picker-Uq91atex.js +0 -1
  322. package/core/built/admin/assets/labels-Cyayfd-e.js +0 -1
  323. package/core/built/admin/assets/layout-WUcz6tu1.js +0 -1
  324. package/core/built/admin/assets/link-helpers-tOlxmuH5.js +0 -1
  325. package/core/built/admin/assets/member-custom-fields-NCzrMuba.js +0 -1
  326. package/core/built/admin/assets/member-detail-DPWDtYnN.js +0 -1
  327. package/core/built/admin/assets/members-B9rO7qr4.js +0 -10
  328. package/core/built/admin/assets/modals-CxRaan1J.js +0 -31
  329. package/core/built/admin/assets/moderation-D8zon60E.js +0 -1
  330. package/core/built/admin/assets/multi-select-combobox-1wYm3nle.js +0 -1
  331. package/core/built/admin/assets/newsletter-DbYMRxRS.js +0 -1
  332. package/core/built/admin/assets/note-PiWPnnrh.js +0 -1
  333. package/core/built/admin/assets/notifications-DEgqqB9G.js +0 -1
  334. package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
  335. package/core/built/admin/assets/onboarding-route-BzF6HeVn.js +0 -1
  336. package/core/built/admin/assets/overview-DixLeIqY.js +0 -1
  337. package/core/built/admin/assets/post-analytics-header-DsMoLx0R.js +0 -1
  338. package/core/built/admin/assets/post-analytics-provider-BJCPLMIb.js +0 -1
  339. package/core/built/admin/assets/preferences-D7-j6A4C.js +0 -5
  340. package/core/built/admin/assets/profile-Dl69QPp0.js +0 -1
  341. package/core/built/admin/assets/select-C1qDZnW4.js +0 -1
  342. package/core/built/admin/assets/settings-BbmC_Iqq.js +0 -4
  343. package/core/built/admin/assets/social-urls-BPL1nADp.js +0 -8
  344. package/core/built/admin/assets/tabs-BQcJQ1ke.js +0 -1
  345. package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
  346. package/core/built/admin/assets/use-filter-params-DjXkhO54.js +0 -1
  347. package/core/built/admin/assets/web-LtHNiNBV.js +0 -1
  348. package/core/server/services/gifts/gift-repository.js +0 -2
  349. package/core/server/services/gifts/gift-repository.ts +0 -29
  350. package/core/server/services/route-settings/activation-bridge.js +0 -164
  351. package/core/server/services/route-settings/activation-bridge.ts +0 -210
  352. package/core/server/services/route-settings/default-settings-manager.js +0 -63
  353. package/core/server/services/route-settings/route-settings.js +0 -170
  354. package/core/server/services/route-settings/settings-loader.js +0 -60
  355. package/core/server/services/route-settings/settings-path-manager.js +0 -47
  356. package/core/server/services/route-settings/validate.js +0 -440
@@ -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
@@ -47,6 +47,9 @@ let membersApi;
47
47
  let verificationTrigger;
48
48
 
49
49
  const buildImporterDeps = ({stripeAPIService}) => {
50
+ // Required here, not statically: boot builds the custom fields services before this
51
+ // one (the exporter below relies on the same).
52
+ const customFields = require('../members-custom-fields');
50
53
  return {
51
54
  getTimezone: () => settingsCache.get('timezone'),
52
55
  // A getter rather than a value because the threshold is an operator
@@ -81,7 +84,11 @@ const buildImporterDeps = ({stripeAPIService}) => {
81
84
  knex: db.knex,
82
85
  urlFor: urlUtils.urlFor.bind(urlUtils),
83
86
  stripeAPIService,
84
- productRepository: membersApi.productRepository
87
+ productRepository: membersApi.productRepository,
88
+ customFields: {
89
+ definitions: customFields.definitions,
90
+ values: customFields.values
91
+ }
85
92
  };
86
93
  };
87
94
 
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DbCustomFieldValueWithField = exports.DbCustomFieldValue = exports.DbCustomField = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
3
+ exports.DbCustomFieldValueWithField = exports.StoredCompositeValue = exports.DbCustomFieldValue = exports.DbCustomField = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const custom_field_types_1 = require("@tryghost/custom-field-types");
6
6
  const db_date_1 = require("../../lib/db-date");
@@ -37,6 +37,13 @@ exports.DbCustomFieldValue = zod_1.z.object({
37
37
  created_at: db_date_1.DbDate,
38
38
  updated_at: db_date_1.DbDate.nullable()
39
39
  });
40
+ // A composite value once decoded: sub-field keys to values, and no narrower. Which
41
+ // sub-fields a composite has is its field type's business, not the row's.
42
+ //
43
+ // It doubles as the guard on a stored blob. A JSON column can just as easily hold an
44
+ // array, a null or a bare number, and a record rejects all three — so parsing against
45
+ // this is what narrows the type, with no assertion needed.
46
+ exports.StoredCompositeValue = zod_1.z.record(zod_1.z.string(), zod_1.z.unknown());
40
47
  // The value join a read needs: the field's identity and type travel with the
41
48
  // stored columns, so a row can be decoded without a second lookup. `type` is
42
49
  // parsed as the field-type enum, which narrows it with no cast.
@@ -44,6 +44,14 @@ export const DbCustomFieldValue = z.object({
44
44
 
45
45
  type CustomFieldValueRow = z.infer<typeof DbCustomFieldValue>;
46
46
 
47
+ // A composite value once decoded: sub-field keys to values, and no narrower. Which
48
+ // sub-fields a composite has is its field type's business, not the row's.
49
+ //
50
+ // It doubles as the guard on a stored blob. A JSON column can just as easily hold an
51
+ // array, a null or a bare number, and a record rejects all three — so parsing against
52
+ // this is what narrows the type, with no assertion needed.
53
+ export const StoredCompositeValue = z.record(z.string(), z.unknown());
54
+
47
55
  // The value join a read needs: the field's identity and type travel with the
48
56
  // stored columns, so a row can be decoded without a second lookup. `type` is
49
57
  // parsed as the field-type enum, which narrows it with no cast.