ghost 6.60.0 → 6.62.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.
- package/components/ghost-storage-base-3.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-checkout-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.1.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
- package/content/settings/README.md +1 -1
- package/content/themes/casper/.gitignore +28 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/css/screen.css +2 -1
- package/content/themes/casper/assets/js/lightbox.js +63 -46
- package/content/themes/casper/package.json +6 -6
- package/content/themes/casper/page.hbs +1 -0
- package/content/themes/casper/post.hbs +1 -0
- package/content/themes/source/.gitignore +28 -0
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/source.js +1 -1
- package/content/themes/source/assets/css/screen.css +2 -1
- package/content/themes/source/assets/js/lightbox.js +62 -45
- package/content/themes/source/assets/js/main.js +2 -2
- package/content/themes/source/package.json +4 -4
- package/content/themes/source/partials/feature-image.hbs +1 -0
- package/core/boot.js +62 -21
- package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
- package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
- package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
- package/core/built/admin/assets/{PolarAngleAxis-C4qtHy1B.js → PolarAngleAxis-pkMyqz8g.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-Bh09Stsj.js → RadialBarChart-JAKoS4KT.js} +1 -1
- package/core/built/admin/assets/{about-BgpmhCuM.js → about-Dyae_UT6.js} +1 -1
- package/core/built/admin/assets/account-migration-CESa0mnX.js +1 -0
- package/core/built/admin/assets/{action-list-lGzZLHc8.js → action-list-C4v0-du8.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DMHuPnJn.js → account-migration-By69CgCp.js} +16 -16
- package/core/built/admin/assets/activitypub/activitypub.js +9 -10
- package/core/built/admin/assets/activitypub/{avatar-DhTo5D1B.js → avatar-DLPL3Abs.js} +59 -68
- package/core/built/admin/assets/activitypub/{avatar-flipboard-DnVgbEfa.js → avatar-flipboard-C9iDY-QV.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-BT4zAlW7.js → bluesky-sharing-BJcPMrDd.js} +37 -37
- package/core/built/admin/assets/activitypub/{content-formatters-BeDZ-DIa.js → content-formatters-3rTHg3mp.js} +18 -18
- package/core/built/admin/assets/activitypub/{copy-8sqxUuNd.js → copy-DIAOQoCZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-xQDDu1CS.js → domain-DUR4xSkV.js} +121 -121
- package/core/built/admin/assets/activitypub/{edit-profile-BXdF_r2G.js → edit-profile-BqtfzxCp.js} +40 -40
- package/core/built/admin/assets/activitypub/{explore-CMt7gKlL.js → explore-BxuJd-J4.js} +27 -27
- package/core/built/admin/assets/activitypub/{feed-Bwqdddp_.js → feed-BM1TcksA.js} +51 -51
- package/core/built/admin/assets/activitypub/{field-4co_8FVg.js → field-Dn7HHx2-.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-DwakMs4X.js → hash-Bm3WxYN4.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-CCjkuow2.js +1284 -0
- package/core/built/admin/assets/activitypub/{inbox-ckD4wo_r.js → inbox-d8oJRTf4.js} +2 -2
- package/core/built/admin/assets/activitypub/label-1AA_s9Fa.js +16 -0
- package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-KCCO7L2J.js → moderation-CnmxEB8w.js} +91 -91
- package/core/built/admin/assets/activitypub/{note-BX_Bz5A0.js → note-ZKc_Bnba.js} +24 -24
- package/core/built/admin/assets/activitypub/{notifications-BQOYGvRv.js → notifications-CB2tJZau.js} +51 -51
- package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-BsJ9E5qf.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-CSUJJIlh.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-6h43G9_q.js → preferences-0-JZnUkI.js} +1519 -1519
- package/core/built/admin/assets/activitypub/{profile-BMN9VVZ2.js → profile-C35cFIhM.js} +256 -256
- package/core/built/admin/assets/activitypub/{reply-ZNo04xsO.js → reply-DYK5vVqb.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-3B9mUQ7i.js → routes-BD4vscyc.js} +796 -790
- package/core/built/admin/assets/activitypub/{separator-D9taVCCa.js → separator-BbQqTm-p.js} +3 -3
- package/core/built/admin/assets/activitypub/settings-BsMROdqh.js +315 -0
- package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-O0RL9YCN.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-BsGGnBuT.js → step-1-BMSx7c_X.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-2-teosIS0k.js → step-2-DtPQzlTH.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-BjD5TokC.js → step-3-CLlOZTyt.js} +11 -11
- package/core/built/admin/assets/activitypub/{tabs-Cvwl1wXU.js → tabs-Dxk-b9M-.js} +306 -306
- package/core/built/admin/assets/activitypub/{topic-filter-C3LZJc2X.js → topic-filter-5yZH6tId.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-Dp9f0gov.js → use-activity-pub-queries-CV50qe03.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-C4RI0sa9.js → use-navigate-with-base-path-Bmk5h4lW.js} +220 -220
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-C2oNDMUH.js → use-reply-chain-data-Cbvzgjzd.js} +30 -30
- package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-BjcoP_HC.js} +7 -5
- package/core/built/admin/assets/activitypub/{x-Dxm6pV9X.js → x-DbQZOLiI.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BlF6rs_f.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-CglVQqah.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-wz7BsuaG.js +1 -0
- package/core/built/admin/assets/analytics-C4HE1gBg.js +1 -0
- package/core/built/admin/assets/{announcement-bar-modal-BTHiy1ZO.js → announcement-bar-modal-Ci4zz8jO.js} +1 -1
- package/core/built/admin/assets/{api-keys-Boy5pLbD.js → api-keys-Ck-Yteiv.js} +1 -1
- package/core/built/admin/assets/audience-CtbA54Sn.js +1 -0
- package/core/built/admin/assets/{automated-emails-te1D5kq-.js → automated-emails-D5VVMND9.js} +1 -1
- package/core/built/admin/assets/automations-CzKt5fb8.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-BEtJ_gL_.js +1 -0
- package/core/built/admin/assets/{box-By_EgGtm.js → box-CdfQAP_F.js} +1 -1
- package/core/built/admin/assets/{brand-icon-09YqFZjV.js → brand-icon-CrIGOA3Z.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-CcFr_ltt.js → breadcrumb-zUrZrBtW.js} +1 -1
- package/core/built/admin/assets/{button-BshWPHvn.js → button-eNMKQWXr.js} +1 -1
- package/core/built/admin/assets/{card-CiiE7QfM.js → card--VNbfgo3.js} +1 -1
- package/core/built/admin/assets/{chart-BnXyfH11.js → chart-BrhA41Zi.js} +1 -1
- package/core/built/admin/assets/chart-helpers-CfBKt0vT.js +1 -0
- package/core/built/admin/assets/{checkbox-Dwwr_3Py.js → checkbox-CgOn_tKW.js} +1 -1
- package/core/built/admin/assets/{chunk.524.91ecb33e6afc3bd8949b.js → chunk.524.a697dee88db67fdf6021.js} +4 -4
- package/core/built/admin/assets/{chunk.582.512549909741883f960c.js → chunk.582.5ed5d124cfca9c259b9c.js} +6 -6
- package/core/built/admin/assets/{chunk.16.f76a6314082d813d67e0.js → chunk.598.7e056fabb47ce7dbd73f.js} +14 -14
- package/core/built/admin/assets/{code-editor-view-BwIdPy_k.js → code-editor-view-BYNoAoNV.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-CtkF82Fv.js → code-editor-view-K2wI-I5f.js} +1 -1
- package/core/built/admin/assets/color-picker-Ddw80xX6.js +3 -0
- package/core/built/admin/assets/color-picker-field-D9nUhvA7.js +1 -0
- package/core/built/admin/assets/combobox-w9j8PPQT.js +1 -0
- package/core/built/admin/assets/command-BfpHORBv.js +1 -0
- package/core/built/admin/assets/comments-BLmCmyCs.js +1 -0
- package/core/built/admin/assets/confirmation-context-CBM8G0Vj.js +1 -0
- package/core/built/admin/assets/{container-LiZf3XdY.js → container-BuQG1wuP.js} +1 -1
- package/core/built/admin/assets/{content-api-modal-BtpNI92-.js → content-api-modal-TXRz3U7S.js} +1 -1
- package/core/built/admin/assets/context-menu-_QMdd3kp.js +1 -0
- package/core/built/admin/assets/{copy-field-CglgRZyG.js → copy-field-6N01S07E.js} +1 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-qIBymvvf.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-DmNquxUV.js +1 -0
- package/core/built/admin/assets/{data-list-CupF7bjM.js → data-list-CA2jYGPq.js} +1 -1
- package/core/built/admin/assets/{date-range-select-JPwM0yUj.js → date-range-select-uvz_DtPh.js} +1 -1
- package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +2 -0
- package/core/built/admin/assets/{detail-page-D8lx2SdP.js → detail-page-Dnv-CdR1.js} +1 -1
- package/core/built/admin/assets/dirty-confirm-dialog-BZ1iytEd.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-BTJ2ClQu.js → disabled-sources-indicator-DMRrxsiJ.js} +1 -1
- package/core/built/admin/assets/{dist-DzcfoDJ3.js → dist-8YJUlf3B.js} +1 -1
- package/core/built/admin/assets/{dist-Cb5RSHSg.js → dist-B3keLvjj.js} +1 -1
- package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
- package/core/built/admin/assets/{dist-BT_Z-eCn.js → dist-Dgt2cr6a.js} +1 -1
- package/core/built/admin/assets/domain-BNYrt0aD.js +1 -0
- package/core/built/admin/assets/dropzone-D7nFdAe5.js +5 -0
- package/core/built/admin/assets/edit-profile-C5Asm4LA.js +1 -0
- package/core/built/admin/assets/editor-REokrzci.js +8 -0
- package/core/built/admin/assets/{embed-signup-form-modal-BFTPf04i.js → embed-signup-form-modal-Ds6mPEi5.js} +1 -1
- package/core/built/admin/assets/{empty-indicator-WB1WgT9h.js → empty-indicator-D9QdpuNX.js} +1 -1
- package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
- package/core/built/admin/assets/explore-UY10Egsp.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-D1iF_8bc.js +1 -0
- package/core/built/admin/assets/feed-81C8mJWR.js +1 -0
- package/core/built/admin/assets/{field-LmNk7wZh.js → field-Dihp5p18.js} +1 -1
- package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
- package/core/built/admin/assets/filter-sources-BO9xUPj7.js +1 -0
- package/core/built/admin/assets/filters-CRUWecfY.js +1 -0
- package/core/built/admin/assets/filters-DirqNXjH.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-CT6MxYZb.js +1 -0
- package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8eabfc1ece81b03cf7b90780d31487e8.css} +1 -1
- package/core/built/admin/assets/{ghost-403cb39badad3df411fc0ba27d220047.js → ghost-c2848508292937415f20f5a830eea59e.js} +31 -28
- package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-CQVAye17.js +1 -0
- package/core/built/admin/assets/{grid-CUciBfjf.js → grid-Dj29PY05.js} +1 -1
- package/core/built/admin/assets/growth-BWIu-6xj.js +3 -0
- package/core/built/admin/assets/growth-DU29oxeA.js +1 -0
- package/core/built/admin/assets/{heading-CRZZ2FUe.js → heading-Dh5SFVjL.js} +1 -1
- package/core/built/admin/assets/history-modal-CFd1Hvx6.js +1 -0
- package/core/built/admin/assets/html-field-CJBCLTvv.js +1 -0
- package/core/built/admin/assets/{images-C0XvUYMb.js → images-scfkWZiK.js} +1 -1
- package/core/built/admin/assets/{inbox-DZXDUt4s.js → inbox-Mcp4DXZN.js} +14 -14
- package/core/built/admin/assets/index-6MkPdCtr.js +4 -0
- package/core/built/admin/assets/index-Bn7QhcZu.css +2 -0
- package/core/built/admin/assets/index.es-CNnuNH0d.js +1 -0
- package/core/built/admin/assets/{inline-Cw-gigDW.js → inline-BWSs_VzR.js} +1 -1
- package/core/built/admin/assets/{input-group-BPAxETpQ.js → input-group-CMhe2DvY.js} +1 -1
- package/core/built/admin/assets/{input-surface-BdOe4KzO.js → input-surface-BsrIZeSF.js} +1 -1
- package/core/built/admin/assets/{integration-header-B4DyXV06.js → integration-header-Bm6IDb1A.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-DfheMXs0.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
- package/core/built/admin/assets/koenig-lexical/style.css +1 -1
- package/core/built/admin/assets/{koenig-lexical-60O_8aiO.js → koenig-lexical-DBSqAuiI.js} +3 -3
- package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +2 -0
- package/core/built/admin/assets/kpi-card-DBRMfGOW.js +1 -0
- package/core/built/admin/assets/{kpi-card-fUHZlzBX.js → kpi-card-DFafD1qq.js} +1 -1
- package/core/built/admin/assets/kpi-helpers-Cv8i8hns.js +1 -0
- package/core/built/admin/assets/kpi-tabs-BJItvLYm.js +1 -0
- package/core/built/admin/assets/{label-VIG969IC.js → label-BnSCvffu.js} +1 -1
- package/core/built/admin/assets/label-picker-KVoo91sh.js +1 -0
- package/core/built/admin/assets/layout-Bjv2Ll-5.js +1 -0
- package/core/built/admin/assets/{link-helpers-Dtq9fxQg.js → link-helpers-MVDdnFCY.js} +1 -1
- package/core/built/admin/assets/{list-page-CdsnQR9N.js → list-page-BucXJ0hi.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-DDQV6QeG.js +1 -0
- package/core/built/admin/assets/member-detail-CdJF6Agw.js +1 -0
- package/core/built/admin/assets/members-DjQbq9Z2.js +4 -0
- package/core/built/admin/assets/{modal-page-BMTzFHVO.js → modal-page-xCivTMvI.js} +1 -1
- package/core/built/admin/assets/moderation-AgCIEso4.js +1 -0
- package/core/built/admin/assets/{multi-select-combobox-CGohnsxJ.js → multi-select-combobox-BcITkUVr.js} +1 -1
- package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +1 -0
- package/core/built/admin/assets/newsletter-BMMAuvgq.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-D5ql1rud.js → newsletter-emails-DKzwafuh.js} +1 -1
- package/core/built/admin/assets/newsletters-BRBb_uQC.js +1 -0
- package/core/built/admin/assets/note-DyGHtS9a.js +1 -0
- package/core/built/admin/assets/notifications-ZgWniKna.js +1 -0
- package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
- package/core/built/admin/assets/offers-container-modal-B-E8TIWr.js +1 -0
- package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-CzIsre-d.js} +1 -1
- package/core/built/admin/assets/onboarding-qg8Akovm.js +1 -0
- package/core/built/admin/assets/{onboarding-route-Cu_ROWPn.js → onboarding-route-CaavZulg.js} +1 -1
- package/core/built/admin/assets/overview-BW8GLeYi.js +1 -0
- package/core/built/admin/assets/overview-BkXZNzaZ.js +1 -0
- package/core/built/admin/assets/{page-header-Cfds8pCg.js → page-header-De0VzKBi.js} +1 -1
- package/core/built/admin/assets/pagemenu-B5KKCpbA.js +1 -0
- package/core/built/admin/assets/pages-jgaFm69d.js +1 -0
- package/core/built/admin/assets/pages-route-CIUAQxtY.js +1 -0
- package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
- package/core/built/admin/assets/{pintura-modal-TDTrJ89x.js → pintura-modal-BZ2J9w3n.js} +1 -1
- package/core/built/admin/assets/portal-frame-B6Hr9wHc.js +1 -0
- package/core/built/admin/assets/portal-modal-D4ceei3t.js +1 -0
- package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +1 -0
- package/core/built/admin/assets/post-analytics-ot7f6pih.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-Cu3jNeFh.js → post-analytics-provider-Bo1gmYaP.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-P1_M6o-H.js → post-share-modal-C-PaYZ_e.js} +1 -1
- package/core/built/admin/assets/posts-DuGazgTb.js +1 -0
- package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +2 -0
- package/core/built/admin/assets/posts-route-D6xmS7Vf.js +1 -0
- package/core/built/admin/assets/preferences-C5YJLp74.js +5 -0
- package/core/built/admin/assets/{preview-chrome-BOVO6_PE.js → preview-chrome-BljUBlOk.js} +1 -1
- package/core/built/admin/assets/{preview-modal-MlTjP8UU.js → preview-modal-BfeFqi5y.js} +1 -1
- package/core/built/admin/assets/profile-B8mx2yAu.js +1 -0
- package/core/built/admin/assets/{radio-group-CjtmsCPl.js → radio-group-DczlLKOn.js} +1 -1
- package/core/built/admin/assets/{router-provider-CJC58zTy.js → router-provider-B0gZyiGJ.js} +1 -1
- package/core/built/admin/assets/{schemas-CRsC53ls.js → schemas-DppqmpeJ.js} +2 -2
- package/core/built/admin/assets/select-cb0t_TAJ.js +1 -0
- package/core/built/admin/assets/settings-DZqxR0Da.js +1 -0
- package/core/built/admin/assets/settings-DvfTNdHQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-sLDNedBW.js → settings-breadcrumbs-DLzbRRSt.js} +1 -1
- package/core/built/admin/assets/{shade-app-F7WxOyfk-nufqw1hd.js → shade-app-CcS3f8MZ-vwURazk8.js} +2 -2
- package/core/built/admin/assets/share-modal-D2-Bz1_I.js +1 -0
- package/core/built/admin/assets/{skeleton-D7hwjdry.js → skeleton-Ce9GgUpf.js} +1 -1
- package/core/built/admin/assets/slack-modal-DYDTUria.js +1 -0
- package/core/built/admin/assets/sort-button-VVeQjjbu.js +1 -0
- package/core/built/admin/assets/{source-icon-BPsD5ees.js → source-icon-Ck5_5Kgq.js} +1 -1
- package/core/built/admin/assets/{stack-CIfOAAVf.js → stack-B2eszIiq.js} +1 -1
- package/core/built/admin/assets/stats-Rjmk5owg.js +1 -0
- package/core/built/admin/assets/stats-view-BGGmkmgv.js +1 -0
- package/core/built/admin/assets/{step-1-CMta6YFO.js → step-1-CsaBjeN4.js} +1 -1
- package/core/built/admin/assets/{step-2-BG04nh5q.js → step-2-D2vnxOOM.js} +1 -1
- package/core/built/admin/assets/{step-3-ZnMaVIPj.js → step-3-wBoLdGEb.js} +2 -2
- package/core/built/admin/assets/{stripe-button-DDoUKN5a.js → stripe-button-CDgLz0S0.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-CSjMJetP.js +1 -0
- package/core/built/admin/assets/{switch-C9djUIMO.js → switch-CI4gLWqb.js} +1 -1
- package/core/built/admin/assets/{table-CrqQtkR2.js → table-Do6iQ83Q.js} +1 -1
- package/core/built/admin/assets/{tabs-CGisSbWq.js → tabs-D2yWLMp_.js} +1 -1
- package/core/built/admin/assets/tag-detail-BhXlOsCa.js +2 -0
- package/core/built/admin/assets/tags-ldukRApe.js +1 -0
- package/core/built/admin/assets/testimonials-modal-CtnZBJKl.js +1 -0
- package/core/built/admin/assets/{text-DU9Hv33_.js → text-BQDSDsKs.js} +1 -1
- package/core/built/admin/assets/{textarea-d9qEkMOP.js → textarea-DJ1Q5V39.js} +1 -1
- package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +1 -0
- package/core/built/admin/assets/{toggle-group-CtKZURJd.js → toggle-group-ByUJsxwD.js} +1 -1
- package/core/built/admin/assets/{topic-filter-D8JT8Tq1.js → topic-filter-BgC3g5_v.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-6PUf0idx.js → transistor-modal-Drr0SNfo.js} +1 -1
- package/core/built/admin/assets/trend-badge-6ye93YAW.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-BPuDBPja.js → unsplash-modal-p6AoQoWp.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data--opuzwcz.js → use-analytics-data-CWCMAESo.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
- package/core/built/admin/assets/use-check-theme-limit-error-Cw3WQHy8.js +2 -0
- package/core/built/admin/assets/use-filter-params-BIni9taJ.js +1 -0
- package/core/built/admin/assets/use-growth-stats-DCVG3s9w.js +1 -0
- package/core/built/admin/assets/{use-limiter-TDSNiHPB.js → use-limiter-D2WRRx0H.js} +1 -1
- package/core/built/admin/assets/{use-navigate-with-base-path-CK989MlC.js → use-navigate-with-base-path-BHPA228f.js} +1 -1
- package/core/built/admin/assets/{use-pintura-config-CnCGrDFk.js → use-pintura-config-h-J0Lvta.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-ntphvtUd.js → use-pintura-editor-BfN1UwzH.js} +1 -1
- package/core/built/admin/assets/use-post-referrers-Czg47m8s.js +1 -0
- package/core/built/admin/assets/use-reply-chain-data-ns0caK8k.js +1 -0
- package/core/built/admin/assets/{use-router-history-entry-BwfQS2mq.js → use-router-history-entry-uldn7zJO.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-DQxV42-O.js → use-setting-group-DpcuCUDg.js} +1 -1
- package/core/built/admin/assets/use-settings-navigation-CZjGrhsp.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-B2TdNX67.js → use-simple-pagination-DzJWobPR.js} +1 -1
- package/core/built/admin/assets/{use-staff-users-QrR9voeC.js → use-staff-users-Csipb-O0.js} +1 -1
- package/core/built/admin/assets/use-tinybird-query-CSnGXzZh.js +1 -0
- package/core/built/admin/assets/use-tinybird-token-DTOKRR7e.js +1 -0
- package/core/built/admin/assets/{use-unsaved-changes-guard-DpYJcj1d.js → use-unsaved-changes-guard-CBzs-KPi.js} +1 -1
- package/core/built/admin/assets/{use-url-input-WnIqckzS.js → use-url-input-DFr8lCbS.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +1 -0
- package/core/built/admin/assets/users-Cg-OeGzC.js +1 -0
- package/core/built/admin/assets/{virtual-list-D3MfPNOh.js → virtual-list-BW-padMe.js} +1 -1
- package/core/built/admin/assets/web-C1v33ehA.js +2 -0
- package/core/built/admin/assets/web-C4BKzn2T.js +1 -0
- package/core/built/admin/assets/{zapier-modal-BO6PS_Vd.js → zapier-modal-BBFfI6Ml.js} +1 -1
- package/core/built/admin/index.html +24 -30
- package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
- package/core/frontend/helpers/cancel_link.js +2 -2
- package/core/frontend/helpers/pagination.js +2 -2
- package/core/frontend/helpers/social_accounts.js +2 -2
- package/core/frontend/public/ghost.css +1 -1
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/services/rendering/renderer.js +15 -0
- package/core/frontend/services/sitemap/user-map-generator.js +9 -2
- package/core/server/adapters/storage/LocalStorageBase.js +8 -5
- package/core/server/api/endpoints/db.js +19 -5
- package/core/server/api/endpoints/index.js +6 -2
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/member-metafields.js +89 -0
- package/core/server/api/endpoints/members.js +4 -4
- package/core/server/api/endpoints/posts.js +4 -3
- package/core/server/api/endpoints/tiers-checkout-config.js +59 -0
- package/core/server/api/endpoints/utils/serializers/input/members.js +4 -7
- package/core/server/api/endpoints/utils/serializers/output/config.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +6 -2
- package/core/server/api/endpoints/utils/serializers/output/{member-custom-fields.js → member-metafields.js} +2 -4
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
- package/core/server/api/endpoints/utils/serializers/output/tiers-checkout-config.js +12 -0
- package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
- package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
- package/core/server/data/exporter/table-lists.js +3 -0
- package/core/server/data/importer/content-files.js +57 -0
- package/core/server/data/importer/handlers/image.js +2 -2
- package/core/server/data/importer/import-archive.js +131 -0
- package/core/server/data/importer/import-manager.js +18 -180
- package/core/server/data/importer/importers/content-file-importer.js +66 -4
- package/core/server/data/importer/importers/data/data-importer.js +3 -3
- package/core/server/data/migrations/utils/migrations.js +1 -1
- 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
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-38-53-add-members-custom-field-bindings-table.js +26 -0
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-48-06-add-tier-checkout-field-tables.js +35 -0
- package/core/server/data/schema/schema.js +90 -0
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-actions-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-runs-importer.js +5 -5
- package/core/server/data/seeders/importers/automations-importer.js +3 -3
- package/core/server/data/seeders/importers/comment-reports-importer.js +1 -1
- package/core/server/data/seeders/importers/comments-importer.js +1 -1
- package/core/server/data/seeders/importers/email-batches-importer.js +1 -1
- package/core/server/data/seeders/importers/email-recipients-importer.js +1 -1
- package/core/server/data/seeders/importers/emails-importer.js +1 -1
- package/core/server/data/seeders/importers/labels-importer.js +1 -1
- package/core/server/data/seeders/importers/members-click-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-created-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-feedback-importer.js +1 -1
- package/core/server/data/seeders/importers/members-importer.js +1 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-products-importer.js +1 -1
- package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +1 -1
- package/core/server/data/seeders/importers/offers-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-importer.js +1 -1
- package/core/server/data/seeders/importers/table-importer.js +2 -2
- package/core/server/data/seeders/importers/tags-importer.js +1 -1
- package/core/server/data/seeders/importers/users-importer.js +1 -1
- package/core/server/data/seeders/importers/web-mentions-importer.js +1 -1
- package/core/server/data/seeders/utils/random.js +3 -3
- package/core/server/ghost-server.js +3 -2
- package/core/server/lib/db-types/boolean.js +18 -0
- package/core/server/lib/image/image-size.js +24 -0
- package/core/server/lib/package-json/parse.js +2 -2
- package/core/server/lib/promise-pool.js +31 -0
- package/core/server/models/member.js +6 -17
- package/core/server/models/post.js +2 -2
- package/core/server/models/relations/authors.js +2 -16
- package/core/server/models/user.js +9 -2
- package/core/server/services/auth/passwordreset.js +13 -0
- package/core/server/services/automations/database-automations-repository.js +26 -26
- package/core/server/services/automations/poll.js +1 -0
- package/core/server/services/comments/comments-controller.js +30 -9
- package/core/server/services/comments/comments-service.js +55 -5
- package/core/server/services/content-import/csv/formula.js +12 -0
- package/core/server/services/content-import/csv/index.js +6 -1
- package/core/server/services/content-import/csv/parse.js +27 -6
- package/core/server/services/content-import/csv/serialize.js +13 -0
- package/core/server/services/content-import/import/assets.js +74 -0
- package/core/server/services/content-import/import/completion-email.js +164 -0
- package/core/server/services/content-import/import/errors-file.js +36 -0
- package/core/server/services/content-import/import/importer.js +204 -54
- package/core/server/services/content-import/import/local-media-url.js +63 -0
- package/core/server/services/content-import/import/media.js +254 -0
- package/core/server/services/content-import/import/post-data.js +1 -1
- package/core/server/services/content-import/import/post-link.js +11 -0
- package/core/server/services/content-import/import/post-repository.js +100 -0
- package/core/server/services/content-import/import/reader.js +9 -2
- package/core/server/services/content-import/import/relations.js +149 -0
- package/core/server/services/content-import/import/report.js +34 -0
- package/core/server/services/content-import/import/row.js +11 -2
- package/core/server/services/content-import/import/schema.js +30 -0
- package/core/server/services/content-import/import/source.js +125 -0
- package/core/server/services/content-import/import/staged-file.js +29 -0
- package/core/server/services/content-import/import/store.js +25 -2
- package/core/server/services/content-import/index.js +58 -34
- package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
- package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
- package/core/server/services/custom-redirects/redirects-service.js +1 -1
- package/core/server/services/custom-redirects/validation.js +1 -1
- package/core/server/services/email-address/email-address-parser.js +30 -38
- package/core/server/services/email-address/email-address-service.js +37 -4
- package/core/server/services/email-analytics/email-analytics-service.js +8 -1
- package/core/server/services/email-analytics/index.js +2 -7
- package/core/server/services/email-analytics/lib/queries.js +7 -1
- package/core/server/services/email-service/batch-sending-service.js +0 -4
- package/core/server/services/email-service/email-renderer.js +14 -4
- package/core/server/services/email-service/email-service-wrapper.js +0 -9
- package/core/server/services/email-service/mailgun-email-provider.js +20 -9
- package/core/server/services/gift-links/schema.js +3 -3
- package/core/server/services/gifts/CONTEXT.md +1 -1
- package/core/server/services/gifts/constants.js +1 -1
- package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
- package/core/server/services/gifts/gift-bookshelf-repository.js +2 -2
- package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +18 -9
- package/core/server/services/gifts/gift-delivery-schema.js +5 -5
- package/core/server/services/gifts/gift-delivery-service.js +4 -0
- package/core/server/services/gifts/gift-email-service.js +0 -1
- package/core/server/services/gifts/gift-schema.js +10 -10
- package/core/server/services/gifts/gift-service.js +4 -2
- package/core/server/services/gifts/index.js +3 -3
- package/core/server/services/gifts/jobs/index.js +4 -4
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
- package/core/server/services/invitations/accept.js +4 -2
- package/core/server/services/jobs/job-service.js +0 -9
- package/core/server/services/jobs-service/register-job-handlers.js +25 -15
- package/core/server/services/lib/mailgun-client.js +44 -8
- package/core/server/services/mail/ghost-mailer.js +4 -4
- package/core/server/services/media-inliner/external-media-inliner-job.js +12 -0
- package/core/server/services/media-inliner/external-media-inliner.js +103 -29
- package/core/server/services/media-inliner/service.js +13 -41
- package/core/server/services/media-inliner/types.js +2 -0
- package/core/server/services/member-welcome-emails/service.js +7 -7
- package/core/server/services/members/api.js +1 -0
- package/core/server/services/members/import-export/csv/parse.js +0 -7
- package/core/server/services/members/import-export/export/exporter.js +4 -6
- package/core/server/services/members/import-export/import/completion-email.js +2 -5
- package/core/server/services/members/import-export/import/importer.js +21 -5
- package/core/server/services/members/import-export/import/row.js +0 -3
- package/core/server/services/members/import-export/import/stripe-utils.js +6 -1
- package/core/server/services/members/import-export/index.js +10 -12
- package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
- package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
- package/core/server/services/members/jobs/index.js +48 -24
- package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
- package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
- package/core/server/services/members/members-api/members-api.js +9 -6
- package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
- package/core/server/services/members/members-api/services/member-bread-service.js +53 -63
- package/core/server/services/members/members-api/services/payments-service.js +47 -1
- package/core/server/services/members/service.js +1 -5
- package/core/server/services/members-custom-fields/actions.js +27 -1
- package/core/server/services/members-custom-fields/bindings-service.js +120 -0
- package/core/server/services/members-custom-fields/codec.js +3 -4
- package/core/server/services/members-custom-fields/definitions-service.js +165 -22
- package/core/server/services/members-custom-fields/filter.js +75 -93
- package/core/server/services/members-custom-fields/index.js +11 -4
- package/core/server/services/members-custom-fields/key.js +2 -1
- package/core/server/services/members-custom-fields/models.js +1 -1
- package/core/server/services/members-custom-fields/queries.js +5 -0
- package/core/server/services/members-custom-fields/schema.js +37 -6
- package/core/server/services/members-custom-fields/serializers.js +3 -5
- package/core/server/services/members-custom-fields/values-service.js +65 -31
- package/core/server/services/mentions/mention-controller.js +23 -21
- package/core/server/services/mentions/process-webmention-job.js +16 -0
- package/core/server/services/mentions/service.js +8 -4
- package/core/server/services/public-config/config.js +7 -0
- package/core/server/services/route-settings/validation-errors.js +1 -1
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/settings/settings-service.js +1 -8
- package/core/server/services/settings-helpers/settings-helpers.js +4 -6
- package/core/server/services/slack-ping/slack-ping-service.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +2 -2
- package/core/server/services/stripe/config.js +14 -8
- package/core/server/services/stripe/service.js +10 -1
- package/core/server/services/stripe/services/checkout/completed-session.js +68 -0
- package/core/server/services/stripe/services/checkout/session-options.js +100 -0
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +71 -1
- package/core/server/services/stripe/stripe-api.js +31 -0
- package/core/server/services/stripe/stripe-service.js +26 -9
- package/core/server/services/stripe/webhook-controller.js +10 -0
- package/core/server/services/tier-checkout-config/codec.js +99 -0
- package/core/server/services/tier-checkout-config/index.js +26 -0
- package/core/server/services/tier-checkout-config/models.js +63 -0
- package/core/server/services/tier-checkout-config/queries.js +80 -0
- package/core/server/services/tier-checkout-config/schema.js +33 -0
- package/core/server/services/tier-checkout-config/serializers.js +128 -0
- package/core/server/services/tier-checkout-config/service.js +304 -0
- package/core/server/services/tiers/service.js +21 -0
- package/core/server/services/update-check/index.js +13 -13
- package/core/server/services/update-check/jobs/update-check-job.js +7 -0
- package/core/server/services/update-check/update-check-service.js +2 -2
- package/core/server/web/admin/controller.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +34 -20
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/server/web/parent/app.js +5 -0
- package/core/server/web/parent/middleware/filter-query-parameters.js +94 -0
- package/core/server/web/parent/middleware/index.js +1 -0
- package/core/server/web/query-parameter-policy/index.js +9 -0
- package/core/server/web/query-parameter-policy/policy.json +58 -0
- package/core/server/web/query-parameter-policy/schema.js +38 -0
- package/core/shared/config/defaults.json +3 -0
- package/core/shared/config/env/config.testing-mysql.json +4 -1
- package/core/shared/config/env/config.testing.json +3 -0
- package/core/shared/config/overrides.json +4 -2
- package/core/shared/labs.js +9 -15
- package/package.json +25 -25
- package/pnpm-lock.yaml +741 -108
- package/pnpm-workspace.yaml +12 -9
- package/tsconfig.json +2 -3
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/core/built/admin/assets/Combination-gnJO3vIw.js +0 -41
- package/core/built/admin/assets/account-migration-8qTEI1i1.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-DQ5CwLma.js +0 -1284
- package/core/built/admin/assets/activitypub/label-B72sfvEU.js +0 -16
- package/core/built/admin/assets/activitypub/layout-CXajhRsL.js +0 -1
- package/core/built/admin/assets/activitypub/settings-71gCm3SO.js +0 -315
- package/core/built/admin/assets/add-integration-modal--YpTq3c0.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-C8Ix2hol.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-CScuNFz4.js +0 -1
- package/core/built/admin/assets/alert-dialog-wEbVxANM.js +0 -1
- package/core/built/admin/assets/analytics-Cnji45H5.js +0 -1
- package/core/built/admin/assets/app-utils-D2Wp7ZSS.js +0 -1
- package/core/built/admin/assets/audience-DTLMXAFH.js +0 -1
- package/core/built/admin/assets/automations-BWpRCZ_f.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-DHOpQxff.js +0 -1
- package/core/built/admin/assets/chart-helpers-B3OqQ6Ez.js +0 -1
- package/core/built/admin/assets/color-picker-Bmo1r9Fc.js +0 -3
- package/core/built/admin/assets/color-picker-field-DywowPhF.js +0 -1
- package/core/built/admin/assets/combobox-C4QCh6Yw.js +0 -1
- package/core/built/admin/assets/command-DcLbSifC.js +0 -1
- package/core/built/admin/assets/comments-Bxy_3WyE.js +0 -1
- package/core/built/admin/assets/confirmation-context-CLFHhyyH.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-D0PXoSnP.js +0 -10
- package/core/built/admin/assets/custom-integration-modal-DCIHKN33.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-CZ-PkW7o.js +0 -23
- package/core/built/admin/assets/dialog-Db7c164G.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-CmARpZQS.js +0 -1
- package/core/built/admin/assets/dist-QLZuGq7o2.js +0 -1
- package/core/built/admin/assets/domain-D0SAtc-T.js +0 -1
- package/core/built/admin/assets/dropzone-CaPOeDRZ.js +0 -5
- package/core/built/admin/assets/edit-profile-By7v5XW5.js +0 -1
- package/core/built/admin/assets/editor-COXn4Atu.js +0 -8
- package/core/built/admin/assets/explore-BsNYrubV.js +0 -1
- package/core/built/admin/assets/feed-8RR1O1bK.js +0 -1
- package/core/built/admin/assets/filter-sources-4C0kF1NU.js +0 -1
- package/core/built/admin/assets/filters-CV5jCb9n.js +0 -1
- package/core/built/admin/assets/filters-Dmr0PoqU.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-OCzSaL3B.js +0 -1
- package/core/built/admin/assets/gift-link-modal-0mzjn5oF.js +0 -1
- package/core/built/admin/assets/growth-55vWlGT_.js +0 -1
- package/core/built/admin/assets/growth-DA5AluYw.js +0 -3
- package/core/built/admin/assets/history-modal-G1s5hHz7.js +0 -1
- package/core/built/admin/assets/html-field-BegxsWpw.js +0 -1
- package/core/built/admin/assets/index-CDP9tIOk.js +0 -4
- package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
- package/core/built/admin/assets/index.es-BMUCj9TR.js +0 -1
- package/core/built/admin/assets/input-UHrKuvBT.js +0 -1
- package/core/built/admin/assets/invite-user-modal-BrAkxEO3.js +0 -1
- package/core/built/admin/assets/koenig-loader-DwVY_V-H.js +0 -1
- package/core/built/admin/assets/kpi-card-LERqWBoY.js +0 -1
- package/core/built/admin/assets/kpi-helpers-DB3ouhYF.js +0 -1
- package/core/built/admin/assets/kpi-tabs-BGvtMD8S.js +0 -1
- package/core/built/admin/assets/label-picker-BBWykUeA.js +0 -1
- package/core/built/admin/assets/layout-Di5K7doM.js +0 -1
- package/core/built/admin/assets/loading-indicator-SFTqyYVE.js +0 -1
- package/core/built/admin/assets/member-custom-fields-gOv6Hoh3.js +0 -1
- package/core/built/admin/assets/member-detail-D7mZDDp3.js +0 -1
- package/core/built/admin/assets/member-route-0Aa52Ntz.js +0 -1
- package/core/built/admin/assets/members-DvZG9t69.js +0 -2
- package/core/built/admin/assets/moderation-DzzPLDyl.js +0 -1
- package/core/built/admin/assets/navigation-modal-CI8Bx5vn.js +0 -1
- package/core/built/admin/assets/newsletter-Bg3cq5U-.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-DQU3r4g2.js +0 -1
- package/core/built/admin/assets/newsletters-DMDfZbMS.js +0 -1
- package/core/built/admin/assets/note-i8k6pjaw.js +0 -1
- package/core/built/admin/assets/notifications-psA_2Ph4.js +0 -1
- package/core/built/admin/assets/offers-container-modal-BWfjSdyg.js +0 -1
- package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
- package/core/built/admin/assets/overview-C-6xOLaf.js +0 -1
- package/core/built/admin/assets/overview-DaLhpRrC.js +0 -1
- package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
- package/core/built/admin/assets/popover-CFgqFDvC.js +0 -1
- package/core/built/admin/assets/portal-frame-DjBF-uy2.js +0 -1
- package/core/built/admin/assets/portal-modal-Cf0pBAk8.js +0 -1
- package/core/built/admin/assets/post-analytics-Buz1COoY.js +0 -1
- package/core/built/admin/assets/post-analytics-header-B-P0eFnT.js +0 -1
- package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
- package/core/built/admin/assets/preferences-Cq1aOiLy.js +0 -5
- package/core/built/admin/assets/profile-D6dHYf2o.js +0 -1
- package/core/built/admin/assets/select-D_RQaXgt.js +0 -1
- package/core/built/admin/assets/settings-B5hJKZ5C.js +0 -1
- package/core/built/admin/assets/settings-DIo_F-MF.js +0 -4
- package/core/built/admin/assets/share-modal-CLBfj377.js +0 -1
- package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
- package/core/built/admin/assets/slack-modal-Bt9I_tEo.js +0 -1
- package/core/built/admin/assets/sort-button-DMBXF7tP.js +0 -1
- package/core/built/admin/assets/stats-B_YkF7bp.js +0 -1
- package/core/built/admin/assets/stats-view-BOCp9RG8.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-BeSukI8q.js +0 -1
- package/core/built/admin/assets/tag-detail-2a3BfzTW.js +0 -2
- package/core/built/admin/assets/tags-Dp2_8UzL.js +0 -1
- package/core/built/admin/assets/testimonials-modal-C9nrypNo.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-CxRwgTPR.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-C9-QhGdV.js +0 -1
- package/core/built/admin/assets/use-filter-params-CSpAvgD2.js +0 -1
- package/core/built/admin/assets/use-growth-stats-BOmoPDHJ.js +0 -1
- package/core/built/admin/assets/use-post-referrers-CbXLFIex.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-JYFKSO83.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BBzro2-V.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-D2Aeoty5.js +0 -39
- package/core/built/admin/assets/use-tinybird-token-DBuwMmln.js +0 -1
- package/core/built/admin/assets/user-detail-modal-59mBJqxN.js +0 -1
- package/core/built/admin/assets/users-5msE1WMs.js +0 -1
- package/core/built/admin/assets/web-DUuX7nE2.js +0 -2
- package/core/built/admin/assets/web-N8ucs2aG.js +0 -1
- package/core/server/api/endpoints/member-custom-fields.js +0 -100
- package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
- package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
- package/core/server/services/update-check/run-update-check.js +0 -63
- /package/core/server/lib/{db-date.js → db-types/date.js} +0 -0
|
@@ -9,16 +9,13 @@ 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.
|
|
15
17
|
const CUSTOM_FIELDS_EDITED_ACTION = 'custom_fields_edited';
|
|
16
18
|
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {object} ILabsService
|
|
19
|
-
* @prop {(key: string) => boolean} isSet
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
19
|
/**
|
|
23
20
|
* @typedef {object} IEmailService
|
|
24
21
|
* @prop {(data: {email: string, requestedType: string}) => Promise<any>} sendEmailWithMagicLink
|
|
@@ -43,7 +40,6 @@ module.exports = class MemberBREADService {
|
|
|
43
40
|
* @param {object} deps
|
|
44
41
|
* @param {import('../repositories/member-repository')} deps.memberRepository
|
|
45
42
|
* @param {import('../../../offers/application/offers-api')} deps.offersAPI
|
|
46
|
-
* @param {ILabsService} deps.labsService
|
|
47
43
|
* @param {IEmailService} deps.emailService
|
|
48
44
|
* @param {IStripeService} deps.stripeService
|
|
49
45
|
* @param {import('../../../member-attribution/member-attribution-service')} deps.memberAttributionService
|
|
@@ -52,10 +48,10 @@ module.exports = class MemberBREADService {
|
|
|
52
48
|
* @param {import('./next-payment-calculator')} deps.nextPaymentCalculator
|
|
53
49
|
* @param {IGiftsModule} deps.giftService
|
|
54
50
|
* @param {import('../../../members-custom-fields/values-service').CustomFieldValuesService} deps.customFieldValues Required: boot builds it before the members service
|
|
51
|
+
* @param {import('../../../members-custom-fields/definitions-service').CustomFieldDefinitionsService} deps.customFieldDefinitions Required: boot builds it before the members service
|
|
55
52
|
*/
|
|
56
53
|
constructor({
|
|
57
54
|
memberRepository,
|
|
58
|
-
labsService,
|
|
59
55
|
emailService,
|
|
60
56
|
stripeService,
|
|
61
57
|
offersAPI,
|
|
@@ -66,13 +62,12 @@ module.exports = class MemberBREADService {
|
|
|
66
62
|
commentsService,
|
|
67
63
|
giftService,
|
|
68
64
|
customFieldValues,
|
|
65
|
+
customFieldDefinitions,
|
|
69
66
|
}) {
|
|
70
67
|
this.offersAPI = offersAPI;
|
|
71
68
|
/** @private */
|
|
72
69
|
this.memberRepository = memberRepository;
|
|
73
70
|
/** @private */
|
|
74
|
-
this.labsService = labsService;
|
|
75
|
-
/** @private */
|
|
76
71
|
this.emailService = emailService;
|
|
77
72
|
/** @private */
|
|
78
73
|
this.stripeService = stripeService;
|
|
@@ -90,31 +85,25 @@ module.exports = class MemberBREADService {
|
|
|
90
85
|
this.giftService = giftService;
|
|
91
86
|
/** @private */
|
|
92
87
|
this.customFieldValues = customFieldValues;
|
|
88
|
+
/** @private */
|
|
89
|
+
this.customFieldDefinitions = customFieldDefinitions;
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* `custom_fields` key entirely, keeping a pre-feature member payload identical.
|
|
93
|
+
* Metafields are extra fields a publisher can define on member records, such as a shoe
|
|
94
|
+
* size or a delivery address. Their values live in their own table, so they are fetched
|
|
95
|
+
* here rather than loaded alongside the member.
|
|
100
96
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* protects consumers that predate the feature — an include would outlive it
|
|
107
|
-
* and become permanent API surface.
|
|
108
|
-
*
|
|
109
|
-
* Browse is the opposite — opt-in via `include=custom_fields`, exactly how
|
|
110
|
-
* `products`/`tiers` already behave: a read always carries them, a list only
|
|
111
|
-
* when asked. Read and browse must stay format-identical, so a browse that
|
|
112
|
-
* asks gets the same key a read gives unasked.
|
|
97
|
+
* Returns null when the site has defined no fields, which tells the caller to leave the
|
|
98
|
+
* `metafields` key off the member payload rather than send an empty object: a key added to
|
|
99
|
+
* an API response cannot be withdrawn without breaking whoever started reading it, and
|
|
100
|
+
* most sites have never defined a field, so those sites keep the payload they had before
|
|
101
|
+
* this feature existed.
|
|
113
102
|
* @param {string[]} memberIds
|
|
114
103
|
* @returns {Promise<Map<string, Record<string, unknown>> | null>}
|
|
115
104
|
*/
|
|
116
105
|
async fetchCustomFieldValues(memberIds) {
|
|
117
|
-
if (!this.
|
|
106
|
+
if (!(await this.customFieldDefinitions.hasAnyActive())) {
|
|
118
107
|
return null;
|
|
119
108
|
}
|
|
120
109
|
|
|
@@ -392,7 +381,16 @@ module.exports = class MemberBREADService {
|
|
|
392
381
|
}
|
|
393
382
|
}
|
|
394
383
|
|
|
395
|
-
|
|
384
|
+
/**
|
|
385
|
+
* @param {object} data
|
|
386
|
+
* @param {object} [options]
|
|
387
|
+
* @param {boolean} [options.withCustomFields] Pass false to leave custom field values
|
|
388
|
+
* off the result. Ghost calls this method to identify a signed-in reader on every page
|
|
389
|
+
* view of a themed site, and that caller renders the member through a fixed list of
|
|
390
|
+
* fields that has never included custom ones. Fetching them there costs two database
|
|
391
|
+
* queries per page view whose results are then thrown away.
|
|
392
|
+
*/
|
|
393
|
+
async read(data, { withCustomFields = true, ...options } = {}) {
|
|
396
394
|
const defaultWithRelated = [
|
|
397
395
|
'labels',
|
|
398
396
|
'stripeSubscriptions',
|
|
@@ -453,43 +451,25 @@ module.exports = class MemberBREADService {
|
|
|
453
451
|
const unsubscribeUrl = this.settingsHelpers.createUnsubscribeUrl(member.uuid);
|
|
454
452
|
member.unsubscribe_url = unsubscribeUrl;
|
|
455
453
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
454
|
+
if (withCustomFields) {
|
|
455
|
+
const customFields = await this.fetchCustomFieldValues([member.id]);
|
|
456
|
+
if (customFields) {
|
|
457
|
+
member.metafields = customFields.get(member.id) ?? {};
|
|
458
|
+
}
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
return member;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
-
/**
|
|
465
|
-
* @private
|
|
466
|
-
* The write-side flag gate, paired with `fetchCustomFieldValues` on the read
|
|
467
|
-
* side. The schema declares `custom_fields` for every site, so the key arrives
|
|
468
|
-
* whether or not the feature is on and this is what decides it goes no further.
|
|
469
|
-
* @param {object} data
|
|
470
|
-
*/
|
|
471
|
-
dropCustomFieldsWhenDisabled(data) {
|
|
472
|
-
if (!this.labsService.isSet('membersCustomFields')) {
|
|
473
|
-
delete data.custom_fields;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
464
|
async add(data, options) {
|
|
478
|
-
this.
|
|
479
|
-
|
|
480
|
-
// Values cannot be set on create, only on a subsequent edit. `namesValues`
|
|
481
|
-
// both judges the body and rejects a malformed one, so a body refused here is
|
|
482
|
-
// refused on edit for the same reason.
|
|
483
|
-
if (this.customFieldValues.namesValues(data.custom_fields)) {
|
|
465
|
+
if (this.customFieldValues.namesValues(this.customFieldValues.unwrapWire(data.metafields))) {
|
|
484
466
|
throw new errors.ValidationError({
|
|
485
467
|
message: tpl(messages.customFieldsOnAdd),
|
|
486
|
-
property: '
|
|
468
|
+
property: 'metafields',
|
|
487
469
|
});
|
|
488
470
|
}
|
|
489
471
|
|
|
490
|
-
|
|
491
|
-
// an absent key or one naming no values gets this far.
|
|
492
|
-
delete data.custom_fields;
|
|
472
|
+
delete data.metafields;
|
|
493
473
|
|
|
494
474
|
if (!this.stripeService.configured && (data.comped || data.stripe_customer_id)) {
|
|
495
475
|
const property = data.comped ? 'comped' : 'stripe_customer_id';
|
|
@@ -585,15 +565,9 @@ module.exports = class MemberBREADService {
|
|
|
585
565
|
async edit(data, options) {
|
|
586
566
|
delete data.last_seen_at;
|
|
587
567
|
|
|
588
|
-
this.
|
|
589
|
-
|
|
590
|
-
// Values live in their own table, so they come off the member data before
|
|
591
|
-
// the repository sees it — `custom_fields` is not a member column. The gate
|
|
592
|
-
// above has already dropped it when the feature is off, so by this point its
|
|
593
|
-
// presence is the signal to write.
|
|
594
|
-
const customFields = data.custom_fields;
|
|
568
|
+
const customFields = this.customFieldValues.unwrapWire(data.metafields);
|
|
595
569
|
const writeCustomFields = customFields !== undefined;
|
|
596
|
-
delete data.
|
|
570
|
+
delete data.metafields;
|
|
597
571
|
|
|
598
572
|
// Plan (which validates) before the member is touched, so a bad value 422s
|
|
599
573
|
// here rather than after the member edit has been applied — and keep the
|
|
@@ -654,7 +628,23 @@ module.exports = class MemberBREADService {
|
|
|
654
628
|
}
|
|
655
629
|
|
|
656
630
|
if (plannedCustomFields) {
|
|
657
|
-
|
|
631
|
+
// Every value reaching here was typed into the Admin API, so the writer is
|
|
632
|
+
// whoever made the request — the same pair the action log records, so the two
|
|
633
|
+
// agree about who did it rather than one saying only that it was "admin".
|
|
634
|
+
//
|
|
635
|
+
// The only route to this branch is the authenticated Admin API, so an anonymous
|
|
636
|
+
// request is a mistake somewhere upstream rather than a writer to invent a name
|
|
637
|
+
// for. Refusing keeps every stored writer resolvable.
|
|
638
|
+
const context = options.context || {};
|
|
639
|
+
if (!context.integration && !context.user) {
|
|
640
|
+
throw new errors.IncorrectUsageError({
|
|
641
|
+
message: tpl(messages.customFieldsWithoutWriter),
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
const writtenBy = context.integration
|
|
645
|
+
? { type: 'integration', id: context.integration.id }
|
|
646
|
+
: { type: 'user', id: context.user };
|
|
647
|
+
await this.customFieldValues.applyWrite(model.id, plannedCustomFields, { writtenBy });
|
|
658
648
|
|
|
659
649
|
// Custom fields aren't a member column or relation, so an edit touching
|
|
660
650
|
// only them leaves `model._changed` empty and the save fires nothing.
|
|
@@ -815,7 +805,7 @@ module.exports = class MemberBREADService {
|
|
|
815
805
|
delete member.products;
|
|
816
806
|
}
|
|
817
807
|
if (customFieldsByMember) {
|
|
818
|
-
member.
|
|
808
|
+
member.metafields = customFieldsByMember.get(model.id) ?? {};
|
|
819
809
|
}
|
|
820
810
|
member.email_suppression = {
|
|
821
811
|
suppressed: bulkSuppressionData[index].suppressed || !!model.get('email_disabled'),
|
|
@@ -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
|
-
|
|
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('stripeCheckoutCollection') || !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]
|
|
@@ -7,7 +7,6 @@ const MembersConfigProvider = require('./members-config-provider');
|
|
|
7
7
|
const { makeImporter, makeExporter } = require('./import-export');
|
|
8
8
|
const { resolveInlineThreshold } = require('./import-export/config');
|
|
9
9
|
const MembersStats = require('./stats/members-stats');
|
|
10
|
-
const memberJobs = require('./jobs');
|
|
11
10
|
const logging = require('@tryghost/logging');
|
|
12
11
|
const urlUtils = require('../../../shared/url-utils').default;
|
|
13
12
|
const settingsCache = require('../../../shared/settings-cache');
|
|
@@ -29,7 +28,7 @@ const messages = {
|
|
|
29
28
|
sslRequiredForStripe:
|
|
30
29
|
'Cannot run Ghost without SSL when Stripe is connected. Please update your url config to use "https://".',
|
|
31
30
|
remoteWebhooksInDevelopment:
|
|
32
|
-
'Cannot use remote webhooks in development. See https://ghost.org/
|
|
31
|
+
'Cannot use remote webhooks in development. See https://docs.ghost.org/webhooks/#stripe-webhooks for developing with Stripe.',
|
|
33
32
|
};
|
|
34
33
|
|
|
35
34
|
const ghostMailer = new GhostMailer();
|
|
@@ -216,9 +215,6 @@ module.exports = {
|
|
|
216
215
|
);
|
|
217
216
|
}
|
|
218
217
|
}
|
|
219
|
-
|
|
220
|
-
// Schedule daily cron job to clean expired comp subs
|
|
221
|
-
memberJobs.scheduleExpiredCompCleanupJob();
|
|
222
218
|
},
|
|
223
219
|
contentGating: require('./content-gating'),
|
|
224
220
|
|
|
@@ -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(
|
|
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
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
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 { CUSTOM_NAMESPACE } = require('@tryghost/custom-field-types/identity');
|
|
12
|
+
const BINDINGS_TABLE = 'members_custom_field_bindings';
|
|
13
|
+
/**
|
|
14
|
+
* Where a source sends what it collected: a `port` is the name that source uses for a
|
|
15
|
+
* thing, and the binding resolves it to one of the publisher's fields.
|
|
16
|
+
*/
|
|
17
|
+
class CustomFieldBindingsService {
|
|
18
|
+
knex;
|
|
19
|
+
values;
|
|
20
|
+
constructor({ knex, values }) {
|
|
21
|
+
this.knex = knex;
|
|
22
|
+
this.values = values;
|
|
23
|
+
}
|
|
24
|
+
async bind(db, productId, port, customFieldKey, now) {
|
|
25
|
+
const existing = await db(BINDINGS_TABLE).where({ product_id: productId, port }).first();
|
|
26
|
+
if (existing?.custom_field_key === customFieldKey) {
|
|
27
|
+
await db(BINDINGS_TABLE).where('id', existing.id).update({ updated_at: now });
|
|
28
|
+
return existing.id;
|
|
29
|
+
}
|
|
30
|
+
if (existing) {
|
|
31
|
+
await db(BINDINGS_TABLE).where('id', existing.id).del();
|
|
32
|
+
}
|
|
33
|
+
const bindingId = new bson_objectid_1.default().toHexString();
|
|
34
|
+
await db(BINDINGS_TABLE).insert({
|
|
35
|
+
id: bindingId,
|
|
36
|
+
product_id: productId,
|
|
37
|
+
port,
|
|
38
|
+
custom_field_key: customFieldKey,
|
|
39
|
+
created_at: now,
|
|
40
|
+
updated_at: now,
|
|
41
|
+
});
|
|
42
|
+
return bindingId;
|
|
43
|
+
}
|
|
44
|
+
/** Stops the writing. Whatever hangs off the binding cascades with it. */
|
|
45
|
+
async remove(db, productId, port) {
|
|
46
|
+
await db(BINDINGS_TABLE).where({ product_id: productId, port }).del();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Values arrive in the order they are to be applied: where two land in one field, the
|
|
50
|
+
* last of them is what the field holds.
|
|
51
|
+
*/
|
|
52
|
+
async writeCollected(memberId, productId, collected) {
|
|
53
|
+
for (const { port, value } of collected) {
|
|
54
|
+
const destination = await this.resolve(productId, port);
|
|
55
|
+
if (!destination) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
await this.writeOne(memberId, destination, value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async writeOne(memberId, into, value) {
|
|
62
|
+
let planned;
|
|
63
|
+
try {
|
|
64
|
+
planned = await this.values.planWrite({ [`${CUSTOM_NAMESPACE}.${into.key}`]: value });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logging_1.default.warn({
|
|
68
|
+
event: { name: 'members.custom_fields.collected_value_rejected' },
|
|
69
|
+
err,
|
|
70
|
+
memberId,
|
|
71
|
+
customFieldKey: into.key,
|
|
72
|
+
}, 'A collected value could not be saved');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
await this.values.applyWrite(memberId, planned, {
|
|
77
|
+
writtenBy: { type: 'binding', id: into.bindingId },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
logging_1.default.error({
|
|
82
|
+
event: { name: 'members.custom_fields.collected_value_write_failed' },
|
|
83
|
+
err,
|
|
84
|
+
memberId,
|
|
85
|
+
customFieldKey: into.key,
|
|
86
|
+
bindingId: into.bindingId,
|
|
87
|
+
}, 'Failed to store a collected custom field value');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async resolve(productId, port) {
|
|
91
|
+
const row = await this.knex(BINDINGS_TABLE)
|
|
92
|
+
.join(FIELDS_TABLE, `${BINDINGS_TABLE}.custom_field_key`, `${FIELDS_TABLE}.key`)
|
|
93
|
+
.where(`${BINDINGS_TABLE}.product_id`, productId)
|
|
94
|
+
.where(`${BINDINGS_TABLE}.port`, port)
|
|
95
|
+
// An archived destination is still where this goes, and still not somewhere a value
|
|
96
|
+
// can land, so the write drops rather than waiting.
|
|
97
|
+
.where(`${FIELDS_TABLE}.status`, schema_1.FIELD_STATUS.active)
|
|
98
|
+
.select(`${BINDINGS_TABLE}.id as binding_id`, `${FIELDS_TABLE}.key`, `${FIELDS_TABLE}.type`)
|
|
99
|
+
.first();
|
|
100
|
+
if (!row) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// Decoded rather than trusted: a join is a read boundary, and `type` is what decides
|
|
104
|
+
// how the collected value is read. Unreadable counts as unresolved rather than
|
|
105
|
+
// throwing, so one bad row skips its value the way an unbound port does instead of
|
|
106
|
+
// failing everything else the same checkout collected.
|
|
107
|
+
const bound = schema_1.DbBoundField.safeParse(row);
|
|
108
|
+
if (!bound.success) {
|
|
109
|
+
logging_1.default.warn({
|
|
110
|
+
event: { name: 'members.custom_fields.binding_unreadable' },
|
|
111
|
+
err: bound.error,
|
|
112
|
+
productId,
|
|
113
|
+
port,
|
|
114
|
+
}, 'A binding could not be read');
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return { bindingId: bound.data.binding_id, key: bound.data.key, type: bound.data.type };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.CustomFieldBindingsService = CustomFieldBindingsService;
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.customFieldCodec = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const identity_1 = require("@tryghost/custom-field-types/identity");
|
|
5
6
|
const case_keys_1 = require("../../lib/case-keys");
|
|
6
7
|
const schema_1 = require("./schema");
|
|
7
8
|
const models_1 = require("./models");
|
|
8
|
-
// Maps a members_custom_fields row to/from the domain CustomField (snake_case to
|
|
9
|
-
// camelCase; DbDate decoding happens in DbCustomField).
|
|
10
9
|
exports.customFieldCodec = zod_1.z.codec(schema_1.DbCustomField, models_1.CustomField, {
|
|
11
10
|
// DbCustomField validates `type` as the field-type enum, so the decoded row
|
|
12
11
|
// already carries a FieldType and camelKeys preserves it — no cast needed.
|
|
13
|
-
decode: (row) => (0, case_keys_1.camelKeys)(row),
|
|
14
|
-
encode: (field) => (0, case_keys_1.snakeKeys)(field),
|
|
12
|
+
decode: (row) => ({ ...(0, case_keys_1.camelKeys)(row), namespace: identity_1.CUSTOM_NAMESPACE }),
|
|
13
|
+
encode: ({ namespace: _namespace, ...field }) => (0, case_keys_1.snakeKeys)(field),
|
|
15
14
|
});
|