ghost 6.60.0 → 6.61.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 (399) hide show
  1. package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
  2. package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
  3. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  4. package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
  5. package/core/boot.js +13 -4
  6. package/core/built/admin/assets/{Combination-gnJO3vIw.js → Combination-5fYEzgbs.js} +1 -1
  7. package/core/built/admin/assets/{PolarAngleAxis-C4qtHy1B.js → PolarAngleAxis-pkMyqz8g.js} +1 -1
  8. package/core/built/admin/assets/{RadialBarChart-Bh09Stsj.js → RadialBarChart-JAKoS4KT.js} +1 -1
  9. package/core/built/admin/assets/{about-BgpmhCuM.js → about-Bve7IJS2.js} +1 -1
  10. package/core/built/admin/assets/{account-migration-8qTEI1i1.js → account-migration-DRhAehfw.js} +1 -1
  11. package/core/built/admin/assets/{action-list-lGzZLHc8.js → action-list-C4v0-du8.js} +1 -1
  12. package/core/built/admin/assets/activitypub/{account-migration-DMHuPnJn.js → account-migration-C-aej4x_.js} +7 -7
  13. package/core/built/admin/assets/activitypub/activitypub.js +3 -4
  14. package/core/built/admin/assets/activitypub/{avatar-DhTo5D1B.js → avatar-DLPL3Abs.js} +59 -68
  15. package/core/built/admin/assets/activitypub/{avatar-flipboard-DnVgbEfa.js → avatar-flipboard-C9iDY-QV.js} +2 -2
  16. package/core/built/admin/assets/activitypub/{bluesky-sharing-BT4zAlW7.js → bluesky-sharing-BXkA5mqV.js} +8 -8
  17. package/core/built/admin/assets/activitypub/{content-formatters-BeDZ-DIa.js → content-formatters-CHcyMFk4.js} +17 -17
  18. package/core/built/admin/assets/activitypub/{copy-8sqxUuNd.js → copy-DIAOQoCZ.js} +2 -2
  19. package/core/built/admin/assets/activitypub/{domain-xQDDu1CS.js → domain-B3ihkPde.js} +8 -8
  20. package/core/built/admin/assets/activitypub/{edit-profile-BXdF_r2G.js → edit-profile-DsZM3rTX.js} +5 -5
  21. package/core/built/admin/assets/activitypub/{explore-CMt7gKlL.js → explore-BodNrZgR.js} +8 -8
  22. package/core/built/admin/assets/activitypub/{feed-Bwqdddp_.js → feed-PwH1iJ84.js} +9 -9
  23. package/core/built/admin/assets/activitypub/{field-4co_8FVg.js → field-D06-kmOJ.js} +3 -3
  24. package/core/built/admin/assets/activitypub/{hash-DwakMs4X.js → hash-Bm3WxYN4.js} +2 -2
  25. package/core/built/admin/assets/activitypub/{inbox-DQ5CwLma.js → inbox-BluD2ucT.js} +16 -16
  26. package/core/built/admin/assets/activitypub/{inbox-ckD4wo_r.js → inbox-d8oJRTf4.js} +2 -2
  27. package/core/built/admin/assets/activitypub/{label-B72sfvEU.js → label-FHSh6ZAw.js} +3 -3
  28. package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +1 -0
  29. package/core/built/admin/assets/activitypub/{moderation-KCCO7L2J.js → moderation-D1Yapsod.js} +9 -9
  30. package/core/built/admin/assets/activitypub/{note-BX_Bz5A0.js → note-C5zjqq5C.js} +8 -8
  31. package/core/built/admin/assets/activitypub/{notifications-BQOYGvRv.js → notifications-B9p8eCHD.js} +9 -9
  32. package/core/built/admin/assets/activitypub/{preferences-6h43G9_q.js → preferences-BCqUdC3v.js} +9 -9
  33. package/core/built/admin/assets/activitypub/{profile-BMN9VVZ2.js → profile-BRCWoFem.js} +178 -178
  34. package/core/built/admin/assets/activitypub/{reply-ZNo04xsO.js → reply-DYK5vVqb.js} +2 -2
  35. package/core/built/admin/assets/activitypub/{routes-3B9mUQ7i.js → routes-BSXT992i.js} +81 -84
  36. package/core/built/admin/assets/activitypub/{separator-D9taVCCa.js → separator-BbQqTm-p.js} +3 -3
  37. package/core/built/admin/assets/activitypub/{settings-71gCm3SO.js → settings-DQDkr06X.js} +6 -6
  38. package/core/built/admin/assets/activitypub/{step-1-BsGGnBuT.js → step-1-B-YfsVeO.js} +5 -5
  39. package/core/built/admin/assets/activitypub/{step-2-teosIS0k.js → step-2-DtPQzlTH.js} +6 -6
  40. package/core/built/admin/assets/activitypub/{step-3-BjD5TokC.js → step-3-D6BFMXL_.js} +10 -10
  41. package/core/built/admin/assets/activitypub/{tabs-Cvwl1wXU.js → tabs-GpNecvfB.js} +6 -6
  42. package/core/built/admin/assets/activitypub/{topic-filter-C3LZJc2X.js → topic-filter-BPDeQQys.js} +3 -3
  43. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-Dp9f0gov.js → use-activity-pub-queries-BoVOxXkV.js} +2 -2
  44. package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-C4RI0sa9.js → use-navigate-with-base-path-Bmk5h4lW.js} +220 -220
  45. package/core/built/admin/assets/activitypub/{use-reply-chain-data-C2oNDMUH.js → use-reply-chain-data-wVBX7swM.js} +5 -5
  46. package/core/built/admin/assets/activitypub/{x-Dxm6pV9X.js → x-DbQZOLiI.js} +2 -2
  47. package/core/built/admin/assets/{add-integration-modal--YpTq3c0.js → add-integration-modal-BC0jExDZ.js} +1 -1
  48. package/core/built/admin/assets/{add-newsletter-modal-C8Ix2hol.js → add-newsletter-modal-DchTkA7l.js} +1 -1
  49. package/core/built/admin/assets/{add-recommendation-modal-CScuNFz4.js → add-recommendation-modal-DMvaJZKp.js} +1 -1
  50. package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +1 -0
  51. package/core/built/admin/assets/{analytics-Cnji45H5.js → analytics-No1DDY3A.js} +1 -1
  52. package/core/built/admin/assets/{announcement-bar-modal-BTHiy1ZO.js → announcement-bar-modal-DM3cYOpo.js} +1 -1
  53. package/core/built/admin/assets/{api-keys-Boy5pLbD.js → api-keys-BLm2NDBh.js} +1 -1
  54. package/core/built/admin/assets/audience-9mwU5TW6.js +1 -0
  55. package/core/built/admin/assets/{automations-BWpRCZ_f.js → automations-DpUNUdcZ.js} +1 -1
  56. package/core/built/admin/assets/{bluesky-sharing-DHOpQxff.js → bluesky-sharing-Cjrv_f62.js} +1 -1
  57. package/core/built/admin/assets/{box-By_EgGtm.js → box-CdfQAP_F.js} +1 -1
  58. package/core/built/admin/assets/{brand-icon-09YqFZjV.js → brand-icon-CrIGOA3Z.js} +1 -1
  59. package/core/built/admin/assets/{breadcrumb-CcFr_ltt.js → breadcrumb-DLiqCyFc.js} +1 -1
  60. package/core/built/admin/assets/{button-BshWPHvn.js → button-B2jcu6bk.js} +1 -1
  61. package/core/built/admin/assets/{card-CiiE7QfM.js → card--VNbfgo3.js} +1 -1
  62. package/core/built/admin/assets/{chart-BnXyfH11.js → chart-BrhA41Zi.js} +1 -1
  63. package/core/built/admin/assets/chart-helpers-Fo3bUL3K.js +1 -0
  64. package/core/built/admin/assets/{checkbox-Dwwr_3Py.js → checkbox-BfKktMph.js} +1 -1
  65. package/core/built/admin/assets/{chunk.524.91ecb33e6afc3bd8949b.js → chunk.524.60ca4692d91c6022042f.js} +4 -4
  66. package/core/built/admin/assets/{chunk.582.512549909741883f960c.js → chunk.582.525c7617c2d9e94c940a.js} +6 -6
  67. package/core/built/admin/assets/{chunk.16.f76a6314082d813d67e0.js → chunk.896.435fe0637944d4a69810.js} +3 -3
  68. package/core/built/admin/assets/{code-editor-view-BwIdPy_k.js → code-editor-view-CYVu4EW6.js} +1 -1
  69. package/core/built/admin/assets/{code-editor-view-CtkF82Fv.js → code-editor-view-DCQJxsfU.js} +1 -1
  70. package/core/built/admin/assets/{color-picker-Bmo1r9Fc.js → color-picker-BTY_NdEm.js} +1 -1
  71. package/core/built/admin/assets/{color-picker-field-DywowPhF.js → color-picker-field-CPlkzOH0.js} +1 -1
  72. package/core/built/admin/assets/{combobox-C4QCh6Yw.js → combobox-DrMtv_4P.js} +1 -1
  73. package/core/built/admin/assets/command-ZdgYJpoI.js +1 -0
  74. package/core/built/admin/assets/comments-kidj60-h.js +1 -0
  75. package/core/built/admin/assets/{confirmation-context-CLFHhyyH.js → confirmation-context-BhbmQQEl.js} +1 -1
  76. package/core/built/admin/assets/{container-LiZf3XdY.js → container-BuQG1wuP.js} +1 -1
  77. package/core/built/admin/assets/{content-api-modal-BtpNI92-.js → content-api-modal-CyQEC3fr.js} +1 -1
  78. package/core/built/admin/assets/{copy-field-CglgRZyG.js → copy-field-B3M1s78L.js} +1 -1
  79. package/core/built/admin/assets/{custom-field-type-option-D0PXoSnP.js → custom-field-type-option-CsGIZk1J.js} +1 -1
  80. package/core/built/admin/assets/{custom-integration-modal-DCIHKN33.js → custom-integration-modal-BY37O_9X.js} +1 -1
  81. package/core/built/admin/assets/{data-list-CupF7bjM.js → data-list-CA2jYGPq.js} +1 -1
  82. package/core/built/admin/assets/{date-range-select-JPwM0yUj.js → date-range-select-DD2RKkgB.js} +1 -1
  83. package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +22 -0
  84. package/core/built/admin/assets/{detail-page-D8lx2SdP.js → detail-page-Dnv-CdR1.js} +1 -1
  85. package/core/built/admin/assets/dialog-B44iRwlc.js +1 -0
  86. package/core/built/admin/assets/{dirty-confirm-dialog-CmARpZQS.js → dirty-confirm-dialog-BxgQgQ6H.js} +1 -1
  87. package/core/built/admin/assets/{disabled-sources-indicator-BTJ2ClQu.js → disabled-sources-indicator-Cvy47bE2.js} +1 -1
  88. package/core/built/admin/assets/{dist-DzcfoDJ3.js → dist-8YJUlf3B.js} +1 -1
  89. package/core/built/admin/assets/{dist-Cb5RSHSg.js → dist-B3keLvjj.js} +1 -1
  90. package/core/built/admin/assets/{dist-QLZuGq7o2.js → dist-Cy8tfrp_.js} +1 -1
  91. package/core/built/admin/assets/{dist-BT_Z-eCn.js → dist-Dgt2cr6a.js} +1 -1
  92. package/core/built/admin/assets/{domain-D0SAtc-T.js → domain-Ca6KhoQt.js} +1 -1
  93. package/core/built/admin/assets/{dropzone-CaPOeDRZ.js → dropzone-Bb-5zQTk.js} +1 -1
  94. package/core/built/admin/assets/edit-profile-BdupLlus.js +1 -0
  95. package/core/built/admin/assets/{editor-COXn4Atu.js → editor-BP883Lpn.js} +3 -3
  96. package/core/built/admin/assets/{embed-signup-form-modal-BFTPf04i.js → embed-signup-form-modal-DeBMLxlL.js} +1 -1
  97. package/core/built/admin/assets/{empty-indicator-WB1WgT9h.js → empty-indicator-D9QdpuNX.js} +1 -1
  98. package/core/built/admin/assets/{explore-BsNYrubV.js → explore-FB-CimXw.js} +1 -1
  99. package/core/built/admin/assets/{feed-8RR1O1bK.js → feed-C1GxsmI2.js} +1 -1
  100. package/core/built/admin/assets/{field-LmNk7wZh.js → field-Cj8lmDOo.js} +1 -1
  101. package/core/built/admin/assets/{filter-sources-4C0kF1NU.js → filter-sources-D5n5bFA2.js} +1 -1
  102. package/core/built/admin/assets/{filters-CV5jCb9n.js → filters-DJEzkMUK.js} +1 -1
  103. package/core/built/admin/assets/{filters-Dmr0PoqU.js → filters-MZXvdH5D.js} +1 -1
  104. package/core/built/admin/assets/{first-promoter-modal-OCzSaL3B.js → first-promoter-modal-CGTGLCr5.js} +1 -1
  105. package/core/built/admin/assets/{ghost-403cb39badad3df411fc0ba27d220047.js → ghost-3894eedcc6968f658578c0abfe942aab.js} +2 -2
  106. package/core/built/admin/assets/{gift-link-modal-0mzjn5oF.js → gift-link-modal-B6v0tnRN.js} +1 -1
  107. package/core/built/admin/assets/{grid-CUciBfjf.js → grid-Dj29PY05.js} +1 -1
  108. package/core/built/admin/assets/growth-BGAJD1rs.js +3 -0
  109. package/core/built/admin/assets/growth-D6mhcx3q.js +1 -0
  110. package/core/built/admin/assets/{heading-CRZZ2FUe.js → heading-Dh5SFVjL.js} +1 -1
  111. package/core/built/admin/assets/history-modal-iKwUY3XR.js +1 -0
  112. package/core/built/admin/assets/html-field-C8qVGGC1.js +1 -0
  113. package/core/built/admin/assets/{images-C0XvUYMb.js → images-h8VZEQ9k.js} +1 -1
  114. package/core/built/admin/assets/{inbox-DZXDUt4s.js → inbox-BYd1M06Q.js} +13 -13
  115. package/core/built/admin/assets/index-DjQe-e5s.js +4 -0
  116. package/core/built/admin/assets/{index.es-BMUCj9TR.js → index.es-VihPwfeH.js} +1 -1
  117. package/core/built/admin/assets/{inline-Cw-gigDW.js → inline-BWSs_VzR.js} +1 -1
  118. package/core/built/admin/assets/{input-UHrKuvBT.js → input--W-rCTrX.js} +1 -1
  119. package/core/built/admin/assets/{input-group-BPAxETpQ.js → input-group-D8sCXRF5.js} +1 -1
  120. package/core/built/admin/assets/{input-surface-BdOe4KzO.js → input-surface-BsrIZeSF.js} +1 -1
  121. package/core/built/admin/assets/{integration-header-B4DyXV06.js → integration-header-Bm6IDb1A.js} +1 -1
  122. package/core/built/admin/assets/{invite-user-modal-BrAkxEO3.js → invite-user-modal-MdImLCaJ.js} +1 -1
  123. package/core/built/admin/assets/{koenig-lexical-60O_8aiO.js → koenig-lexical-_-IsVI7E.js} +1 -1
  124. package/core/built/admin/assets/koenig-loader-DYZpm6El.js +2 -0
  125. package/core/built/admin/assets/{kpi-card-fUHZlzBX.js → kpi-card-DW_2y3ug.js} +1 -1
  126. package/core/built/admin/assets/kpi-card-DZQk_0W8.js +1 -0
  127. package/core/built/admin/assets/kpi-helpers-PueTKuA6.js +1 -0
  128. package/core/built/admin/assets/kpi-tabs-CojDs-Qz.js +1 -0
  129. package/core/built/admin/assets/{label-VIG969IC.js → label-DjbT91GM.js} +1 -1
  130. package/core/built/admin/assets/{label-picker-BBWykUeA.js → label-picker-cB51xl1r.js} +1 -1
  131. package/core/built/admin/assets/layout-c_B_1hax.js +1 -0
  132. package/core/built/admin/assets/{link-helpers-Dtq9fxQg.js → link-helpers-MVDdnFCY.js} +1 -1
  133. package/core/built/admin/assets/{list-page-CdsnQR9N.js → list-page-BucXJ0hi.js} +1 -1
  134. package/core/built/admin/assets/{loading-indicator-SFTqyYVE.js → loading-indicator-BpFWTVgh.js} +1 -1
  135. package/core/built/admin/assets/{member-custom-fields-gOv6Hoh3.js → member-custom-fields-qs0dIkxM.js} +1 -1
  136. package/core/built/admin/assets/member-detail-B6uec2a6.js +1 -0
  137. package/core/built/admin/assets/members-BQF3CqhI.js +2 -0
  138. package/core/built/admin/assets/{modal-page-BMTzFHVO.js → modal-page-xCivTMvI.js} +1 -1
  139. package/core/built/admin/assets/{moderation-DzzPLDyl.js → moderation-DY4wF1Q3.js} +1 -1
  140. package/core/built/admin/assets/{multi-select-combobox-CGohnsxJ.js → multi-select-combobox-B6LiRzDO.js} +1 -1
  141. package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +1 -0
  142. package/core/built/admin/assets/newsletter-CyQA7m3m.js +1 -0
  143. package/core/built/admin/assets/{newsletter-detail-modal-DQU3r4g2.js → newsletter-detail-modal-BDrptuxn.js} +1 -1
  144. package/core/built/admin/assets/{newsletter-emails-D5ql1rud.js → newsletter-emails-CFeLlt4z.js} +1 -1
  145. package/core/built/admin/assets/newsletters-DXuNmC42.js +1 -0
  146. package/core/built/admin/assets/{note-i8k6pjaw.js → note-Cgo2LCJ6.js} +1 -1
  147. package/core/built/admin/assets/notifications-BC50AMku.js +1 -0
  148. package/core/built/admin/assets/{offers-container-modal-BWfjSdyg.js → offers-container-modal-DWycIMzT.js} +1 -1
  149. package/core/built/admin/assets/{onboarding-route-Cu_ROWPn.js → onboarding-route-Dmc4IPm3.js} +1 -1
  150. package/core/built/admin/assets/overview-CVbFtn-k.js +1 -0
  151. package/core/built/admin/assets/overview-YKj7SZaK.js +1 -0
  152. package/core/built/admin/assets/{page-header-Cfds8pCg.js → page-header-BR_nbIPp.js} +1 -1
  153. package/core/built/admin/assets/{pintura-modal-TDTrJ89x.js → pintura-modal-BR9hEvS8.js} +1 -1
  154. package/core/built/admin/assets/popover-CCy0h5rq.js +1 -0
  155. package/core/built/admin/assets/{portal-frame-DjBF-uy2.js → portal-frame-dc4XKr0e.js} +1 -1
  156. package/core/built/admin/assets/portal-modal-BvvdF_B9.js +1 -0
  157. package/core/built/admin/assets/{post-analytics-Buz1COoY.js → post-analytics-DX470WRG.js} +1 -1
  158. package/core/built/admin/assets/{post-analytics-header-B-P0eFnT.js → post-analytics-header-CXpJmdDv.js} +1 -1
  159. package/core/built/admin/assets/{post-analytics-provider-Cu3jNeFh.js → post-analytics-provider-CLfKGWZy.js} +1 -1
  160. package/core/built/admin/assets/{post-share-modal-P1_M6o-H.js → post-share-modal-Cl0NXRJr.js} +1 -1
  161. package/core/built/admin/assets/{preferences-Cq1aOiLy.js → preferences-DO0UUWFp.js} +2 -2
  162. package/core/built/admin/assets/{preview-chrome-BOVO6_PE.js → preview-chrome-BljUBlOk.js} +1 -1
  163. package/core/built/admin/assets/{preview-modal-MlTjP8UU.js → preview-modal-C-BL-F9k.js} +1 -1
  164. package/core/built/admin/assets/profile-CxT-0l4d.js +1 -0
  165. package/core/built/admin/assets/radio-group-Bgz60i81.js +1 -0
  166. package/core/built/admin/assets/{router-provider-CJC58zTy.js → router-provider-B0gZyiGJ.js} +1 -1
  167. package/core/built/admin/assets/{schemas-CRsC53ls.js → schemas-4UPic-oD.js} +2 -2
  168. package/core/built/admin/assets/select-DuDUFdPq.js +1 -0
  169. package/core/built/admin/assets/{settings-B5hJKZ5C.js → settings-BjCii7BM.js} +1 -1
  170. package/core/built/admin/assets/settings-b8kNzZSQ.js +4 -0
  171. package/core/built/admin/assets/{settings-breadcrumbs-sLDNedBW.js → settings-breadcrumbs-CyP_9zTo.js} +1 -1
  172. package/core/built/admin/assets/{shade-app-F7WxOyfk-nufqw1hd.js → shade-app-CcS3f8MZ-vwURazk8.js} +2 -2
  173. package/core/built/admin/assets/{share-modal-CLBfj377.js → share-modal-BV70mr9k.js} +1 -1
  174. package/core/built/admin/assets/{skeleton-D7hwjdry.js → skeleton-Ce9GgUpf.js} +1 -1
  175. package/core/built/admin/assets/{slack-modal-Bt9I_tEo.js → slack-modal-Be2_KK-z.js} +1 -1
  176. package/core/built/admin/assets/sort-button-Cc-Z96sA.js +1 -0
  177. package/core/built/admin/assets/{source-icon-BPsD5ees.js → source-icon-v6PUnmyM.js} +1 -1
  178. package/core/built/admin/assets/{stack-CIfOAAVf.js → stack-B2eszIiq.js} +1 -1
  179. package/core/built/admin/assets/{stats-B_YkF7bp.js → stats-C4reGh9O.js} +1 -1
  180. package/core/built/admin/assets/{stats-view-BOCp9RG8.js → stats-view-DP9pQ62k.js} +1 -1
  181. package/core/built/admin/assets/{step-1-CMta6YFO.js → step-1-DSRp2vBj.js} +1 -1
  182. package/core/built/admin/assets/{step-2-BG04nh5q.js → step-2-COqv2g0u.js} +1 -1
  183. package/core/built/admin/assets/{step-3-ZnMaVIPj.js → step-3-6W1NMVQc.js} +2 -2
  184. package/core/built/admin/assets/{stripe-button-DDoUKN5a.js → stripe-button-D2K42Uty.js} +1 -1
  185. package/core/built/admin/assets/{stripe-connect-modal-BeSukI8q.js → stripe-connect-modal-Z-UT_BGc.js} +1 -1
  186. package/core/built/admin/assets/{switch-C9djUIMO.js → switch-CI4gLWqb.js} +1 -1
  187. package/core/built/admin/assets/{table-CrqQtkR2.js → table-B2pIKjVh.js} +1 -1
  188. package/core/built/admin/assets/{tabs-CGisSbWq.js → tabs-SaW_UT-Q.js} +1 -1
  189. package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +2 -0
  190. package/core/built/admin/assets/tags-BLaXBwnb.js +1 -0
  191. package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +1 -0
  192. package/core/built/admin/assets/{text-DU9Hv33_.js → text-BQDSDsKs.js} +1 -1
  193. package/core/built/admin/assets/{textarea-d9qEkMOP.js → textarea-DJ1Q5V39.js} +1 -1
  194. package/core/built/admin/assets/{tier-detail-modal-CxRwgTPR.js → tier-detail-modal-BCFpWwUS.js} +1 -1
  195. package/core/built/admin/assets/{toggle-group-CtKZURJd.js → toggle-group-EM9WpESu.js} +1 -1
  196. package/core/built/admin/assets/{topic-filter-D8JT8Tq1.js → topic-filter-DY4gNHn2.js} +1 -1
  197. package/core/built/admin/assets/{transistor-modal-6PUf0idx.js → transistor-modal-CpyPG-MH.js} +1 -1
  198. package/core/built/admin/assets/trend-badge-ByMkuAuH.js +1 -0
  199. package/core/built/admin/assets/{unsplash-modal-BPuDBPja.js → unsplash-modal-Cv2Q6GtY.js} +1 -1
  200. package/core/built/admin/assets/{use-analytics-data--opuzwcz.js → use-analytics-data-CHvjMYuT.js} +1 -1
  201. package/core/built/admin/assets/use-check-theme-limit-error-Qv4SVXqw.js +2 -0
  202. package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +1 -0
  203. package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +1 -0
  204. package/core/built/admin/assets/{use-limiter-TDSNiHPB.js → use-limiter-cwJvsNBo.js} +1 -1
  205. package/core/built/admin/assets/{use-navigate-with-base-path-CK989MlC.js → use-navigate-with-base-path-BHPA228f.js} +1 -1
  206. package/core/built/admin/assets/{use-pintura-config-CnCGrDFk.js → use-pintura-config-CzTfnufz.js} +1 -1
  207. package/core/built/admin/assets/{use-pintura-editor-ntphvtUd.js → use-pintura-editor-BDBXWUYq.js} +1 -1
  208. package/core/built/admin/assets/{use-post-referrers-CbXLFIex.js → use-post-referrers-DbY-mzci.js} +1 -1
  209. package/core/built/admin/assets/{use-reply-chain-data-JYFKSO83.js → use-reply-chain-data-BeQwz8oF.js} +1 -1
  210. package/core/built/admin/assets/{use-router-history-entry-BwfQS2mq.js → use-router-history-entry-Bg6BhBQQ.js} +1 -1
  211. package/core/built/admin/assets/{use-setting-group-DQxV42-O.js → use-setting-group-VM7FQwMY.js} +1 -1
  212. package/core/built/admin/assets/{use-settings-navigation-BBzro2-V.js → use-settings-navigation-BbrSUVcf.js} +1 -1
  213. package/core/built/admin/assets/{use-simple-pagination-B2TdNX67.js → use-simple-pagination-Dum6Irbs.js} +1 -1
  214. package/core/built/admin/assets/{use-staff-users-QrR9voeC.js → use-staff-users-cZg4ugOF.js} +1 -1
  215. package/core/built/admin/assets/{use-tinybird-query-D2Aeoty5.js → use-tinybird-query-B1IMyAcv.js} +1 -1
  216. package/core/built/admin/assets/{use-tinybird-token-DBuwMmln.js → use-tinybird-token-Bow0Zwn8.js} +1 -1
  217. package/core/built/admin/assets/{use-unsaved-changes-guard-DpYJcj1d.js → use-unsaved-changes-guard-BAy0RANR.js} +1 -1
  218. package/core/built/admin/assets/{use-url-input-WnIqckzS.js → use-url-input-Df5QzNa7.js} +1 -1
  219. package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +1 -0
  220. package/core/built/admin/assets/{virtual-list-D3MfPNOh.js → virtual-list-DEx8WgiO.js} +1 -1
  221. package/core/built/admin/assets/web-CKcXAu86.js +1 -0
  222. package/core/built/admin/assets/web-DR4TqM3E.js +2 -0
  223. package/core/built/admin/assets/{zapier-modal-BO6PS_Vd.js → zapier-modal-Cw3BTBUu.js} +1 -1
  224. package/core/built/admin/index.html +24 -25
  225. package/core/frontend/services/rendering/renderer.js +15 -0
  226. package/core/server/adapters/storage/LocalStorageBase.js +8 -5
  227. package/core/server/api/endpoints/db.js +19 -5
  228. package/core/server/api/endpoints/index.js +4 -0
  229. package/core/server/api/endpoints/member-custom-fields.js +4 -14
  230. package/core/server/api/endpoints/posts.js +1 -0
  231. package/core/server/api/endpoints/tiers-checkout-config.js +59 -0
  232. package/core/server/api/endpoints/utils/serializers/output/config.js +1 -0
  233. package/core/server/api/endpoints/utils/serializers/output/index.js +4 -0
  234. package/core/server/api/endpoints/utils/serializers/output/tiers-checkout-config.js +12 -0
  235. package/core/server/data/exporter/table-lists.js +3 -0
  236. package/core/server/data/importer/content-files.js +57 -0
  237. package/core/server/data/importer/handlers/image.js +2 -2
  238. package/core/server/data/importer/import-archive.js +131 -0
  239. package/core/server/data/importer/import-manager.js +18 -180
  240. package/core/server/data/importer/importers/content-file-importer.js +66 -4
  241. package/core/server/data/migrations/versions/6.61/2026-08-19-14-30-20-add-written-by-to-members-custom-field-values.js +42 -0
  242. package/core/server/data/migrations/versions/6.61/2026-08-19-14-38-53-add-members-custom-field-bindings-table.js +26 -0
  243. package/core/server/data/migrations/versions/6.61/2026-08-19-14-48-06-add-tier-checkout-field-tables.js +35 -0
  244. package/core/server/data/schema/schema.js +90 -0
  245. package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -3
  246. package/core/server/data/seeders/importers/automation-actions-importer.js +3 -3
  247. package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -3
  248. package/core/server/data/seeders/importers/automation-runs-importer.js +5 -5
  249. package/core/server/data/seeders/importers/automations-importer.js +3 -3
  250. package/core/server/data/seeders/importers/comment-reports-importer.js +1 -1
  251. package/core/server/data/seeders/importers/comments-importer.js +1 -1
  252. package/core/server/data/seeders/importers/email-batches-importer.js +1 -1
  253. package/core/server/data/seeders/importers/email-recipients-importer.js +1 -1
  254. package/core/server/data/seeders/importers/emails-importer.js +1 -1
  255. package/core/server/data/seeders/importers/labels-importer.js +1 -1
  256. package/core/server/data/seeders/importers/members-click-events-importer.js +1 -1
  257. package/core/server/data/seeders/importers/members-created-events-importer.js +1 -1
  258. package/core/server/data/seeders/importers/members-feedback-importer.js +1 -1
  259. package/core/server/data/seeders/importers/members-importer.js +1 -1
  260. package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
  261. package/core/server/data/seeders/importers/members-products-importer.js +1 -1
  262. package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
  263. package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
  264. package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
  265. package/core/server/data/seeders/importers/members-subscribe-events-importer.js +1 -1
  266. package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
  267. package/core/server/data/seeders/importers/offer-redemptions-importer.js +1 -1
  268. package/core/server/data/seeders/importers/offers-importer.js +1 -1
  269. package/core/server/data/seeders/importers/posts-importer.js +1 -1
  270. package/core/server/data/seeders/importers/table-importer.js +2 -2
  271. package/core/server/data/seeders/importers/tags-importer.js +1 -1
  272. package/core/server/data/seeders/importers/users-importer.js +1 -1
  273. package/core/server/data/seeders/importers/web-mentions-importer.js +1 -1
  274. package/core/server/data/seeders/utils/random.js +3 -3
  275. package/core/server/ghost-server.js +3 -2
  276. package/core/server/lib/db-types/boolean.js +18 -0
  277. package/core/server/lib/promise-pool.js +31 -0
  278. package/core/server/services/automations/database-automations-repository.js +26 -26
  279. package/core/server/services/automations/poll.js +1 -0
  280. package/core/server/services/content-import/import/assets.js +74 -0
  281. package/core/server/services/content-import/import/importer.js +57 -14
  282. package/core/server/services/content-import/import/post-data.js +1 -0
  283. package/core/server/services/content-import/import/post-repository.js +78 -0
  284. package/core/server/services/content-import/import/relations.js +149 -0
  285. package/core/server/services/content-import/import/row.js +11 -0
  286. package/core/server/services/content-import/import/schema.js +29 -0
  287. package/core/server/services/content-import/import/source.js +125 -0
  288. package/core/server/services/content-import/index.js +6 -28
  289. package/core/server/services/email-analytics/email-analytics-service.js +8 -1
  290. package/core/server/services/email-analytics/lib/queries.js +7 -1
  291. package/core/server/services/gift-links/schema.js +3 -3
  292. package/core/server/services/gifts/CONTEXT.md +1 -1
  293. package/core/server/services/gifts/constants.js +1 -1
  294. package/core/server/services/gifts/gift-bookshelf-repository.js +2 -2
  295. package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +18 -9
  296. package/core/server/services/gifts/gift-delivery-schema.js +5 -5
  297. package/core/server/services/gifts/gift-delivery-service.js +4 -0
  298. package/core/server/services/gifts/gift-schema.js +10 -10
  299. package/core/server/services/gifts/gift-service.js +4 -2
  300. package/core/server/services/jobs-service/register-job-handlers.js +5 -5
  301. package/core/server/services/lib/mailgun-client.js +44 -8
  302. package/core/server/services/media-inliner/external-media-inliner-job.js +12 -0
  303. package/core/server/services/media-inliner/external-media-inliner.js +103 -29
  304. package/core/server/services/media-inliner/service.js +13 -41
  305. package/core/server/services/media-inliner/types.js +2 -0
  306. package/core/server/services/members/import-export/export/exporter.js +4 -6
  307. package/core/server/services/members/import-export/import/importer.js +21 -5
  308. package/core/server/services/members/import-export/import/stripe-utils.js +6 -1
  309. package/core/server/services/members/import-export/index.js +6 -1
  310. package/core/server/services/members/members-api/members-api.js +5 -0
  311. package/core/server/services/members/members-api/services/member-bread-service.js +19 -1
  312. package/core/server/services/members/members-api/services/payments-service.js +47 -1
  313. package/core/server/services/members-custom-fields/actions.js +27 -1
  314. package/core/server/services/members-custom-fields/bindings-service.js +119 -0
  315. package/core/server/services/members-custom-fields/definitions-service.js +104 -13
  316. package/core/server/services/members-custom-fields/index.js +11 -4
  317. package/core/server/services/members-custom-fields/queries.js +5 -0
  318. package/core/server/services/members-custom-fields/schema.js +37 -6
  319. package/core/server/services/members-custom-fields/values-service.js +15 -3
  320. package/core/server/services/public-config/config.js +7 -0
  321. package/core/server/services/stripe/services/checkout/allowed-countries.js +267 -0
  322. package/core/server/services/stripe/services/checkout/completed-session.js +68 -0
  323. package/core/server/services/stripe/services/checkout/field-ports.js +34 -0
  324. package/core/server/services/stripe/services/checkout/session-options.js +97 -0
  325. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +49 -0
  326. package/core/server/services/stripe/stripe-api.js +29 -0
  327. package/core/server/services/stripe/stripe-service.js +7 -0
  328. package/core/server/services/tier-checkout-config/codec.js +95 -0
  329. package/core/server/services/tier-checkout-config/destinations.js +17 -0
  330. package/core/server/services/tier-checkout-config/index.js +26 -0
  331. package/core/server/services/tier-checkout-config/models.js +57 -0
  332. package/core/server/services/tier-checkout-config/queries.js +80 -0
  333. package/core/server/services/tier-checkout-config/schema.js +33 -0
  334. package/core/server/services/tier-checkout-config/serializers.js +122 -0
  335. package/core/server/services/tier-checkout-config/service.js +303 -0
  336. package/core/server/services/tiers/service.js +21 -0
  337. package/core/server/web/api/endpoints/admin/routes.js +24 -13
  338. package/core/server/web/api/middleware/upload.js +2 -2
  339. package/core/server/web/parent/app.js +5 -0
  340. package/core/server/web/parent/middleware/filter-query-parameters.js +94 -0
  341. package/core/server/web/parent/middleware/index.js +1 -0
  342. package/core/server/web/query-parameter-policy/index.js +9 -0
  343. package/core/server/web/query-parameter-policy/policy.json +58 -0
  344. package/core/server/web/query-parameter-policy/schema.js +38 -0
  345. package/core/shared/config/defaults.json +3 -0
  346. package/core/shared/config/env/config.testing-mysql.json +3 -0
  347. package/core/shared/config/env/config.testing.json +3 -0
  348. package/core/shared/config/overrides.json +4 -2
  349. package/core/shared/labs.js +5 -14
  350. package/package.json +12 -13
  351. package/pnpm-lock.yaml +44 -46
  352. package/pnpm-workspace.yaml +6 -6
  353. package/tsconfig.json +2 -3
  354. package/core/built/admin/assets/activitypub/layout-CXajhRsL.js +0 -1
  355. package/core/built/admin/assets/alert-dialog-wEbVxANM.js +0 -1
  356. package/core/built/admin/assets/app-utils-D2Wp7ZSS.js +0 -1
  357. package/core/built/admin/assets/audience-DTLMXAFH.js +0 -1
  358. package/core/built/admin/assets/chart-helpers-B3OqQ6Ez.js +0 -1
  359. package/core/built/admin/assets/command-DcLbSifC.js +0 -1
  360. package/core/built/admin/assets/comments-Bxy_3WyE.js +0 -1
  361. package/core/built/admin/assets/design-and-theme-modal-CZ-PkW7o.js +0 -23
  362. package/core/built/admin/assets/dialog-Db7c164G.js +0 -1
  363. package/core/built/admin/assets/edit-profile-By7v5XW5.js +0 -1
  364. package/core/built/admin/assets/growth-55vWlGT_.js +0 -1
  365. package/core/built/admin/assets/growth-DA5AluYw.js +0 -3
  366. package/core/built/admin/assets/history-modal-G1s5hHz7.js +0 -1
  367. package/core/built/admin/assets/html-field-BegxsWpw.js +0 -1
  368. package/core/built/admin/assets/index-CDP9tIOk.js +0 -4
  369. package/core/built/admin/assets/koenig-loader-DwVY_V-H.js +0 -1
  370. package/core/built/admin/assets/kpi-card-LERqWBoY.js +0 -1
  371. package/core/built/admin/assets/kpi-helpers-DB3ouhYF.js +0 -1
  372. package/core/built/admin/assets/kpi-tabs-BGvtMD8S.js +0 -1
  373. package/core/built/admin/assets/layout-Di5K7doM.js +0 -1
  374. package/core/built/admin/assets/member-detail-D7mZDDp3.js +0 -1
  375. package/core/built/admin/assets/member-route-0Aa52Ntz.js +0 -1
  376. package/core/built/admin/assets/members-DvZG9t69.js +0 -2
  377. package/core/built/admin/assets/navigation-modal-CI8Bx5vn.js +0 -1
  378. package/core/built/admin/assets/newsletter-Bg3cq5U-.js +0 -1
  379. package/core/built/admin/assets/newsletters-DMDfZbMS.js +0 -1
  380. package/core/built/admin/assets/notifications-psA_2Ph4.js +0 -1
  381. package/core/built/admin/assets/overview-C-6xOLaf.js +0 -1
  382. package/core/built/admin/assets/overview-DaLhpRrC.js +0 -1
  383. package/core/built/admin/assets/popover-CFgqFDvC.js +0 -1
  384. package/core/built/admin/assets/portal-modal-Cf0pBAk8.js +0 -1
  385. package/core/built/admin/assets/profile-D6dHYf2o.js +0 -1
  386. package/core/built/admin/assets/radio-group-CjtmsCPl.js +0 -1
  387. package/core/built/admin/assets/select-D_RQaXgt.js +0 -1
  388. package/core/built/admin/assets/settings-DIo_F-MF.js +0 -4
  389. package/core/built/admin/assets/sort-button-DMBXF7tP.js +0 -1
  390. package/core/built/admin/assets/tag-detail-2a3BfzTW.js +0 -2
  391. package/core/built/admin/assets/tags-Dp2_8UzL.js +0 -1
  392. package/core/built/admin/assets/testimonials-modal-C9nrypNo.js +0 -1
  393. package/core/built/admin/assets/use-check-theme-limit-error-C9-QhGdV.js +0 -1
  394. package/core/built/admin/assets/use-filter-params-CSpAvgD2.js +0 -1
  395. package/core/built/admin/assets/use-growth-stats-BOmoPDHJ.js +0 -1
  396. package/core/built/admin/assets/user-detail-modal-59mBJqxN.js +0 -1
  397. package/core/built/admin/assets/web-DUuX7nE2.js +0 -2
  398. package/core/built/admin/assets/web-N8ucs2aG.js +0 -1
  399. /package/core/server/lib/{db-date.js → db-types/date.js} +0 -0
