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
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CustomFieldBindingsService = void 0;
7
+ const bson_objectid_1 = __importDefault(require("bson-objectid"));
8
+ const logging_1 = __importDefault(require("@tryghost/logging"));
9
+ const schema_1 = require("./schema");
10
+ const FIELDS_TABLE = 'members_custom_fields';
11
+ const BINDINGS_TABLE = 'members_custom_field_bindings';
12
+ /**
13
+ * Where a source sends what it collected: a `port` is the name that source uses for a
14
+ * thing, and the binding resolves it to one of the publisher's fields.
15
+ */
16
+ class CustomFieldBindingsService {
17
+ knex;
18
+ values;
19
+ constructor({ knex, values }) {
20
+ this.knex = knex;
21
+ this.values = values;
22
+ }
23
+ async bind(db, productId, port, customFieldKey, now) {
24
+ const existing = await db(BINDINGS_TABLE).where({ product_id: productId, port }).first();
25
+ if (existing?.custom_field_key === customFieldKey) {
26
+ await db(BINDINGS_TABLE).where('id', existing.id).update({ updated_at: now });
27
+ return existing.id;
28
+ }
29
+ if (existing) {
30
+ await db(BINDINGS_TABLE).where('id', existing.id).del();
31
+ }
32
+ const bindingId = new bson_objectid_1.default().toHexString();
33
+ await db(BINDINGS_TABLE).insert({
34
+ id: bindingId,
35
+ product_id: productId,
36
+ port,
37
+ custom_field_key: customFieldKey,
38
+ created_at: now,
39
+ updated_at: now,
40
+ });
41
+ return bindingId;
42
+ }
43
+ /** Stops the writing. Whatever hangs off the binding cascades with it. */
44
+ async remove(db, productId, port) {
45
+ await db(BINDINGS_TABLE).where({ product_id: productId, port }).del();
46
+ }
47
+ /**
48
+ * Values arrive in the order they are to be applied: where two land in one field, the
49
+ * last of them is what the field holds.
50
+ */
51
+ async writeCollected(memberId, productId, collected) {
52
+ for (const { port, value } of collected) {
53
+ const destination = await this.resolve(productId, port);
54
+ if (!destination) {
55
+ continue;
56
+ }
57
+ await this.writeOne(memberId, destination, value);
58
+ }
59
+ }
60
+ async writeOne(memberId, into, value) {
61
+ let planned;
62
+ try {
63
+ planned = await this.values.planWrite({ [into.key]: value });
64
+ }
65
+ catch (err) {
66
+ logging_1.default.warn({
67
+ event: { name: 'members.custom_fields.collected_value_rejected' },
68
+ err,
69
+ memberId,
70
+ customFieldKey: into.key,
71
+ }, 'A collected value could not be saved');
72
+ return;
73
+ }
74
+ try {
75
+ await this.values.applyWrite(memberId, planned, {
76
+ writtenBy: { type: 'binding', id: into.bindingId },
77
+ });
78
+ }
79
+ catch (err) {
80
+ logging_1.default.error({
81
+ event: { name: 'members.custom_fields.collected_value_write_failed' },
82
+ err,
83
+ memberId,
84
+ customFieldKey: into.key,
85
+ bindingId: into.bindingId,
86
+ }, 'Failed to store a collected custom field value');
87
+ }
88
+ }
89
+ async resolve(productId, port) {
90
+ const row = await this.knex(BINDINGS_TABLE)
91
+ .join(FIELDS_TABLE, `${BINDINGS_TABLE}.custom_field_key`, `${FIELDS_TABLE}.key`)
92
+ .where(`${BINDINGS_TABLE}.product_id`, productId)
93
+ .where(`${BINDINGS_TABLE}.port`, port)
94
+ // An archived destination is still where this goes, and still not somewhere a value
95
+ // can land, so the write drops rather than waiting.
96
+ .where(`${FIELDS_TABLE}.status`, schema_1.FIELD_STATUS.active)
97
+ .select(`${BINDINGS_TABLE}.id as binding_id`, `${FIELDS_TABLE}.key`, `${FIELDS_TABLE}.type`)
98
+ .first();
99
+ if (!row) {
100
+ return null;
101
+ }
102
+ // Decoded rather than trusted: a join is a read boundary, and `type` is what decides
103
+ // how the collected value is read. Unreadable counts as unresolved rather than
104
+ // throwing, so one bad row skips its value the way an unbound port does instead of
105
+ // failing everything else the same checkout collected.
106
+ const bound = schema_1.DbBoundField.safeParse(row);
107
+ if (!bound.success) {
108
+ logging_1.default.warn({
109
+ event: { name: 'members.custom_fields.binding_unreadable' },
110
+ err: bound.error,
111
+ productId,
112
+ port,
113
+ }, 'A binding could not be read');
114
+ return null;
115
+ }
116
+ return { bindingId: bound.data.binding_id, key: bound.data.key, type: bound.data.type };
117
+ }
118
+ }
119
+ exports.CustomFieldBindingsService = CustomFieldBindingsService;
@@ -33,10 +33,12 @@ const MAX_KEY_BASE_LENGTH = MAX_KEY_LENGTH - (String(MAX_KEY_ITERATIONS).length
33
33
  // object from JSON.parse. A key naming a member of Object.prototype reads back as
34
34
  // inherited rather than absent wherever one of those objects is indexed.
35
35
  //
36
- // Derived rather than listed, because the set is a consequence of how keys are
37
- // minted: minting lowercases and trims leading underscores, so a prototype name
38
- // survives only if it has neither. `constructor` is the only one.
39
- const RESERVED_KEYS = Object.getOwnPropertyNames(Object.prototype).filter((name) => (0, key_1.mintableKey)(name) === name);
36
+ // Derived rather than listed, from the format a key may take rather than from how one
37
+ // is minted: a caller can state a key instead of deriving it from a name, so what has
38
+ // to be reserved is every prototype name a well-formed key could spell. The ones that
39
+ // carry a capital cannot be spelled at all and need no reserving; `constructor` and
40
+ // `__proto__` can.
41
+ const RESERVED_KEYS = Object.getOwnPropertyNames(Object.prototype).filter((name) => key_1.KEY_CHARACTERS.test(name));
40
42
  const FieldName = zod_1.z
41
43
  .string()
42
44
  .trim()
@@ -114,11 +116,11 @@ class CustomFieldDefinitionsService {
114
116
  return rows.map((row) => zod_1.z.decode(codec_1.customFieldCodec, row));
115
117
  }
116
118
  async read(key) {
117
- const row = await this.knex(TABLE).where('key', key).first();
118
- if (!row) {
119
+ const [field] = await this.list((0, queries_1.fieldByKey)(this.knex, key));
120
+ if (!field) {
119
121
  throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
120
122
  }
121
- return zod_1.z.decode(codec_1.customFieldCodec, row);
123
+ return field;
122
124
  }
123
125
  /**
124
126
  * Create one or more field definitions. All-or-nothing: the batch runs in a
@@ -167,13 +169,11 @@ class CustomFieldDefinitionsService {
167
169
  for (const [index, field] of fields.entries()) {
168
170
  await this.assertNameAvailable(trx, field.name);
169
171
  const key = await this.mintKey(trx, bases[index]);
170
- await trx(TABLE).insert({
171
- id: new bson_objectid_1.default().toHexString(),
172
+ await this.insertField(trx, {
172
173
  key,
173
174
  name: field.name,
174
175
  type: field.type,
175
- sort_order: firstSortOrder + index,
176
- created_at: new Date(),
176
+ sortOrder: firstSortOrder + index,
177
177
  });
178
178
  keys.push(key);
179
179
  }
@@ -194,7 +194,69 @@ class CustomFieldDefinitionsService {
194
194
  // Logged after the commit: the action log is a separate Bookshelf write
195
195
  // outside this transaction, so recording inside it would leave orphaned
196
196
  // "added" entries for fields a rollback never created.
197
- for (const field of created) {
197
+ await this.recordCreated(context, created);
198
+ return created;
199
+ }
200
+ /**
201
+ * Given an executor it joins that transaction; given none it opens its own. Unlike
202
+ * `add`, the key is stated rather than minted from the name, and nothing about it is
203
+ * worked around: a caller states a key because something else already names that key,
204
+ * so a variant of it would be a field nothing points at.
205
+ *
206
+ * The field is returned rather than logged: the history writes on its own connection,
207
+ * which a single-connection pool would deadlock against an open transaction.
208
+ */
209
+ async addOne(wanted, { executor = this.knex } = {}) {
210
+ // Before any database access, the way `add` mints before opening its transaction:
211
+ // an unusable key is a payload problem worth reporting on its own terms.
212
+ assertKeyUsable(wanted.key);
213
+ const write = async (db) => {
214
+ await this.assertWithinLimit(db, 1);
215
+ await this.assertKeyAvailable(db, wanted.key);
216
+ await this.assertNameAvailable(db, wanted.name);
217
+ await this.insertField(db, {
218
+ key: wanted.key,
219
+ name: wanted.name,
220
+ type: wanted.type,
221
+ sortOrder: await this.nextSortOrder(db),
222
+ });
223
+ const [created] = await this.readMany(db, [wanted.key]);
224
+ return created;
225
+ };
226
+ // knex's marker for a transactor: join it rather than nesting a savepoint under it.
227
+ return executor.isTransaction ? write(executor) : executor.transaction(write);
228
+ }
229
+ /**
230
+ * Only for a stated key. Minting picks a free one instead, so this reads as a clean 422
231
+ * where the unique index would read as a 500.
232
+ */
233
+ async assertKeyAvailable(db, key) {
234
+ const taken = await db(TABLE).where('key', key).first();
235
+ if (taken) {
236
+ throw new errors_1.default.ValidationError({
237
+ message: 'A custom field with this key already exists.',
238
+ property: 'key',
239
+ });
240
+ }
241
+ }
242
+ async insertField(db, field) {
243
+ await db(TABLE).insert({
244
+ id: new bson_objectid_1.default().toHexString(),
245
+ key: field.key,
246
+ name: field.name,
247
+ type: field.type,
248
+ sort_order: field.sortOrder,
249
+ created_at: new Date(),
250
+ });
251
+ }
252
+ /** `read` for a caller that is deciding rather than serving: absent is an answer. */
253
+ async findByKey(key, { executor = this.knex } = {}) {
254
+ const row = await executor(TABLE).where('key', key).first();
255
+ return row ? zod_1.z.decode(codec_1.customFieldCodec, row) : null;
256
+ }
257
+ /** The same entry `add` writes. Only the caller knows its transaction committed. */
258
+ async recordCreated(context, fields) {
259
+ for (const field of fields) {
198
260
  await this.recordAction({
199
261
  context,
200
262
  verb: 'create',
@@ -202,7 +264,6 @@ class CustomFieldDefinitionsService {
202
264
  details: { primary_name: field.name, key: field.key },
203
265
  });
204
266
  }
205
- return created;
206
267
  }
207
268
  /**
208
269
  * The operational ceiling on how many definitions a site can hold. This is a
@@ -460,6 +521,36 @@ class CustomFieldDefinitionsService {
460
521
  }
461
522
  }
462
523
  exports.CustomFieldDefinitionsService = CustomFieldDefinitionsService;
524
+ /**
525
+ * The shape a stated key has to have. Minting derives one that is usable by
526
+ * construction, so this is the check that path never needed: a caller stating its own
527
+ * key has said nothing about the format, and guarding here covers every route in
528
+ * rather than whichever one arrived first.
529
+ *
530
+ * The length bound is the column's own, not `mintKey`'s: that one holds back room for a
531
+ * `_<n>` collision suffix, and a stated key is written exactly as given and never
532
+ * suffixed, so the whole column is available to it.
533
+ */
534
+ function assertKeyUsable(key) {
535
+ if (!key_1.KEY_CHARACTERS.test(key)) {
536
+ throw new errors_1.default.ValidationError({
537
+ message: 'A custom field key can only contain lowercase letters, numbers and underscores.',
538
+ property: 'key',
539
+ });
540
+ }
541
+ if (key.length > MAX_KEY_LENGTH) {
542
+ throw new errors_1.default.ValidationError({
543
+ message: `A custom field key can be at most ${MAX_KEY_LENGTH} characters.`,
544
+ property: 'key',
545
+ });
546
+ }
547
+ if (RESERVED_KEYS.includes(key)) {
548
+ throw new errors_1.default.ValidationError({
549
+ message: `${key} cannot be used as a custom field key.`,
550
+ property: 'key',
551
+ });
552
+ }
553
+ }
463
554
  // The field a zod issue points at. Create validates an array, so an issue's path
464
555
  // is prefixed with the item's index (`[0, 'name']`); `property` names the field
465
556
  // that is wrong, so the numeric prefix is dropped. Which item it was is reported
@@ -1,15 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.values = exports.definitions = void 0;
3
+ exports.bindings = exports.values = exports.definitions = exports.actingContext = void 0;
4
4
  exports.init = init;
5
5
  const definitions_service_1 = require("./definitions-service");
6
6
  const values_service_1 = require("./values-service");
7
+ const bindings_service_1 = require("./bindings-service");
7
8
  const actions_1 = require("./actions");
8
9
  const config_1 = require("./config");
10
+ var actions_2 = require("./actions");
11
+ Object.defineProperty(exports, "actingContext", { enumerable: true, get: function () { return actions_2.actingContext; } });
9
12
  function init() {
10
- // The two are constructed together below, so checking both keeps the "both or
11
- // neither" invariant explicit rather than trusting one to stand in for the pair.
12
- if (exports.definitions && exports.values) {
13
+ // The three are constructed together below, so checking all of them keeps the "all or
14
+ // none" invariant explicit rather than trusting one to stand in for the rest.
15
+ if (exports.definitions && exports.values && exports.bindings) {
13
16
  return;
14
17
  }
15
18
  const { knex } = require('../../data/db');
@@ -31,4 +34,8 @@ function init() {
31
34
  knex,
32
35
  getMaxDefinitions: () => (0, config_1.resolveMaxDefinitions)(config.get('members:customFields:maxDefinitions')),
33
36
  });
37
+ // Built after the values, which is what a binding routes into. It has no handle on the
38
+ // definitions: making a field is not part of binding to one, so a caller that needs both
39
+ // asks for both.
40
+ exports.bindings = new bindings_service_1.CustomFieldBindingsService({ knex, values: exports.values });
34
41
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.activeFields = activeFields;
4
+ exports.fieldByKey = fieldByKey;
4
5
  exports.inFieldOrder = inFieldOrder;
5
6
  const schema_1 = require("./schema");
6
7
  const FIELDS_TABLE = 'members_custom_fields';
@@ -11,6 +12,10 @@ const FIELDS_TABLE = 'members_custom_fields';
11
12
  function activeFields(db) {
12
13
  return db(FIELDS_TABLE).where('status', schema_1.FIELD_STATUS.active);
13
14
  }
15
+ /** One field by key, as the same kind of query the rest of this reads through. */
16
+ function fieldByKey(db, key) {
17
+ return db(FIELDS_TABLE).where(`${FIELDS_TABLE}.key`, key);
18
+ }
14
19
  /**
15
20
  * The publisher's order, applied to every read of the list. Here for the same reason the
16
21
  * status filter is: a read that forgets it comes back in whatever order the engine chose.
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DbCustomFieldLeaf = exports.DbCustomFieldValue = exports.DbCustomField = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
3
+ exports.DbBoundField = exports.DbCustomFieldBinding = exports.DbCustomFieldLeaf = exports.DbCustomFieldValue = exports.WrittenBy = 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
- const db_date_1 = require("../../lib/db-date");
6
+ const date_1 = require("../../lib/db-types/date");
7
7
  // `archived` is soft: the field drops out of the values path but stays in the definition
8
8
  // list so it can be renamed, restored or deleted. Mirrors schema.js's `isIn` on the
9
9
  // column, which is static config and cannot import this.
@@ -17,9 +17,21 @@ exports.DbCustomField = zod_1.z.object({
17
17
  name: zod_1.z.string(),
18
18
  type: custom_field_types_1.FieldTypeSchema,
19
19
  status: exports.FieldStatusSchema,
20
- created_at: db_date_1.DbDate,
21
- updated_at: db_date_1.DbDate.nullable(),
20
+ created_at: date_1.DbDate,
21
+ updated_at: date_1.DbDate.nullable(),
22
22
  });
23
+ /**
24
+ * How a value arrived, not who caused it: who edited a member's fields is already an
25
+ * action, and Stripe is not a person. `type` is the namespace that makes `id` resolvable —
26
+ * `users`, `integrations`, `members_custom_field_bindings` — so the one writer that
27
+ * resolves in no table is the one with no id to give.
28
+ */
29
+ exports.WrittenBy = zod_1.z.discriminatedUnion('type', [
30
+ zod_1.z.object({ type: zod_1.z.literal('user'), id: zod_1.z.string() }),
31
+ zod_1.z.object({ type: zod_1.z.literal('integration'), id: zod_1.z.string() }),
32
+ zod_1.z.object({ type: zod_1.z.literal('binding'), id: zod_1.z.string() }),
33
+ zod_1.z.object({ type: zod_1.z.literal('import'), id: zod_1.z.null() }),
34
+ ]);
23
35
  // One part of a member's value. What a `path` means is storage.ts's business, so the row
24
36
  // carries it as a plain string.
25
37
  exports.DbCustomFieldValue = zod_1.z.object({
@@ -30,8 +42,13 @@ exports.DbCustomFieldValue = zod_1.z.object({
30
42
  // Nullable like the column, though nothing here writes a null: a part with no value
31
43
  // has no row.
32
44
  value_text: zod_1.z.string().nullable(),
33
- created_at: db_date_1.DbDate,
34
- updated_at: db_date_1.DbDate.nullable(),
45
+ // Plain columns rather than the `WrittenBy` union: the rule holds at the write
46
+ // boundary, so one malformed row cannot throw away a member's whole profile on read.
47
+ written_by_type: zod_1.z.string(),
48
+ // Null for the one writer that resolves nowhere: an import, until runs are tracked.
49
+ written_by_id: zod_1.z.string().nullable(),
50
+ created_at: date_1.DbDate,
51
+ updated_at: date_1.DbDate.nullable(),
35
52
  });
36
53
  // The field's key travels with the row so a value assembles without a second lookup.
37
54
  //
@@ -45,3 +62,17 @@ exports.DbCustomFieldLeaf = zod_1.z.object({
45
62
  path: zod_1.z.string(),
46
63
  value_text: zod_1.z.string(),
47
64
  });
65
+ exports.DbCustomFieldBinding = zod_1.z.object({
66
+ id: zod_1.z.string(),
67
+ product_id: zod_1.z.string(),
68
+ port: zod_1.z.string(),
69
+ custom_field_key: zod_1.z.string(),
70
+ created_at: date_1.DbDate,
71
+ updated_at: date_1.DbDate.nullable(),
72
+ });
73
+ /** A binding joined to the field it points at, which is how a collected value is routed. */
74
+ exports.DbBoundField = zod_1.z.object({
75
+ binding_id: zod_1.z.string(),
76
+ key: zod_1.z.string(),
77
+ type: custom_field_types_1.FieldTypeSchema,
78
+ });
@@ -80,7 +80,12 @@ class CustomFieldValuesService {
80
80
  leaves.push(schema_1.DbCustomFieldLeaf.parse(row));
81
81
  }
82
82
  catch (err) {
83
- logging_1.default.warn(`Skipping unreadable custom field value (field '${row.key}', path '${row.path}'): ${err instanceof Error ? err.message : String(err)}`);
83
+ logging_1.default.warn({
84
+ event: { name: 'members.custom_fields.value_unreadable' },
85
+ err,
86
+ customFieldKey: row.key,
87
+ path: row.path,
88
+ }, 'Skipping an unreadable custom field value');
84
89
  }
85
90
  }
86
91
  return (0, storage_1.valuesFromLeaves)(leaves);
@@ -167,10 +172,13 @@ class CustomFieldValuesService {
167
172
  * them, and saying nothing about a path leaves it alone. There is no whole-value
168
173
  * replace, so a caller that does not know about a field cannot erase it.
169
174
  *
175
+ * `writtenBy` is required and has no default: every writer has to name itself, so a new
176
+ * one cannot quietly inherit the identity of whichever was written first.
177
+ *
170
178
  * Always transactional. Given an executor it joins that transaction, so the importer's
171
179
  * failed value write takes its member with it; given none it opens its own.
172
180
  */
173
- async applyWrite(memberId, writes, { executor = this.knex } = {}) {
181
+ async applyWrite(memberId, writes, { writtenBy, executor = this.knex }) {
174
182
  if (writes.length === 0) {
175
183
  return;
176
184
  }
@@ -197,6 +205,8 @@ class CustomFieldValuesService {
197
205
  custom_field_key: field.key,
198
206
  path: leaf.path,
199
207
  value_text: leaf.value_text,
208
+ written_by_type: writtenBy.type,
209
+ written_by_id: writtenBy.id,
200
210
  created_at: now,
201
211
  updated_at: now,
202
212
  })));
@@ -227,7 +237,9 @@ class CustomFieldValuesService {
227
237
  // Naming the columns rather than giving values takes each from the row
228
238
  // that lost the conflict, so every part updates to its own value.
229
239
  .onConflict(['member_id', 'custom_field_key', 'path'])
230
- .merge(['value_text', 'updated_at']);
240
+ // The writer is merged with the value, so a leaf names who wrote what
241
+ // it currently holds rather than who wrote its first value.
242
+ .merge(['value_text', 'written_by_type', 'written_by_id', 'updated_at']);
231
243
  }
232
244
  };
233
245
  // knex's marker for a transactor: join it rather than nesting a savepoint under it.
@@ -86,5 +86,12 @@ module.exports = function getConfigProperties() {
86
86
  };
87
87
  }
88
88
 
89
+ if (config.get('docsbot')) {
90
+ configProperties.docsbot = {
91
+ enabled: config.get('docsbot:enabled'),
92
+ id: config.get('docsbot:id'),
93
+ };
94
+ }
95
+
89
96
  return configProperties;
90
97
  };