@@ -165,8 +165,8 @@ module.exports = class MailgunClient {
165
165
  * @param {Object} mailgunOptions
166
166
  * @param {Function} batchHandler
167
167
  * @param {Object} options
168
- * @param {number} [options.maxEvents] Not a strict maximum. We stop fetching after we reached the maximum AND received at least one event after begin (not equal) to prevent deadlocks.
169
- * @returns {Promise<void>}
168
+ * @param {number} [options.maxEvents] Per-domain soft maximum. We stop fetching a domain after we reached the maximum AND received at least one event after begin (not equal) to prevent deadlocks.
169
+ * @returns {Promise<{safeCursor?: Date} | void>}
170
170
  */
171
171
  async fetchEvents(mailgunOptions, batchHandler, { maxEvents = Infinity } = {}) {
172
172
  const mailgunInstance = this.getInstance();
@@ -184,12 +184,33 @@ module.exports = class MailgunClient {
184
184
  // Determine which domains to fetch from
185
185
  const domains = this.#getDomainsToFetch(mailgunConfig);
186
186
 
187
+ const cappedDomainCursors = [];
188
+
187
189
  // Fetch events from each domain
188
190
  for (const domain of domains) {
189
- await this.#fetchEventsFromDomain(domain, mailgunInstance, mailgunOptions, batchHandler, {
190
- maxEvents,
191
- });
191
+ const result = await this.#fetchEventsFromDomain(
192
+ domain,
193
+ mailgunInstance,
194
+ mailgunOptions,
195
+ batchHandler,
196
+ { maxEvents },
197
+ );
198
+
199
+ if (
200
+ result.capped &&
201
+ result.lastEventTimestamp &&
202
+ Number.isFinite(result.lastEventTimestamp.getTime())
203
+ ) {
204
+ cappedDomainCursors.push(result.lastEventTimestamp);
205
+ }
192
206
  }
207
+
208
+ const safeCursor = cappedDomainCursors.reduce(
209
+ (earliest, cursor) => (!earliest || cursor < earliest ? cursor : earliest),
210
+ undefined,
211
+ );
212
+
213
+ return { safeCursor };
193
214
  }
194
215
 
195
216
  /**
@@ -220,7 +241,7 @@ module.exports = class MailgunClient {
220
241
  * @param {Function} batchHandler
221
242
  * @param {Object} options
222
243
  * @param {number} options.maxEvents
223
- * @returns {Promise<void>}
244
+ * @returns {Promise<{capped: boolean, eventCount: number, lastEventTimestamp?: Date}>}
224
245
  */
225
246
  async #fetchEventsFromDomain(
226
247
  domain,
@@ -235,6 +256,9 @@ module.exports = class MailgunClient {
235
256
 
236
257
  let batchCount = 0;
237
258
  let totalBatchTime = 0;
259
+ let eventCount = 0;
260
+ let lastEventTimestamp;
261
+ let capped = false;
238
262
 
239
263
  try {
240
264
  let page = await this.getEventsFromMailgun(mailgunInstance, domain, mailgunOptions);
@@ -248,7 +272,6 @@ module.exports = class MailgunClient {
248
272
  `[MailgunClient fetchEventsFromDomain ${domain}]: finished fetching first page with ${events.length} events`,
249
273
  );
250
274
 
251
- let eventCount = 0;
252
275
  const beginTimestamp = mailgunOptions.begin
253
276
  ? Math.ceil(mailgunOptions.begin * 1000)
254
277
  : undefined; // ceil here if we have rounding errors
@@ -263,6 +286,13 @@ module.exports = class MailgunClient {
263
286
  totalBatchTime += batchDuration;
264
287
 
265
288
  eventCount += events.length;
289
+ const batchLastEventTimestamp = events[events.length - 1].timestamp;
290
+ if (
291
+ batchLastEventTimestamp &&
292
+ (!lastEventTimestamp || batchLastEventTimestamp > lastEventTimestamp)
293
+ ) {
294
+ lastEventTimestamp = batchLastEventTimestamp;
295
+ }
266
296
 
267
297
  if (
268
298
  eventCount >= maxEvents &&
@@ -270,6 +300,7 @@ module.exports = class MailgunClient {
270
300
  !events[events.length - 1].timestamp ||
271
301
  events[events.length - 1].timestamp.getTime() > beginTimestamp)
272
302
  ) {
303
+ capped = true;
273
304
  break;
274
305
  }
275
306
 
@@ -294,10 +325,15 @@ module.exports = class MailgunClient {
294
325
  const overallEndTime = Date.now();
295
326
  const totalDuration = overallEndTime - overallStartTime;
296
327
  const averageBatchTime = batchCount > 0 ? totalBatchTime / batchCount : 0;
328
+ const cursor = Number.isFinite(lastEventTimestamp?.getTime())
329
+ ? lastEventTimestamp.toISOString()
330
+ : 'none';
297
331
 
298
332
  logging.info(
299
- `[MailgunClient fetchEventsFromDomain ${domain}]: Processed ${batchCount} batches in ${(totalDuration / 1000).toFixed(2)}s. Average batch time: ${(averageBatchTime / 1000).toFixed(2)}s`,
333
+ `[MailgunClient fetchEventsFromDomain ${domain}]: Processed ${eventCount} events in ${batchCount} batches in ${(totalDuration / 1000).toFixed(2)}s. Average batch time: ${(averageBatchTime / 1000).toFixed(2)}s. Status: ${capped ? 'capped' : 'exhausted'}. Cursor: ${cursor}`,
300
334
  );
335
+
336
+ return { capped, eventCount, lastEventTimestamp };
301
337
  } catch (error) {
302
338
  logging.error(`[MailgunClient fetchEventsFromDomain ${domain}]: Error fetching events`);
303
339
  logging.error(error);
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const job_1 = require("../jobs-service/job");
4
+ class ExternalMediaInlinerJob extends job_1.Job {
5
+ static type = 'external-media-inliner';
6
+ domains;
7
+ constructor({ domains }) {
8
+ super();
9
+ this.domains = domains;
10
+ }
11
+ }
12
+ exports.default = ExternalMediaInlinerJob;
@@ -177,6 +177,96 @@ class ExternalMediaInliner {
177
177
  }
178
178
  }
179
179
 
180
+ /**
181
+ * Download and store one external media URL without deciding where it came from
182
+ * or where its replacement belongs. Callers own discovery and replacement.
183
+ *
184
+ * @param {string} sourceUrl
185
+ * @returns {Promise<import('./types').ExternalMediaImportResult>}
186
+ */
187
+ async importUrl(sourceUrl) {
188
+ let response;
189
+ try {
190
+ response = await this.getRemoteMedia(sourceUrl);
191
+ } catch (error) {
192
+ return {
193
+ status: 'failed',
194
+ sourceUrl,
195
+ stage: 'download',
196
+ reason: 'The media file could not be downloaded.',
197
+ error,
198
+ };
199
+ }
200
+
201
+ if (!response) {
202
+ return {
203
+ status: 'failed',
204
+ sourceUrl,
205
+ stage: 'download',
206
+ reason: 'The media file could not be downloaded.',
207
+ };
208
+ }
209
+
210
+ let media;
211
+ try {
212
+ media = await this.extractFileDataFromResponse(sourceUrl, response);
213
+ } catch (error) {
214
+ return {
215
+ status: 'failed',
216
+ sourceUrl,
217
+ stage: 'extract',
218
+ reason: 'The downloaded media file could not be read.',
219
+ error,
220
+ };
221
+ }
222
+
223
+ try {
224
+ const storedUrl = await this.storeMediaLocally(media);
225
+ if (!storedUrl) {
226
+ return {
227
+ status: 'failed',
228
+ sourceUrl,
229
+ stage: 'unsupported',
230
+ reason: 'No configured storage accepts this media file.',
231
+ };
232
+ }
233
+
234
+ return {
235
+ status: 'stored',
236
+ sourceUrl,
237
+ storedUrl,
238
+ };
239
+ } catch (error) {
240
+ return {
241
+ status: 'failed',
242
+ sourceUrl,
243
+ stage: 'storage',
244
+ reason: 'The media file could not be stored in Ghost.',
245
+ error,
246
+ };
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Convert an import result into the replacement URL expected by the existing
252
+ * content and field processing. Expected download and unsupported-file failures
253
+ * return null so the original URL remains unchanged. Results containing an
254
+ * underlying processing error are rethrown so the established per-resource
255
+ * catch boundary continues to log and isolate them.
256
+ *
257
+ * @param {import('./types').ExternalMediaImportResult} result
258
+ * @returns {string|null}
259
+ */
260
+ #replacementUrlFromImportResult(result) {
261
+ if (result.status === 'stored') {
262
+ return result.storedUrl;
263
+ }
264
+ if ('error' in result) {
265
+ throw result.error;
266
+ }
267
+ return null;
268
+ }
269
+
180
270
  static findMatches(content, domain) {
181
271
  // NOTE: the src could end with a quote, bracket, apostrophe, double-backslash, or encoded quote.
182
272
  // Backlashes are added to content as an escape character
@@ -208,22 +298,14 @@ class ExternalMediaInliner {
208
298
  const matches = this.constructor.findMatches(content, domain);
209
299
 
210
300
  for (const src of matches) {
211
- const response = await this.getRemoteMedia(src);
212
-
213
- let media;
214
- if (response) {
215
- media = await this.extractFileDataFromResponse(src, response);
216
- }
217
-
218
- if (media) {
219
- const inlinedSrc = await this.storeMediaLocally(media);
220
-
221
- if (inlinedSrc) {
222
- // NOTE: does not account for duplicate images in content
223
- // in those cases would be processed twice
224
- content = content.replace(src, inlinedSrc);
225
- logging.info(`Inlined media: ${src} -> ${inlinedSrc}`);
226
- }
301
+ const result = await this.importUrl(src);
302
+ const replacementUrl = this.#replacementUrlFromImportResult(result);
303
+
304
+ if (replacementUrl) {
305
+ // NOTE: does not account for duplicate images in content
306
+ // in those cases would be processed twice
307
+ content = content.replace(src, replacementUrl);
308
+ logging.info(`Inlined media: ${src} -> ${replacementUrl}`);
227
309
  }
228
310
  }
229
311
  }
@@ -246,20 +328,12 @@ class ExternalMediaInliner {
246
328
  const src = resourceModel.get(field);
247
329
 
248
330
  if (src && src.startsWith(domain)) {
249
- const response = await this.getRemoteMedia(src);
250
-
251
- let media;
252
- if (response) {
253
- media = await this.extractFileDataFromResponse(src, response);
254
- }
255
-
256
- if (media) {
257
- const inlinedSrc = await this.storeMediaLocally(media);
331
+ const result = await this.importUrl(src);
332
+ const replacementUrl = this.#replacementUrlFromImportResult(result);
258
333
 
259
- if (inlinedSrc) {
260
- updatedFields[field] = inlinedSrc;
261
- logging.info(`Added media to inline: ${src} -> ${inlinedSrc}`);
262
- }
334
+ if (replacementUrl) {
335
+ updatedFields[field] = replacementUrl;
336
+ logging.info(`Added media to inline: ${src} -> ${replacementUrl}`);
263
337
  }
264
338
  }
265
339
  }
@@ -1,10 +1,11 @@
1
+ const errors = require('@tryghost/errors');
2
+
3
+ let instance;
4
+
1
5
  module.exports = {
2
6
  async init() {
3
- const debug = require('@tryghost/debug')('mediaInliner');
4
7
  const MediaInliner = require('./external-media-inliner');
5
- const logging = require('@tryghost/logging');
6
8
  const models = require('../../models');
7
- const jobsService = require('../jobs');
8
9
  const adapterManager = require('../../services/adapter-manager').default;
9
10
 
10
11
  const mediaStorage = adapterManager.getAdapter('storage:media');
@@ -31,45 +32,16 @@ module.exports = {
31
32
  },
32
33
  });
33
34
 
34
- this.api = {
35
- startMediaInliner: async (domains) => {
36
- if (!domains || !domains.length) {
37
- // default domains to inline from if none are provided
38
- domains = ['https://s3.amazonaws.com/revue', 'https://substackcdn.com'];
39
- }
40
-
41
- debug('[Inliner] Starting media inlining job for domains: ', domains);
35
+ instance = mediaInliner;
36
+ },
42
37
 
43
- // @NOTE: the job is "inline" (aka non-offloaded into a thread), because usecases are currently
44
- // limited to migrational, so there is no expectations for site's availability etc.
45
- logging.info('[Background Job] external-media-inliner queued');
46
- await jobsService.addJob({
47
- name: 'external-media-inliner',
48
- job: async (data) => {
49
- const startedAt = Date.now();
50
- logging.info('[Background Job] external-media-inliner started');
51
- try {
52
- const result = await mediaInliner.inline(data.domains);
53
- logging.info(
54
- `[Background Job] external-media-inliner completed in ${Date.now() - startedAt}ms`,
55
- );
56
- return result;
57
- } catch (err) {
58
- logging.error(
59
- err,
60
- `[Background Job] external-media-inliner failed after ${Date.now() - startedAt}ms`,
61
- );
62
- throw err;
63
- }
64
- },
65
- data: { domains },
66
- offloaded: false,
67
- });
38
+ getInstance() {
39
+ if (!instance) {
40
+ throw new errors.IncorrectUsageError({
41
+ message: 'Media inliner used before init(). Call init() from boot first.',
42
+ });
43
+ }
68
44
 
69
- return {
70
- status: 'success',
71
- };
72
- },
73
- };
45
+ return instance;
74
46
  },
75
47
  };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -54,14 +54,14 @@ class MembersCSVExporter {
54
54
  const hasFilter = options.limit !== 'all' || options.filter || options.search;
55
55
  const ids = hasFilter ? await this._members.findFilteredIds(options) : null;
56
56
  if (ids) {
57
- logging.info(`[MembersExporter] Found ${ids.length} members matching filter criteria`);
57
+ logging.info({ event: { name: 'members-export.filtered' }, matched: ids.length }, 'Found members matching the export filter');
58
58
  }
59
59
  const reference = await this.fetchReferenceData();
60
60
  const membersQuery = this._knex('members').select(...MEMBER_COLUMNS);
61
61
  if (ids) {
62
62
  membersQuery.whereIn('id', ids);
63
63
  }
64
- logging.info('[MembersExporter] Starting streaming export of members');
64
+ logging.info({ event: { name: 'members-export.started' } }, 'Starting streaming export of members');
65
65
  const batchingTransform = this.createBatchingTransform();
66
66
  const processingTransform = this.createProcessingTransform(reference);
67
67
  (0, node_stream_1.pipeline)(membersQuery.stream(), batchingTransform, processingTransform, (err) => {
@@ -69,9 +69,7 @@ class MembersCSVExporter {
69
69
  logging.error({ event: { name: 'members-export.stream.error' }, err }, 'Members export stream failed');
70
70
  }
71
71
  else {
72
- logging.info('[MembersExporter] Total time taken for member export: ' +
73
- (Date.now() - start) / 1000 +
74
- 's');
72
+ logging.info({ event: { name: 'members-export.completed' }, durationMs: Date.now() - start }, 'Members export completed');
75
73
  }
76
74
  });
77
75
  return processingTransform;
@@ -95,7 +93,7 @@ class MembersCSVExporter {
95
93
  acc[label.id] = label.name;
96
94
  return acc;
97
95
  }, {}));
98
- logging.info('[MembersExporter] Fetched products and labels in ' + (Date.now() - start) + 'ms');
96
+ logging.info({ event: { name: 'members-export.reference_data_read' }, durationMs: Date.now() - start }, 'Read the products and labels an export names');
99
97
  const activeCustomFields = await this._customFields.activeDefinitions();
100
98
  return { allProducts, allLabels, activeCustomFields };
101
99
  }
@@ -17,6 +17,7 @@ const messages = {
17
17
  giftCannotCombineWithImportTier: 'Cannot specify both gift_id and import_tier.',
18
18
  giftCannotCombineWithComplimentary: 'Cannot specify both gift_id and complimentary_plan.',
19
19
  giftReassignFailed: 'Failed to reassign gift to member.',
20
+ customFieldWriteFailed: 'Failed to save the custom field values for this member.',
20
21
  };
21
22
  // Columns whose presence makes a row slow to import (they reach out to Stripe), so
22
23
  // a file carrying any of them is deferred regardless of size. The routing decision
@@ -90,7 +91,7 @@ class MembersCSVImporter {
90
91
  // a request without a user actually reaches the deferred path.
91
92
  const emailRecipient = requestUserEmail ?? (await this._email.getDefaultRecipient());
92
93
  const spooled = await this._spool.write(rows);
93
- logging.info('[Background Job] members-import queued');
94
+ logging.info({ event: { name: 'members.import.queued' }, rows: rows.length }, 'Members import queued');
94
95
  this._addJob({
95
96
  job: () => this.runImportJob(spooled, { labelName, extraLabels, emailRecipient }, verificationTrigger),
96
97
  offloaded: false,
@@ -101,7 +102,7 @@ class MembersCSVImporter {
101
102
  // in the job itself, and there is no retry behind it.
102
103
  async runImportJob(spooled, { labelName, extraLabels, emailRecipient, }, verificationTrigger) {
103
104
  const startedAt = Date.now();
104
- logging.info('[Background Job] members-import started');
105
+ logging.info({ event: { name: 'members.import.started' } }, 'Members import started');
105
106
  // Null until the import produces one: parsing and mapping already happened inside
106
107
  // the request, so anything failing from here is ours rather than the file's.
107
108
  let result = null;
@@ -125,10 +126,15 @@ class MembersCSVImporter {
125
126
  links: this._email.links,
126
127
  })));
127
128
  if (result) {
128
- logging.info(`[Background Job] members-import completed in ${Date.now() - startedAt}ms: imported ${result.imported}, ${result.errors.length} row(s) rejected`);
129
+ logging.info({
130
+ event: { name: 'members.import.completed' },
131
+ durationMs: Date.now() - startedAt,
132
+ imported: result.imported,
133
+ rejected: result.errors.length,
134
+ }, 'Members import completed');
129
135
  }
130
136
  else {
131
- logging.info(`[Background Job] members-import failed after ${Date.now() - startedAt}ms`);
137
+ logging.info({ event: { name: 'members.import.failed' }, durationMs: Date.now() - startedAt }, 'Members import failed');
132
138
  }
133
139
  }
134
140
  // Only the write itself may throw. Callers rely on that to tell an import that never
@@ -300,7 +306,17 @@ class MembersCSVImporter {
300
306
  }
301
307
  }
302
308
  // On the row's transaction, so the values commit or roll back with the member.
303
- await this._customFields.applyWrite(member.id, customFieldPlan, trx);
309
+ try {
310
+ await this._customFields.applyWrite(member.id, customFieldPlan, trx);
311
+ }
312
+ catch (writeError) {
313
+ // planWrite passed every value before the transaction opened, so a failure
314
+ // here is ours and not the row's. Operators get the original, which a driver
315
+ // will have written a query into; the publisher gets a sentence instead, in a
316
+ // file they open next to a spreadsheet.
317
+ this._report(writeError);
318
+ throw new errors.DataImportError({ message: tpl(messages.customFieldWriteFailed) });
319
+ }
304
320
  await trx.commit();
305
321
  imported += 1;
306
322
  }
@@ -175,7 +175,12 @@ module.exports = class MembersCSVImporterStripeUtils {
175
175
  await this.archivePrice(newStripePrice.id);
176
176
  } catch (archiveErr) {
177
177
  logging.warn(
178
- `Failed to archive orphaned Stripe price ${newStripePrice.id} after a failed subscription update: ${archiveErr.message}`,
178
+ {
179
+ event: { name: 'members.import.orphaned_price_archive_failed' },
180
+ err: archiveErr,
181
+ stripePriceId: newStripePrice.id,
182
+ },
183
+ 'Failed to archive an orphaned Stripe price after a failed subscription update',
179
184
  );
180
185
  }
181
186
  throw err;
@@ -63,7 +63,12 @@ function makeImporter(deps) {
63
63
  const customFields = {
64
64
  activeFields: async () => labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : [],
65
65
  planWrite: (values) => deps.customFields.values.planWrite(values),
66
- applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, { executor }),
66
+ // Every value the import writes came out of the file, whichever column carried it.
67
+ // An import has no id to give until runs are tracked, so it names its kind only.
68
+ applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, {
69
+ writtenBy: { type: 'import', id: null },
70
+ executor,
71
+ }),
67
72
  };
68
73
  // Inline jobs never reach the job manager's Sentry handler, which is wired to the
69
74
  // offloaded worker path only, so a throw here would be seen by nobody.
@@ -179,6 +179,11 @@ module.exports = function MembersAPI({
179
179
  offersAPI,
180
180
  stripeAPIService,
181
181
  settingsCache,
182
+ // The service wrapper, not the checkout config it builds: tiers and members are
183
+ // initialised in the same Promise.all, so reading the property here would capture
184
+ // whatever it was before tiers finished — usually undefined.
185
+ tiersService,
186
+ labsService,
182
187
  });
183
188
 
184
189
  const memberController = new MemberController({
@@ -9,6 +9,8 @@ const messages = {
9
9
  memberNotFound: 'Member not found.',
10
10
  customFieldsOnAdd:
11
11
  'Custom field values cannot be set while creating a member. Create the member, then set values with an edit.',
12
+ customFieldsWithoutWriter:
13
+ 'Custom field values cannot be set by a request with no authenticated user or integration.',
12
14
  };
13
15
 
14
16
  // Stored in the action's `context.action_name`; Admin maps it to a display label.
@@ -654,7 +656,23 @@ module.exports = class MemberBREADService {
654
656
  }
655
657
 
656
658
  if (plannedCustomFields) {
657
- await this.customFieldValues.applyWrite(model.id, plannedCustomFields);
659
+ // Every value reaching here was typed into the Admin API, so the writer is
660
+ // whoever made the request — the same pair the action log records, so the two
661
+ // agree about who did it rather than one saying only that it was "admin".
662
+ //
663
+ // The only route to this branch is the authenticated Admin API, so an anonymous
664
+ // request is a mistake somewhere upstream rather than a writer to invent a name
665
+ // for. Refusing keeps every stored writer resolvable.
666
+ const context = options.context || {};
667
+ if (!context.integration && !context.user) {
668
+ throw new errors.IncorrectUsageError({
669
+ message: tpl(messages.customFieldsWithoutWriter),
670
+ });
671
+ }
672
+ const writtenBy = context.integration
673
+ ? { type: 'integration', id: context.integration.id }
674
+ : { type: 'user', id: context.user };
675
+ await this.customFieldValues.applyWrite(model.id, plannedCustomFields, { writtenBy });
658
676
 
659
677
  // Custom fields aren't a member column or relation, so an edit touching
660
678
  // only them leaves `model._changed` empty and the save fires nothing.
@@ -14,6 +14,8 @@ 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 {{checkout: import('../../../tier-checkout-config').TierCheckoutConfigService}} deps.tiersService
18
+ * @param {{isSet(flag: string): boolean}} deps.labsService
17
19
  */
18
20
  constructor(deps) {
19
21
  /** @private */
@@ -30,6 +32,10 @@ class PaymentsService {
30
32
  this.stripeAPIService = deps.stripeAPIService;
31
33
  /** @private */
32
34
  this.settingsCache = deps.settingsCache;
35
+ /** @private */
36
+ this.tiersService = deps.tiersService;
37
+ /** @private */
38
+ this.labsService = deps.labsService;
33
39
  DomainEvents.subscribe(OfferCreatedEvent, async (event) => {
34
40
  await this.getCouponForOffer(event.data.offer.id);
35
41
  });
@@ -114,11 +120,19 @@ class PaymentsService {
114
120
  const price = await this.getPriceForTierCadence(tier, cadence);
115
121
 
116
122
  const data = {
117
- metadata,
123
+ // The tier being bought, recorded on the session so the completed event can find
124
+ // the configuration that produced its questions. Nothing else carries it: a
125
+ // completed session names prices and products, and mapping those back is a
126
+ // lookup that can fail where this cannot.
127
+ metadata: { ...metadata, ghostTierId: tier.id.toHexString() },
118
128
  successUrl: successUrl,
119
129
  cancelUrl: cancelUrl,
120
130
  trialDays: trialDays ?? tier.trialDays,
121
131
  coupon: coupon?.id,
132
+ // Resolved here rather than cached with the tier, so a field archived a minute
133
+ // ago stops being asked on the next checkout. A failure to resolve must not
134
+ // stop a member paying, so it costs the questions and nothing else.
135
+ checkout: await this.getCheckoutConfigForTier(tier),
122
136
  };
123
137
 
124
138
  // If we already have a coupon, we don't want to give trial days over it
@@ -135,6 +149,38 @@ class PaymentsService {
135
149
  return session.url;
136
150
  }
137
151
 
152
+ /**
153
+ * What this tier's checkout should ask for beyond the payment, or nothing.
154
+ *
155
+ * Undefined on every path but the configured one, including the flag being off: the
156
+ * session builder adds no parameters for it, so an unconfigured site's request to
157
+ * Stripe is exactly the request it made before this existed.
158
+ *
159
+ * @private
160
+ * @param {import('../../../tiers/tier')} tier
161
+ */
162
+ async getCheckoutConfigForTier(tier) {
163
+ const checkoutConfig = this.tiersService?.checkout;
164
+ if (!this.labsService?.isSet('membersCustomFields') || !checkoutConfig) {
165
+ return undefined;
166
+ }
167
+ try {
168
+ return await checkoutConfig.resolve(tier.id.toHexString());
169
+ } catch (err) {
170
+ // A checkout that asks one fewer question still takes the money; one that fails
171
+ // to be created takes none. This is the whole reason it is caught.
172
+ logging.error(
173
+ {
174
+ event: { name: 'stripe_checkout.tier_config.resolve_failed' },
175
+ err,
176
+ tierId: tier.id.toHexString(),
177
+ },
178
+ 'Failed to resolve what a tier checkout should collect',
179
+ );
180
+ return undefined;
181
+ }
182
+ }
183
+
138
184
  /**
139
185
  * @param {object} params
140
186
  * @param {Member} [params.member]
@@ -3,8 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.actingContext = actingContext;
6
7
  exports.recordCustomFieldAction = recordCustomFieldAction;
7
8
  const logging_1 = __importDefault(require("@tryghost/logging"));
9
+ /**
10
+ * Who is acting, read off an API frame's context.
11
+ *
12
+ * Here rather than in each endpoint, because two resources now record custom field history
13
+ * and a second reading of the same shape is a second chance to disagree about it. An
14
+ * integration and a user are both actors; a request that is neither — a webhook, a job —
15
+ * has none, and the history says so rather than guessing.
16
+ */
17
+ function actingContext(context) {
18
+ const frame = (context ?? {});
19
+ if (frame.integration) {
20
+ return { actor: { id: frame.integration.id, type: 'integration' } };
21
+ }
22
+ if (frame.user) {
23
+ return { actor: { id: frame.user, type: 'user' } };
24
+ }
25
+ return { actor: null };
26
+ }
8
27
  // Field-definition changes map to activity-feed events. A field's timeline reads
9
28
  // added -> edited -> archived -> restored, and a permanent delete (only from the
10
29
  // archived state) ends it with deleted.
@@ -35,6 +54,13 @@ async function recordCustomFieldAction({ Action, context, verb, subject, details
35
54
  }, { autoRefresh: false });
36
55
  }
37
56
  catch (err) {
38
- logging_1.default.error(err);
57
+ logging_1.default.error({
58
+ event: { name: 'members.custom_fields.action_log_failed' },
59
+ err,
60
+ verb,
61
+ subject,
62
+ actorType: context.actor.type,
63
+ actorId: context.actor.id,
64
+ }, 'Failed to record a member custom field action');
39
65
  }
40
66
  }