ghost 6.61.0 → 6.63.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-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-limit-service-0.0.0.tgz +0 -0
- package/components/tryghost-metafield-types-0.0.0.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/content/themes/source/partials/post-card.hbs +1 -1
- package/core/boot.js +61 -27
- 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/{about-Bve7IJS2.js → about-C1C3s5-g.js} +1 -1
- package/core/built/admin/assets/accordion-CxGsDKwa.js +1 -0
- package/core/built/admin/assets/account-migration-CbUhW6gk.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-C-aej4x_.js → account-migration-KA6Jjf9L.js} +16 -16
- package/core/built/admin/assets/activitypub/activitypub.js +9 -9
- package/core/built/admin/assets/activitypub/{avatar-DLPL3Abs.js → avatar-DVJF5bxP.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-C9iDY-QV.js → avatar-flipboard-csQaNeYK.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-BXkA5mqV.js → bluesky-sharing-C7nTDMvw.js} +37 -37
- package/core/built/admin/assets/activitypub/{content-formatters-CHcyMFk4.js → content-formatters-CLcjaBwV.js} +4 -4
- package/core/built/admin/assets/activitypub/{copy-DIAOQoCZ.js → copy-DcOh8kXw.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-B3ihkPde.js → domain-fZ-X8i4-.js} +121 -121
- package/core/built/admin/assets/activitypub/{edit-profile-DsZM3rTX.js → edit-profile-BqMLQV88.js} +40 -40
- package/core/built/admin/assets/activitypub/{explore-BodNrZgR.js → explore-B4yPoP8E.js} +27 -27
- package/core/built/admin/assets/activitypub/{feed-PwH1iJ84.js → feed-BRC_mvAe.js} +51 -51
- package/core/built/admin/assets/activitypub/{field-D06-kmOJ.js → field-COacNgOt.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-Bm3WxYN4.js → hash-C7u-irQQ.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-BW-yvUoN.js +1284 -0
- package/core/built/admin/assets/activitypub/{inbox-d8oJRTf4.js → inbox-CbTTdDQ3.js} +2 -2
- package/core/built/admin/assets/activitypub/label-kPMmYI7l.js +16 -0
- package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-D1Yapsod.js → moderation-CuidJzc0.js} +91 -91
- package/core/built/admin/assets/activitypub/{note-C5zjqq5C.js → note-CAMRRSqD.js} +24 -24
- package/core/built/admin/assets/activitypub/{notifications-B9p8eCHD.js → notifications-BmNlXGgp.js} +51 -51
- package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-Bqg7fh_L.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-Dmun9jr_.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-BCqUdC3v.js → preferences--oH19wf9.js} +1519 -1519
- package/core/built/admin/assets/activitypub/{profile-BRCWoFem.js → profile-DH_tQx2D.js} +185 -185
- package/core/built/admin/assets/activitypub/{reply-DYK5vVqb.js → reply-Cap9PxSH.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-BSXT992i.js → routes-DdyY9uFF.js} +744 -735
- package/core/built/admin/assets/activitypub/{separator-BbQqTm-p.js → separator-CAxxu8VV.js} +3 -3
- package/core/built/admin/assets/activitypub/settings-B6EEcVkW.js +315 -0
- package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-BYps63oh.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-B-YfsVeO.js → step-1-CKjR2KAm.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-2-DtPQzlTH.js → step-2-B6PKfOUQ.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-D6BFMXL_.js → step-3-C-qBbcUo.js} +11 -11
- package/core/built/admin/assets/activitypub/{tabs-GpNecvfB.js → tabs-DLhZtVde.js} +306 -306
- package/core/built/admin/assets/activitypub/{topic-filter-BPDeQQys.js → topic-filter-DsUOuu0k.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-BoVOxXkV.js → use-activity-pub-queries-CX9snsLs.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-Bmk5h4lW.js → use-navigate-with-base-path-DsJTGDSS.js} +988 -959
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-wVBX7swM.js → use-reply-chain-data-AJi9zkzm.js} +30 -30
- package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-inweyKL5.js} +192 -164
- package/core/built/admin/assets/activitypub/{x-DbQZOLiI.js → x-DR8AT3-4.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BT_onDJy.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-Cx1kQpiY.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-B5nvAhvr.js +1 -0
- package/core/built/admin/assets/analytics-DjoJWPI7.js +1 -0
- package/core/built/admin/assets/{announcement-bar-modal-DM3cYOpo.js → announcement-bar-modal-Dwd0EGFP.js} +1 -1
- package/core/built/admin/assets/{api-keys-3-0vJC3Y.js → api-keys-BGPzLHzA.js} +1 -1
- package/core/built/admin/assets/{api-keys-BLm2NDBh.js → api-keys-BoeacM-s.js} +1 -1
- package/core/built/admin/assets/{audience-9mwU5TW6.js → audience-ceV3XVHq.js} +1 -1
- package/core/built/admin/assets/automated-emails-BLesE41L.js +1 -0
- package/core/built/admin/assets/{automations-sAcJ1334.js → automations-BN4u3GR8.js} +1 -1
- package/core/built/admin/assets/automations-C-ZTUwW3.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-vzh-oPXL.js +1 -0
- package/core/built/admin/assets/{breadcrumb-DLiqCyFc.js → breadcrumb-CaLk2b7D.js} +1 -1
- package/core/built/admin/assets/{button-B2jcu6bk.js → button-Dvcv1QSR.js} +1 -1
- package/core/built/admin/assets/calendar-Bl9RLcZX.js +1 -0
- package/core/built/admin/assets/{chart-helpers-Fo3bUL3K.js → chart-helpers-FHywU0pj.js} +1 -1
- package/core/built/admin/assets/{checkbox-BfKktMph.js → checkbox-BtayFtjg.js} +1 -1
- package/core/built/admin/assets/chunk.254.035b85ec456ebbd7336b.js +440 -0
- package/core/built/admin/assets/{chunk.524.60ca4692d91c6022042f.js → chunk.524.e6975095fccdf22d90f8.js} +5 -5
- package/core/built/admin/assets/{chunk.582.525c7617c2d9e94c940a.js → chunk.582.98b485f9b2802f540897.js} +6 -6
- package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js → chunk.901.4ccc639821020230bcca.js} +9949 -7921
- package/core/built/admin/assets/{code-editor-view-DCQJxsfU.js → code-editor-view-C3ZfoF1X.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-CYVu4EW6.js → code-editor-view-CAyRWy1s.js} +1 -1
- package/core/built/admin/assets/color-picker-G4kGR7rp.js +3 -0
- package/core/built/admin/assets/color-picker-field-DQFVTNnT.js +1 -0
- package/core/built/admin/assets/combobox-BnYzgEB4.js +1 -0
- package/core/built/admin/assets/command-C_vyTikI.js +1 -0
- package/core/built/admin/assets/comments-aBrxfnZf.js +1 -0
- package/core/built/admin/assets/{config-DOMQDZGZ.js → config-kSHvwdTG.js} +1 -1
- package/core/built/admin/assets/confirmation-context-DwXX_Y5h.js +1 -0
- package/core/built/admin/assets/{content-api-modal-CyQEC3fr.js → content-api-modal-D2G0nJP6.js} +1 -1
- package/core/built/admin/assets/context-menu-DOVB6yaz.js +1 -0
- package/core/built/admin/assets/{copy-field-B3M1s78L.js → copy-field-DCJwh9qf.js} +1 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-5C53jJaX.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-BAuW0F-E.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-AbJGVyKc.js +1 -0
- package/core/built/admin/assets/{date-range-select-DD2RKkgB.js → date-range-select-BY3K-ukj.js} +1 -1
- package/core/built/admin/assets/design-and-theme-modal-BvEy4_pC.js +2 -0
- package/core/built/admin/assets/dirty-confirm-dialog-DVPuGz3U.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-Cvy47bE2.js → disabled-sources-indicator-Cy7Zp4c8.js} +1 -1
- package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
- package/core/built/admin/assets/domain-Dl61QGuk.js +1 -0
- package/core/built/admin/assets/dropzone-Dtz5Rmjt.js +5 -0
- package/core/built/admin/assets/edit-profile-DNJSMDwm.js +1 -0
- package/core/built/admin/assets/editor-DbAen9zf.js +8 -0
- package/core/built/admin/assets/editor-screen-BFWThIai.js +26 -0
- package/core/built/admin/assets/email-sending-status-context-CWDOD1FN.js +1 -0
- package/core/built/admin/assets/email-sending-status-provider-DCPkDICt.js +1 -0
- package/core/built/admin/assets/emails-vFMk4yYl.js +1 -0
- package/core/built/admin/assets/{embed-signup-form-modal-DeBMLxlL.js → embed-signup-form-modal-DGqsORxS.js} +1 -1
- package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
- package/core/built/admin/assets/explore-nnRr1Fja.js +1 -0
- package/core/built/admin/assets/feature-flag-overrides-context-DQjdJFAk.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-BrIHmPs8.js +1 -0
- package/core/built/admin/assets/feed-BNUcNu-W.js +1 -0
- package/core/built/admin/assets/feedback-DJ4Zb9rZ.js +1 -0
- package/core/built/admin/assets/{field-Cj8lmDOo.js → field-C93_Mg6j.js} +1 -1
- package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
- package/core/built/admin/assets/filter-sources-CGJb4C5r.js +1 -0
- package/core/built/admin/assets/filters--kKP-EC8.js +1 -0
- package/core/built/admin/assets/filters-DxA_a4FP.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-BYDtrz2S.js +1 -0
- package/core/built/admin/assets/focus-koenig-editor-on-bottom-click-BbSnGOtK.js +1 -0
- package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8fd5bb799356bbefb254210ba7e1236d.css} +1 -1
- package/core/built/admin/assets/{ghost-3894eedcc6968f658578c0abfe942aab.js → ghost-b4e8a403baba61905cd7560e30b9732d.js} +63 -44
- package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-cac0cb2d5bb78e9bd14d8d1c2768ed45.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-B7u5bD4e.js +1 -0
- package/core/built/admin/assets/{gift-links-DLDS7ljO.js → gift-links-CfLSnst2.js} +1 -1
- package/core/built/admin/assets/{growth-D6mhcx3q.js → growth-Cd1uO9d2.js} +1 -1
- package/core/built/admin/assets/growth-aG32xfdE.js +3 -0
- package/core/built/admin/assets/history-modal-Df8lW8h-.js +1 -0
- package/core/built/admin/assets/hooks-Cy_-oZ4o.js +2 -0
- package/core/built/admin/assets/{html-field-C8qVGGC1.js → html-field-DA4eU7eq.js} +1 -1
- package/core/built/admin/assets/{images-h8VZEQ9k.js → images-KXmyO3Ss.js} +1 -1
- package/core/built/admin/assets/{inbox-BYd1M06Q.js → inbox-gJHG7_xd.js} +14 -14
- package/core/built/admin/assets/index-BHHXtR5C.css +2 -0
- package/core/built/admin/assets/index-CgMCwsxN.js +4 -0
- package/core/built/admin/assets/index.es-CY6-yuw9.js +1 -0
- package/core/built/admin/assets/{input-group-D8sCXRF5.js → input-group-BE8rM2y1.js} +1 -1
- package/core/built/admin/assets/{integrations-Bjbu86Z0.js → integrations-B4kuRBFY.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-B4CuNcx4.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-_-IsVI7E.js → koenig-lexical-CGXb0F8o.js} +3 -3
- package/core/built/admin/assets/koenig-loader-BeM5rFPX.js +2 -0
- package/core/built/admin/assets/{kpi-card-DZQk_0W8.js → kpi-card-B21aToe8.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DW_2y3ug.js → kpi-card-pz6Wv4Ae.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-PueTKuA6.js → kpi-helpers-Dq5jelmP.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-CojDs-Qz.js → kpi-tabs-DEZHpKUt.js} +1 -1
- package/core/built/admin/assets/{label-DjbT91GM.js → label-OLCzze8N.js} +1 -1
- package/core/built/admin/assets/label-picker-BigOvMPV.js +1 -0
- package/core/built/admin/assets/{labels-CH3Kn8RV.js → labels-Dr0SgUQD.js} +1 -1
- package/core/built/admin/assets/layout-DUTL8SH5.js +1 -0
- package/core/built/admin/assets/lib-7QPTI0FS.js +1 -0
- package/core/built/admin/assets/link-helpers-BRAg9SCj.js +1 -0
- package/core/built/admin/assets/links-BDHPTlyj.js +1 -0
- package/core/built/admin/assets/member-custom-fields-TRTHCAz9.js +1 -0
- package/core/built/admin/assets/member-detail-b6dRwc3C.js +1 -0
- package/core/built/admin/assets/members-Czt5lJSc.js +4 -0
- package/core/built/admin/assets/moderation-ZWefD9uH.js +1 -0
- package/core/built/admin/assets/{multi-select-combobox-B6LiRzDO.js → multi-select-combobox-CT13MTo-.js} +1 -1
- package/core/built/admin/assets/navigation-modal-C8FEhBK5.js +1 -0
- package/core/built/admin/assets/newsletter-5VYW4RyJ.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-ChjC0ZqH.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-CFeLlt4z.js → newsletter-emails-DZH6I-PY.js} +1 -1
- package/core/built/admin/assets/newsletters-DOC9KBcX.js +1 -0
- package/core/built/admin/assets/newsletters-Rq1M3GD-.js +1 -0
- package/core/built/admin/assets/note-DIV-Pojr.js +1 -0
- package/core/built/admin/assets/notifications-s20-H49V.js +1 -0
- package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
- package/core/built/admin/assets/{offers-9J1CSiLy.js → offers--e2nGvkW.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BmG5M5Tv.js +1 -0
- package/core/built/admin/assets/onboarding-CUuy9unR.js +1 -0
- package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-pXyct0Jm.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-Dmc4IPm3.js → onboarding-route-CK4pcXoR.js} +1 -1
- package/core/built/admin/assets/overview-CaPsciMs.js +1 -0
- package/core/built/admin/assets/overview-a8GfcsKX.js +1 -0
- package/core/built/admin/assets/{page-header-BR_nbIPp.js → page-header-DM_KyoFI.js} +1 -1
- package/core/built/admin/assets/pagemenu-vgZVUVKR.js +1 -0
- package/core/built/admin/assets/pages-CjDY2CBI.js +1 -0
- package/core/built/admin/assets/pages-route-BzKZYdV5.js +1 -0
- package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
- package/core/built/admin/assets/{pintura-modal-BR9hEvS8.js → pintura-modal-DExCF1ii.js} +1 -1
- package/core/built/admin/assets/portal-frame-Cqanidq4.js +1 -0
- package/core/built/admin/assets/portal-modal-oinBgA27.js +1 -0
- package/core/built/admin/assets/post-analytics-C5oJ1dWV.js +1 -0
- package/core/built/admin/assets/post-analytics-header-OATo3X_r.js +1 -0
- package/core/built/admin/assets/post-analytics-provider-tOpIVGT5.js +1 -0
- package/core/built/admin/assets/post-helpers-C88JBa4D.js +1 -0
- package/core/built/admin/assets/post-share-modal-DvhC1XgV.js +1 -0
- package/core/built/admin/assets/post-utils-DKF4mfuo.js +1 -0
- package/core/built/admin/assets/posts-list-screen-o4ZjbgS3.js +2 -0
- package/core/built/admin/assets/posts-pCPrhQeR.js +1 -0
- package/core/built/admin/assets/posts-route-9F--xdIb.js +1 -0
- package/core/built/admin/assets/preferences--vNubsqD.js +5 -0
- package/core/built/admin/assets/{preview-modal-C-BL-F9k.js → preview-modal-YYbzlYdO.js} +1 -1
- package/core/built/admin/assets/profile-BllKV3dR.js +1 -0
- package/core/built/admin/assets/{radio-group-Bgz60i81.js → radio-group-DL8GI8rt.js} +1 -1
- package/core/built/admin/assets/{referrers-CnhCMWCs.js → referrers-D8oHPmDB.js} +1 -1
- package/core/built/admin/assets/router-provider-BSvTXgZY.js +1 -0
- package/core/built/admin/assets/schemas-BoYdB6YU.js +64 -0
- package/core/built/admin/assets/select-v5YbA3Mw.js +1 -0
- package/core/built/admin/assets/settings-PEsZupw1.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-CyP_9zTo.js → settings-breadcrumbs-NwPnCTC7.js} +1 -1
- package/core/built/admin/assets/settings-u_s1CRkr.js +1 -0
- package/core/built/admin/assets/share-modal-SskCmVGL.js +1 -0
- package/core/built/admin/assets/slack-modal-XxSJiYgo.js +1 -0
- package/core/built/admin/assets/{sort-button-Cc-Z96sA.js → sort-button-Cx1KtINE.js} +1 -1
- package/core/built/admin/assets/{source-icon-v6PUnmyM.js → source-icon-Bdp9Im_5.js} +1 -1
- package/core/built/admin/assets/stats-D9PDLS3x.js +1 -0
- package/core/built/admin/assets/stats-view-W2g6Jsd-.js +1 -0
- package/core/built/admin/assets/{step-1-DSRp2vBj.js → step-1-BQYzvBzr.js} +1 -1
- package/core/built/admin/assets/{step-2-COqv2g0u.js → step-2-xES32xJV.js} +1 -1
- package/core/built/admin/assets/{step-3-6W1NMVQc.js → step-3-DA_XK5kW.js} +2 -2
- package/core/built/admin/assets/{stripe-button-D2K42Uty.js → stripe-button-CVsLHelt.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-ClmLOMGS.js +1 -0
- package/core/built/admin/assets/style-Wd9nQubI.css +1 -0
- package/core/built/admin/assets/{table-B2pIKjVh.js → table-BenqVupI.js} +1 -1
- package/core/built/admin/assets/{tabs-SaW_UT-Q.js → tabs-eBBlxPp8.js} +1 -1
- package/core/built/admin/assets/tag-detail-Ce8Se2qd.js +2 -0
- package/core/built/admin/assets/tag-picker-C3UR-uJ-.js +1 -0
- package/core/built/admin/assets/tags-B-kJxCs7.js +1 -0
- package/core/built/admin/assets/{tags-zTFiKRpb.js → tags-ChfdwJyS.js} +1 -1
- package/core/built/admin/assets/testimonials-modal-9Q6fT06V.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-CDyYdqK7.js +1 -0
- package/core/built/admin/assets/tiers-BCpIOGQr.js +1 -0
- package/core/built/admin/assets/{toggle-group-EM9WpESu.js → toggle-group-CviIkyok.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DY4gNHn2.js → topic-filter-I2jCBGxm.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-CpyPG-MH.js → transistor-modal-p53u3W8y.js} +1 -1
- package/core/built/admin/assets/{trend-badge-ByMkuAuH.js → trend-badge-CideVzQd.js} +1 -1
- package/core/built/admin/assets/{unsplash-modal-Cv2Q6GtY.js → unsplash-modal-DY8UcKcE.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-CHvjMYuT.js → use-analytics-data-DAIGcr2f.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-Qv4SVXqw.js → use-check-theme-limit-error-DRUjK4YM.js} +1 -1
- package/core/built/admin/assets/use-filter-params-JkZhIS8L.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-CzXcpUh8.js → use-filterable-api-BVEVMrhZ.js} +1 -1
- package/core/built/admin/assets/use-growth-stats-937CUj6j.js +1 -0
- package/core/built/admin/assets/use-host-limits-D0Dv3tKA.js +1 -0
- package/core/built/admin/assets/{use-limiter-cwJvsNBo.js → use-limiter-CohWfzvK.js} +1 -1
- package/core/built/admin/assets/{use-navigate-with-base-path-BHPA228f.js → use-navigate-with-base-path-BkxcPj5l.js} +1 -1
- package/core/built/admin/assets/use-pintura-config-xgytjU35.js +1 -0
- package/core/built/admin/assets/{use-pintura-editor-BDBXWUYq.js → use-pintura-editor-CaV1mbfO.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-DbY-mzci.js → use-post-referrers-BCQI7fDg.js} +1 -1
- package/core/built/admin/assets/use-reply-chain-data-CYRhoaxY.js +1 -0
- package/core/built/admin/assets/{use-router-history-entry-Bg6BhBQQ.js → use-router-history-entry-NOKcd--l.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-VM7FQwMY.js → use-setting-group-bLKMwoBT.js} +1 -1
- package/core/built/admin/assets/use-settings-navigation-B-qrriKD.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-Dum6Irbs.js → use-simple-pagination-CNPd6Rf6.js} +1 -1
- package/core/built/admin/assets/use-staff-users-pRIMIyiW.js +1 -0
- package/core/built/admin/assets/use-tinybird-query-CmdbCS9X.js +1 -0
- package/core/built/admin/assets/use-tinybird-token-KLne235s.js +1 -0
- package/core/built/admin/assets/{use-unsaved-changes-guard-BAy0RANR.js → use-unsaved-changes-guard-7-ug0JdH.js} +1 -1
- package/core/built/admin/assets/use-upgrade-route-Dm_qqKr0.js +1 -0
- package/core/built/admin/assets/{use-url-input-Df5QzNa7.js → use-url-input-fvp-TpPx.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-D3KlGYv-.js +1 -0
- package/core/built/admin/assets/users-DVNWrqg9.js +1 -0
- package/core/built/admin/assets/{vendor-5753232c7d6ffb3c44e1eb0a02133953.js → vendor-2dd263291938d407802568d3357ec69e.js} +19 -17
- package/core/built/admin/assets/{virtual-list-DEx8WgiO.js → virtual-list-CZ3RK2Yf.js} +1 -1
- package/core/built/admin/assets/web-ClEJsCAj.js +2 -0
- package/core/built/admin/assets/web-X9rdnrtq.js +1 -0
- package/core/built/admin/assets/{zapier-modal-Cw3BTBUu.js → zapier-modal-DfVhV17j.js} +1 -1
- package/core/built/admin/index.html +23 -27
- 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/sitemap/user-map-generator.js +9 -2
- package/core/server/adapters/jobs/InMemoryJobsBackend.js +74 -29
- package/core/server/api/endpoints/authentication.js +1 -2
- package/core/server/api/endpoints/authors-public.js +15 -5
- package/core/server/api/endpoints/emails.js +13 -0
- package/core/server/api/endpoints/index.js +10 -2
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/member-metafields-members.js +23 -0
- package/core/server/api/endpoints/member-metafields.js +102 -0
- package/core/server/api/endpoints/members-account.js +42 -0
- package/core/server/api/endpoints/members.js +4 -4
- package/core/server/api/endpoints/newsletters-public.js +10 -1
- package/core/server/api/endpoints/pages-public.js +15 -5
- package/core/server/api/endpoints/posts-public.js +15 -5
- package/core/server/api/endpoints/posts.js +3 -3
- package/core/server/api/endpoints/tags-public.js +22 -3
- package/core/server/api/endpoints/tiers-checkout-config.js +2 -2
- package/core/server/api/endpoints/tiers-public.js +38 -1
- package/core/server/api/endpoints/users.js +2 -2
- package/core/server/api/endpoints/utils/api-filter-utils.js +170 -3
- package/core/server/api/endpoints/utils/public-cache-keys.js +3 -0
- package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -0
- package/core/server/api/endpoints/utils/serializers/input/members.js +20 -9
- package/core/server/api/endpoints/utils/serializers/input/pages.js +2 -0
- package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -0
- package/core/server/api/endpoints/utils/serializers/input/tags.js +2 -0
- package/core/server/api/endpoints/utils/serializers/output/emails.js +7 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +6 -2
- package/core/server/api/endpoints/utils/serializers/output/member-metafields.js +18 -0
- package/core/server/api/endpoints/utils/serializers/output/members-account.js +19 -0
- package/core/server/api/endpoints/utils/serializers/output/members-csv-transform.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
- package/core/server/api/endpoints/utils/serializers/output/tiers.js +10 -2
- package/core/server/api/endpoints/utils/serializers/output/users.js +0 -10
- 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 -3
- 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.53/2026-07-15-20-22-53-add-members-custom-field-values.js +1 -1
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +10 -1
- package/core/server/data/migrations/versions/6.63/2026-09-03-10-07-03-rename-custom-field-tables-to-metafields.js +169 -0
- package/core/server/data/migrations/versions/6.63/2026-09-03-11-03-31-remove-member-custom-field-read-permissions.js +30 -0
- package/core/server/data/schema/fixtures/fixtures.json +0 -11
- package/core/server/data/schema/lib/default-index-name.js +8 -11
- package/core/server/data/schema/schema.js +18 -18
- package/core/server/data/seeders/README.md +48 -0
- package/core/server/data/tinybird/pipes/mv_hits.pipe +86 -50
- package/core/server/lib/db-types/count.js +16 -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/validate-password.js +81 -120
- package/core/server/models/action.js +4 -4
- package/core/server/models/base/plugins/filtered-collection.js +16 -0
- package/core/server/models/member.js +10 -21
- package/core/server/models/post.js +2 -2
- package/core/server/models/relations/authors.js +2 -16
- package/core/server/models/user.js +10 -3
- package/core/server/services/auth/passwordreset.js +13 -0
- package/core/server/services/automations/automations-api.js +1 -1
- package/core/server/services/automations/database-automations-repository.js +11 -4
- 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/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 +160 -53
- 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 +0 -1
- package/core/server/services/content-import/import/post-link.js +11 -0
- package/core/server/services/content-import/import/post-repository.js +25 -3
- package/core/server/services/content-import/import/reader.js +9 -2
- package/core/server/services/content-import/import/report.js +34 -0
- package/core/server/services/content-import/import/row.js +0 -2
- package/core/server/services/content-import/import/schema.js +1 -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 +52 -6
- 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/index.js +2 -7
- package/core/server/services/email-service/CONTEXT.md +37 -0
- package/core/server/services/email-service/README.md +42 -0
- package/core/server/services/email-service/batch-sending-service.js +2 -4
- package/core/server/services/email-service/email-controller.js +18 -2
- package/core/server/services/email-service/email-renderer.js +14 -4
- package/core/server/services/email-service/email-service-wrapper.js +3 -9
- package/core/server/services/email-service/mailgun-email-provider.js +20 -9
- package/core/server/services/email-service/sending-status-schema.js +21 -0
- package/core/server/services/email-service/sending-status-serializers.js +33 -0
- package/core/server/services/email-service/sending-status-service.js +47 -0
- package/core/server/services/email-service/sending-status.js +111 -0
- package/core/server/services/email-suppression-list/email-suppression-list.js +0 -33
- package/core/server/services/email-suppression-list/mailgun-email-suppression-list.js +6 -7
- package/core/server/services/gifts/README.md +2 -2
- 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-email-service.js +0 -1
- package/core/server/services/gifts/gift-service.js +19 -57
- package/core/server/services/gifts/index.js +3 -5
- 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/index.js +8 -0
- package/core/server/services/jobs-service/jobs-service.js +52 -3
- package/core/server/services/jobs-service/register-job-handlers.js +32 -11
- package/core/server/services/mail/ghost-mailer.js +4 -4
- package/core/server/services/member-welcome-emails/service.js +7 -7
- package/core/server/services/members/account-service.js +98 -0
- package/core/server/services/members/api.js +4 -3
- package/core/server/services/members/import-export/csv/parse.js +0 -7
- package/core/server/services/members/import-export/export/exporter.js +20 -20
- package/core/server/services/members/import-export/import/completion-email.js +8 -11
- package/core/server/services/members/import-export/import/importer.js +13 -13
- package/core/server/services/members/import-export/import/row.js +0 -3
- package/core/server/services/members/import-export/index.js +10 -17
- 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 +26 -8
- package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
- package/core/server/services/members/members-api/repositories/member-repository.js +4 -4
- package/core/server/services/members/members-api/services/member-bread-service.js +62 -84
- package/core/server/services/members/members-api/services/payments-service.js +1 -1
- package/core/server/services/members/members-ssr.js +18 -0
- package/core/server/services/members/middleware.js +95 -81
- package/core/server/services/members/service.js +71 -47
- package/core/server/services/members/utils.js +9 -0
- package/core/server/services/members-metafields/access.js +28 -0
- package/core/server/services/{members-custom-fields → members-metafields}/actions.js +5 -5
- package/core/server/services/members-metafields/bindings-service.js +147 -0
- package/core/server/services/members-metafields/codec.js +14 -0
- package/core/server/services/{members-custom-fields → members-metafields}/config.js +1 -1
- package/core/server/services/{members-custom-fields → members-metafields}/definitions-service.js +68 -22
- package/core/server/services/members-metafields/filter.js +181 -0
- package/core/server/services/{members-custom-fields → members-metafields}/index.js +16 -7
- package/core/server/services/{members-custom-fields → members-metafields}/key.js +2 -1
- package/core/server/services/{members-custom-fields → members-metafields}/models.js +5 -5
- package/core/server/services/members-metafields/namespaces.js +34 -0
- package/core/server/services/{members-custom-fields → members-metafields}/queries.js +1 -1
- package/core/server/services/{members-custom-fields → members-metafields}/schema.js +16 -12
- package/core/server/services/members-metafields/serializers.js +22 -0
- package/core/server/services/{members-custom-fields → members-metafields}/values-service.js +80 -48
- package/core/server/services/mentions/mention-controller.js +23 -21
- package/core/server/services/mentions/mention-sending-service.js +26 -9
- package/core/server/services/mentions/process-webmention-job.js +16 -0
- package/core/server/services/mentions/send-webmentions-job.js +18 -0
- package/core/server/services/mentions/service.js +7 -33
- 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 +2 -2
- package/core/server/services/stripe/services/checkout/session-options.js +18 -15
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +28 -7
- package/core/server/services/stripe/stripe-api.js +2 -0
- package/core/server/services/stripe/stripe-service.js +24 -14
- package/core/server/services/stripe/webhook-controller.js +10 -0
- package/core/server/services/tier-checkout-config/codec.js +11 -7
- package/core/server/services/tier-checkout-config/index.js +2 -1
- package/core/server/services/tier-checkout-config/models.js +11 -5
- package/core/server/services/tier-checkout-config/queries.js +12 -12
- package/core/server/services/tier-checkout-config/serializers.js +73 -10
- package/core/server/services/tier-checkout-config/service.js +14 -13
- package/core/server/services/tiers/service.js +3 -3
- 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/services/url/router-filter.js +8 -10
- package/core/server/services/users.js +1 -19
- package/core/server/web/admin/controller.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +20 -16
- package/core/server/web/members/account/index.js +2 -0
- package/core/server/web/members/account/routes.js +46 -0
- package/core/server/web/members/app.js +4 -18
- package/core/shared/config/defaults.json +1 -1
- package/core/shared/config/env/config.testing-mysql.json +1 -1
- package/core/shared/labs.js +7 -2
- package/package.json +42 -41
- package/pnpm-lock.yaml +1159 -582
- package/pnpm-workspace.yaml +18 -15
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/core/built/admin/assets/Combination-5fYEzgbs.js +0 -41
- package/core/built/admin/assets/account-migration-DRhAehfw.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-BluD2ucT.js +0 -1284
- package/core/built/admin/assets/activitypub/label-FHSh6ZAw.js +0 -16
- package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +0 -1
- package/core/built/admin/assets/activitypub/settings-DQDkr06X.js +0 -315
- package/core/built/admin/assets/add-integration-modal-BC0jExDZ.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-DchTkA7l.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-DMvaJZKp.js +0 -1
- package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +0 -1
- package/core/built/admin/assets/analytics-No1DDY3A.js +0 -1
- package/core/built/admin/assets/automated-emails-te1D5kq-.js +0 -1
- package/core/built/admin/assets/automations-DpUNUdcZ.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-Cjrv_f62.js +0 -1
- package/core/built/admin/assets/chunk.896.435fe0637944d4a69810.js +0 -366
- package/core/built/admin/assets/color-picker-BTY_NdEm.js +0 -3
- package/core/built/admin/assets/color-picker-field-CPlkzOH0.js +0 -1
- package/core/built/admin/assets/combobox-DrMtv_4P.js +0 -1
- package/core/built/admin/assets/command-ZdgYJpoI.js +0 -1
- package/core/built/admin/assets/comments-kidj60-h.js +0 -1
- package/core/built/admin/assets/confirmation-context-BhbmQQEl.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-CsGIZk1J.js +0 -10
- package/core/built/admin/assets/custom-integration-modal-BY37O_9X.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +0 -22
- package/core/built/admin/assets/dialog-B44iRwlc.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-BxgQgQ6H.js +0 -1
- package/core/built/admin/assets/dist-Cy8tfrp_.js +0 -1
- package/core/built/admin/assets/domain-Ca6KhoQt.js +0 -1
- package/core/built/admin/assets/dropzone-Bb-5zQTk.js +0 -5
- package/core/built/admin/assets/edit-profile-BdupLlus.js +0 -1
- package/core/built/admin/assets/editor-BP883Lpn.js +0 -8
- package/core/built/admin/assets/explore-FB-CimXw.js +0 -1
- package/core/built/admin/assets/feed-C1GxsmI2.js +0 -1
- package/core/built/admin/assets/filter-sources-D5n5bFA2.js +0 -1
- package/core/built/admin/assets/filters-DJEzkMUK.js +0 -1
- package/core/built/admin/assets/filters-MZXvdH5D.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-CGTGLCr5.js +0 -1
- package/core/built/admin/assets/gift-link-modal-B6v0tnRN.js +0 -1
- package/core/built/admin/assets/growth-BGAJD1rs.js +0 -3
- package/core/built/admin/assets/history-modal-iKwUY3XR.js +0 -1
- package/core/built/admin/assets/hooks-CuEZV2-q.js +0 -2
- package/core/built/admin/assets/index-DjQe-e5s.js +0 -4
- package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
- package/core/built/admin/assets/index.es-VihPwfeH.js +0 -1
- package/core/built/admin/assets/input--W-rCTrX.js +0 -1
- package/core/built/admin/assets/invite-user-modal-MdImLCaJ.js +0 -1
- package/core/built/admin/assets/koenig-loader-DYZpm6El.js +0 -2
- package/core/built/admin/assets/label-picker-cB51xl1r.js +0 -1
- package/core/built/admin/assets/layout-c_B_1hax.js +0 -1
- package/core/built/admin/assets/link-helpers-MVDdnFCY.js +0 -1
- package/core/built/admin/assets/loading-indicator-BpFWTVgh.js +0 -1
- package/core/built/admin/assets/member-custom-fields-qs0dIkxM.js +0 -1
- package/core/built/admin/assets/member-detail-B6uec2a6.js +0 -1
- package/core/built/admin/assets/members-BQF3CqhI.js +0 -2
- package/core/built/admin/assets/moderation-DY4wF1Q3.js +0 -1
- package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +0 -1
- package/core/built/admin/assets/newsletter-CyQA7m3m.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-BDrptuxn.js +0 -1
- package/core/built/admin/assets/newsletters-DXuNmC42.js +0 -1
- package/core/built/admin/assets/newsletters-DhNigJUh.js +0 -1
- package/core/built/admin/assets/note-Cgo2LCJ6.js +0 -1
- package/core/built/admin/assets/notifications-BC50AMku.js +0 -1
- package/core/built/admin/assets/offers-container-modal-DWycIMzT.js +0 -1
- package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
- package/core/built/admin/assets/overview-CVbFtn-k.js +0 -1
- package/core/built/admin/assets/overview-YKj7SZaK.js +0 -1
- package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
- package/core/built/admin/assets/popover-CCy0h5rq.js +0 -1
- package/core/built/admin/assets/portal-frame-dc4XKr0e.js +0 -1
- package/core/built/admin/assets/portal-modal-BvvdF_B9.js +0 -1
- package/core/built/admin/assets/post-analytics-DX470WRG.js +0 -1
- package/core/built/admin/assets/post-analytics-header-CXpJmdDv.js +0 -1
- package/core/built/admin/assets/post-analytics-provider-CLfKGWZy.js +0 -1
- package/core/built/admin/assets/post-helpers-C5OQMBoi.js +0 -1
- package/core/built/admin/assets/post-share-modal-Cl0NXRJr.js +0 -1
- package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
- package/core/built/admin/assets/preferences-DO0UUWFp.js +0 -5
- package/core/built/admin/assets/profile-CxT-0l4d.js +0 -1
- package/core/built/admin/assets/router-provider-B0gZyiGJ.js +0 -1
- package/core/built/admin/assets/schemas-4UPic-oD.js +0 -64
- package/core/built/admin/assets/select-DuDUFdPq.js +0 -1
- package/core/built/admin/assets/settings-BjCii7BM.js +0 -1
- package/core/built/admin/assets/settings-b8kNzZSQ.js +0 -4
- package/core/built/admin/assets/share-modal-BV70mr9k.js +0 -1
- package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
- package/core/built/admin/assets/slack-modal-Be2_KK-z.js +0 -1
- package/core/built/admin/assets/stats-C4reGh9O.js +0 -1
- package/core/built/admin/assets/stats-view-DP9pQ62k.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-Z-UT_BGc.js +0 -1
- package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +0 -2
- package/core/built/admin/assets/tags-BLaXBwnb.js +0 -1
- package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BCFpWwUS.js +0 -1
- package/core/built/admin/assets/tiers-Nd_z4BZY.js +0 -1
- package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +0 -1
- package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +0 -1
- package/core/built/admin/assets/use-host-limits-DZtR08oa.js +0 -1
- package/core/built/admin/assets/use-pintura-config-CzTfnufz.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-BeQwz8oF.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BbrSUVcf.js +0 -1
- package/core/built/admin/assets/use-staff-users-cZg4ugOF.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-B1IMyAcv.js +0 -39
- package/core/built/admin/assets/use-tinybird-token-Bow0Zwn8.js +0 -1
- package/core/built/admin/assets/use-upgrade-route-DhRSo7at.js +0 -1
- package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +0 -1
- package/core/built/admin/assets/users-5msE1WMs.js +0 -1
- package/core/built/admin/assets/web-CKcXAu86.js +0 -1
- package/core/built/admin/assets/web-DR4TqM3E.js +0 -2
- package/core/server/api/endpoints/member-custom-fields.js +0 -90
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +0 -20
- package/core/server/services/email-suppression-list/in-memory-email-suppression-list.js +0 -39
- 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/members-custom-fields/bindings-service.js +0 -119
- package/core/server/services/members-custom-fields/codec.js +0 -15
- package/core/server/services/members-custom-fields/filter.js +0 -199
- package/core/server/services/members-custom-fields/serializers.js +0 -24
- package/core/server/services/stripe/services/checkout/allowed-countries.js +0 -267
- package/core/server/services/stripe/services/checkout/field-ports.js +0 -34
- package/core/server/services/tier-checkout-config/destinations.js +0 -17
- package/core/server/services/update-check/run-update-check.js +0 -63
- /package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js.LICENSE.txt → chunk.901.4ccc639821020230bcca.js.LICENSE.txt} +0 -0
- /package/core/server/services/{members-custom-fields → members-metafields}/storage.js +0 -0
package/core/server/services/{members-custom-fields → members-metafields}/definitions-service.js
RENAMED
|
@@ -3,13 +3,16 @@ 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.
|
|
6
|
+
exports.MetafieldDefinitionsService = void 0;
|
|
7
7
|
const bson_objectid_1 = __importDefault(require("bson-objectid"));
|
|
8
8
|
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
9
9
|
const zod_1 = require("zod");
|
|
10
|
-
const
|
|
10
|
+
const metafield_types_1 = require("@tryghost/metafield-types");
|
|
11
|
+
const identity_1 = require("@tryghost/metafield-types/identity");
|
|
11
12
|
const codec_1 = require("./codec");
|
|
13
|
+
const namespaces_1 = require("./namespaces");
|
|
12
14
|
const schema_1 = require("./schema");
|
|
15
|
+
const access_1 = require("./access");
|
|
13
16
|
const queries_1 = require("./queries");
|
|
14
17
|
const key_1 = require("./key");
|
|
15
18
|
// The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to
|
|
@@ -17,7 +20,7 @@ const key_1 = require("./key");
|
|
|
17
20
|
// turns that into parametrised WHERE clauses. Neither needs a Bookshelf model.
|
|
18
21
|
const nql = require('@tryghost/nql');
|
|
19
22
|
const knexify = require('@tryghost/mongo-knex');
|
|
20
|
-
const TABLE = '
|
|
23
|
+
const TABLE = 'members_metafields';
|
|
21
24
|
// Column limits come from the canonical schema — the same source the migration and
|
|
22
25
|
// Bookshelf models read — so the service can never drift from the database. The
|
|
23
26
|
// name cap turns an over-long input into a clean 422 (not a DB error), and the key
|
|
@@ -47,7 +50,7 @@ const FieldName = zod_1.z
|
|
|
47
50
|
// The backend mints the key from the name, so create takes just a name and type.
|
|
48
51
|
const AddFieldInput = zod_1.z.object({
|
|
49
52
|
name: FieldName,
|
|
50
|
-
type:
|
|
53
|
+
type: metafield_types_1.FieldTypeSchema,
|
|
51
54
|
});
|
|
52
55
|
// A bound on the work one request can ask for, separate from how many definitions
|
|
53
56
|
// a site may hold in total. Every definition in a batch costs several queries
|
|
@@ -76,9 +79,9 @@ const EditFieldInput = zod_1.z.object({
|
|
|
76
79
|
name: FieldName.optional(),
|
|
77
80
|
status: schema_1.FieldStatusSchema.optional(),
|
|
78
81
|
key: zod_1.z.string().optional(),
|
|
79
|
-
type:
|
|
82
|
+
type: metafield_types_1.FieldTypeSchema.optional(),
|
|
80
83
|
});
|
|
81
|
-
class
|
|
84
|
+
class MetafieldDefinitionsService {
|
|
82
85
|
knex;
|
|
83
86
|
recordAction;
|
|
84
87
|
getMaxDefinitions;
|
|
@@ -92,7 +95,21 @@ class CustomFieldDefinitionsService {
|
|
|
92
95
|
// restart. Where the number comes from is the caller's business.
|
|
93
96
|
this.getMaxDefinitions = getMaxDefinitions;
|
|
94
97
|
}
|
|
95
|
-
async
|
|
98
|
+
async hasAnyActive() {
|
|
99
|
+
const [field] = await this.list((0, queries_1.activeFields)(this.knex).limit(1));
|
|
100
|
+
return Boolean(field);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The fields table has no namespace column: every row in it belongs to the publisher's
|
|
104
|
+
* `custom` namespace, so no other namespace can have a stored field.
|
|
105
|
+
*/
|
|
106
|
+
isStored(namespace) {
|
|
107
|
+
return namespace === identity_1.CUSTOM_NAMESPACE;
|
|
108
|
+
}
|
|
109
|
+
async browse(options = {}, audience = access_1.ADMIN) {
|
|
110
|
+
if (options.namespace !== undefined && !this.isStored(options.namespace)) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
96
113
|
// Archived fields are hidden by default: most surfaces (member details, the
|
|
97
114
|
// filter picker, the importer) only ever want active fields. A caller-
|
|
98
115
|
// supplied `filter` can widen that — Settings pulls active and archived
|
|
@@ -103,7 +120,7 @@ class CustomFieldDefinitionsService {
|
|
|
103
120
|
const query = options.filter
|
|
104
121
|
? applyFilter(this.knex(TABLE), options.filter)
|
|
105
122
|
: (0, queries_1.activeFields)(this.knex);
|
|
106
|
-
return this.list(query);
|
|
123
|
+
return (0, access_1.readableFields)(audience, await this.list(query));
|
|
107
124
|
}
|
|
108
125
|
/**
|
|
109
126
|
* Decode a definition query into the domain, in the publisher's order.
|
|
@@ -113,10 +130,10 @@ class CustomFieldDefinitionsService {
|
|
|
113
130
|
*/
|
|
114
131
|
async list(query) {
|
|
115
132
|
const rows = await (0, queries_1.inFieldOrder)(query).select('*');
|
|
116
|
-
return rows.map((row) => zod_1.z.decode(codec_1.
|
|
133
|
+
return rows.map((row) => zod_1.z.decode(codec_1.metafieldCodec, row));
|
|
117
134
|
}
|
|
118
|
-
async read(key) {
|
|
119
|
-
const [field] = await this.list((0, queries_1.fieldByKey)(this.knex, key));
|
|
135
|
+
async read(namespace, key) {
|
|
136
|
+
const [field] = this.isStored(namespace) ? await this.list((0, queries_1.fieldByKey)(this.knex, key)) : [];
|
|
120
137
|
if (!field) {
|
|
121
138
|
throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
|
|
122
139
|
}
|
|
@@ -132,7 +149,8 @@ class CustomFieldDefinitionsService {
|
|
|
132
149
|
* batch, so two items sharing a name are caught and two items deriving the same
|
|
133
150
|
* key get distinct ones, exactly as if they had arrived as separate requests.
|
|
134
151
|
*/
|
|
135
|
-
async add(context, input) {
|
|
152
|
+
async add(context, namespace, input) {
|
|
153
|
+
(0, namespaces_1.assertDefinable)(namespace);
|
|
136
154
|
const requestedCount = Array.isArray(input) ? input.length : 0;
|
|
137
155
|
const parsed = AddFieldsInput.safeParse(input);
|
|
138
156
|
if (!parsed.success) {
|
|
@@ -252,7 +270,7 @@ class CustomFieldDefinitionsService {
|
|
|
252
270
|
/** `read` for a caller that is deciding rather than serving: absent is an answer. */
|
|
253
271
|
async findByKey(key, { executor = this.knex } = {}) {
|
|
254
272
|
const row = await executor(TABLE).where('key', key).first();
|
|
255
|
-
return row ? zod_1.z.decode(codec_1.
|
|
273
|
+
return row ? zod_1.z.decode(codec_1.metafieldCodec, row) : null;
|
|
256
274
|
}
|
|
257
275
|
/** The same entry `add` writes. Only the caller knows its transaction committed. */
|
|
258
276
|
async recordCreated(context, fields) {
|
|
@@ -310,7 +328,8 @@ class CustomFieldDefinitionsService {
|
|
|
310
328
|
* fresh rank, so a move is well-defined even where every rank is still the default.
|
|
311
329
|
* Returns every definition, archived included, matching what the request named.
|
|
312
330
|
*/
|
|
313
|
-
async reorder(context, input) {
|
|
331
|
+
async reorder(context, namespace, input) {
|
|
332
|
+
(0, namespaces_1.assertDefinable)(namespace);
|
|
314
333
|
const parsed = ReorderInput.safeParse(input);
|
|
315
334
|
if (!parsed.success) {
|
|
316
335
|
const issue = parsed.error.issues[0];
|
|
@@ -374,7 +393,7 @@ class CustomFieldDefinitionsService {
|
|
|
374
393
|
async readMany(db, keys) {
|
|
375
394
|
const rows = await db(TABLE).whereIn('key', keys).select('*');
|
|
376
395
|
const byKey = new Map(rows.map((row) => [row.key, row]));
|
|
377
|
-
return keys.map((key) => zod_1.z.decode(codec_1.
|
|
396
|
+
return keys.map((key) => zod_1.z.decode(codec_1.metafieldCodec, byKey.get(key)));
|
|
378
397
|
}
|
|
379
398
|
/**
|
|
380
399
|
* Pick a free key from the name's base: `base`, then `base_2`, `base_3`, ...
|
|
@@ -429,7 +448,10 @@ class CustomFieldDefinitionsService {
|
|
|
429
448
|
});
|
|
430
449
|
}
|
|
431
450
|
}
|
|
432
|
-
async edit(context, key, input) {
|
|
451
|
+
async edit(context, namespace, key, input) {
|
|
452
|
+
if (!this.isStored(namespace)) {
|
|
453
|
+
throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
|
|
454
|
+
}
|
|
433
455
|
const parsed = EditFieldInput.safeParse(input);
|
|
434
456
|
if (!parsed.success) {
|
|
435
457
|
throw new errors_1.default.ValidationError({
|
|
@@ -438,7 +460,7 @@ class CustomFieldDefinitionsService {
|
|
|
438
460
|
});
|
|
439
461
|
}
|
|
440
462
|
const patch = parsed.data;
|
|
441
|
-
const existing = await this.read(key);
|
|
463
|
+
const existing = await this.read(identity_1.CUSTOM_NAMESPACE, key);
|
|
442
464
|
// Key and type are immutable after creation: values are addressed by key
|
|
443
465
|
// and interpreted by type, so changing either would silently orphan or
|
|
444
466
|
// corrupt stored values. Reject a differing value rather than ignore it.
|
|
@@ -492,7 +514,7 @@ class CustomFieldDefinitionsService {
|
|
|
492
514
|
details: { primary_name: patch.name ?? existing.name, key },
|
|
493
515
|
});
|
|
494
516
|
}
|
|
495
|
-
return this.read(key);
|
|
517
|
+
return this.read(identity_1.CUSTOM_NAMESPACE, key);
|
|
496
518
|
}
|
|
497
519
|
/**
|
|
498
520
|
* Permanently delete a field and every member value attached to it (the FK
|
|
@@ -501,7 +523,10 @@ class CustomFieldDefinitionsService {
|
|
|
501
523
|
* which makes accidental data loss a deliberate two-step. Archiving is the
|
|
502
524
|
* reversible soft state (see `edit` with a status change).
|
|
503
525
|
*/
|
|
504
|
-
async destroy(context, key) {
|
|
526
|
+
async destroy(context, namespace, key) {
|
|
527
|
+
if (!this.isStored(namespace)) {
|
|
528
|
+
throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
|
|
529
|
+
}
|
|
505
530
|
const field = await this.knex(TABLE).where('key', key).first();
|
|
506
531
|
if (!field) {
|
|
507
532
|
throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
|
|
@@ -520,7 +545,7 @@ class CustomFieldDefinitionsService {
|
|
|
520
545
|
});
|
|
521
546
|
}
|
|
522
547
|
}
|
|
523
|
-
exports.
|
|
548
|
+
exports.MetafieldDefinitionsService = MetafieldDefinitionsService;
|
|
524
549
|
/**
|
|
525
550
|
* The shape a stated key has to have. Minting derives one that is usable by
|
|
526
551
|
* construction, so this is the check that path never needed: a caller stating its own
|
|
@@ -581,6 +606,24 @@ function applyFilter(query, filter) {
|
|
|
581
606
|
let mongoQuery;
|
|
582
607
|
try {
|
|
583
608
|
mongoQuery = nql(filter).toJSON();
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
throw new errors_1.default.BadRequestError({
|
|
612
|
+
message: 'Could not parse the filter parameter.',
|
|
613
|
+
property: 'filter',
|
|
614
|
+
err: err,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
// The table has no `namespace` column, so mongo-knex would compile this into SQL against a
|
|
618
|
+
// column that does not exist. Refuse it at the edge, naming the URL as the way to pick a
|
|
619
|
+
// namespace, rather than surface a database error.
|
|
620
|
+
if (filterReferencesAttribute(mongoQuery, 'namespace')) {
|
|
621
|
+
throw new errors_1.default.BadRequestError({
|
|
622
|
+
message: 'Filtering on namespace is not supported. Scope by the namespace route instead.',
|
|
623
|
+
property: 'filter',
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
584
627
|
knexify(query, mongoQuery, { tableName: TABLE });
|
|
585
628
|
}
|
|
586
629
|
catch (err) {
|
|
@@ -599,12 +642,15 @@ function applyFilter(query, filter) {
|
|
|
599
642
|
// $and/$or/$nor combinators — so a status filter at any nesting counts as the
|
|
600
643
|
// caller opting in to (or out of) archived fields.
|
|
601
644
|
function filterReferencesStatus(query) {
|
|
645
|
+
return filterReferencesAttribute(query, 'status');
|
|
646
|
+
}
|
|
647
|
+
function filterReferencesAttribute(query, attribute) {
|
|
602
648
|
return Object.entries(query).some(([key, value]) => {
|
|
603
|
-
if (key ===
|
|
649
|
+
if (key === attribute) {
|
|
604
650
|
return true;
|
|
605
651
|
}
|
|
606
652
|
if ((key === '$and' || key === '$or' || key === '$nor') && Array.isArray(value)) {
|
|
607
|
-
return value.some((sub) =>
|
|
653
|
+
return value.some((sub) => filterReferencesAttribute(sub, attribute));
|
|
608
654
|
}
|
|
609
655
|
return false;
|
|
610
656
|
});
|
|
@@ -0,0 +1,181 @@
|
|
|
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.METAFIELDS_RELATION = void 0;
|
|
7
|
+
exports.createMetafieldsFilterTransformer = createMetafieldsFilterTransformer;
|
|
8
|
+
// Translates the metafield clauses of a members filter into a query over the
|
|
9
|
+
// values table. A metafield is a publisher-defined field on a member; it is
|
|
10
|
+
// addressed as `namespace.key`, and a composite value such as an address names
|
|
11
|
+
// one of its parts by appending it: `namespace.key.part`.
|
|
12
|
+
//
|
|
13
|
+
// (metafields.key:'custom.company'+metafields.value:'Ghost')
|
|
14
|
+
// (metafields.key:'custom.shipping_address.country'+metafields.value:'GB')
|
|
15
|
+
// metafields.key:'custom.company' // has a value
|
|
16
|
+
// metafields.key:-'custom.shipping_address.country' // that part is not set
|
|
17
|
+
//
|
|
18
|
+
// The values table holds one row per stored value: `metafield_key`, `path`
|
|
19
|
+
// (empty for a plain field, the part's name for one part of a composite) and
|
|
20
|
+
// `value_text`. A filter addresses those columns directly, so each one becomes a
|
|
21
|
+
// single $elemMatch over them — positive asserts a matching row exists, $not
|
|
22
|
+
// asserts none does — with no lookup against the field definitions.
|
|
23
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
24
|
+
const identity_1 = require("@tryghost/metafield-types/identity");
|
|
25
|
+
const RELATION = 'metafields';
|
|
26
|
+
const PREFIX = `${RELATION}.`;
|
|
27
|
+
const KEY_ATTRIBUTE = `${PREFIX}key`;
|
|
28
|
+
const VALUE_ATTRIBUTE = `${PREFIX}value`;
|
|
29
|
+
const ROOT_PATH = '';
|
|
30
|
+
// Only recurse into plain objects/arrays; a RegExp, Date, etc. is a leaf value that
|
|
31
|
+
// must pass through by reference (recursing would strip a regex to `{}`).
|
|
32
|
+
function isPlainObject(value) {
|
|
33
|
+
return (typeof value === 'object' &&
|
|
34
|
+
value !== null &&
|
|
35
|
+
!Array.isArray(value) &&
|
|
36
|
+
(Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null));
|
|
37
|
+
}
|
|
38
|
+
function isMetafieldClause(node) {
|
|
39
|
+
if (!isPlainObject(node)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const keys = Object.keys(node);
|
|
43
|
+
return keys.length === 1 && keys[0].startsWith(PREFIX);
|
|
44
|
+
}
|
|
45
|
+
// A grouped compound: an $and whose clauses all target the relation and name exactly one
|
|
46
|
+
// `key`. Its clauses describe one leaf row.
|
|
47
|
+
//
|
|
48
|
+
// Exactly one, not at least one: two whole-field filters ANDed together arrive as a flat
|
|
49
|
+
// `key:'a'+key:'b'`, since a bare key clause needs no group of its own. That is two
|
|
50
|
+
// filters — a member with a value for both — not one leaf that is somehow both fields, so
|
|
51
|
+
// it has to fall through and be transformed a clause at a time.
|
|
52
|
+
function isMetafieldCompound(clauses) {
|
|
53
|
+
if (!Array.isArray(clauses) || clauses.length < 2 || !clauses.every(isMetafieldClause)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return clauses.filter((clause) => Object.keys(clause)[0] === KEY_ATTRIBUTE).length === 1;
|
|
57
|
+
}
|
|
58
|
+
// NQL compiles every negation in this grammar to `{$ne: <string>}`, so a negated clause
|
|
59
|
+
// can arrive in no other shape.
|
|
60
|
+
function negatedString(value) {
|
|
61
|
+
return isPlainObject(value) && typeof value.$ne === 'string' ? value.$ne : null;
|
|
62
|
+
}
|
|
63
|
+
const ANY_PATH = null;
|
|
64
|
+
// A stored key never contains a dot, so a full dotted identity is a key no stored
|
|
65
|
+
// row can equal.
|
|
66
|
+
function unmatchableLeaf(identity) {
|
|
67
|
+
return { key: (0, identity_1.formatIdentity)(identity), path: identity.partPath };
|
|
68
|
+
}
|
|
69
|
+
function parseLeafAddress(raw) {
|
|
70
|
+
const identity = typeof raw === 'string' ? (0, identity_1.parseIdentity)(raw) : null;
|
|
71
|
+
if (!identity) {
|
|
72
|
+
throw new errors_1.default.BadRequestError({
|
|
73
|
+
message: `A metafield filter names a field as namespace.key, for example (${KEY_ATTRIBUTE}:'custom.company'+${VALUE_ATTRIBUTE}:'Ghost').`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (identity.namespace !== identity_1.CUSTOM_NAMESPACE) {
|
|
77
|
+
return unmatchableLeaf(identity);
|
|
78
|
+
}
|
|
79
|
+
return { key: identity.key, path: identity.partPath };
|
|
80
|
+
}
|
|
81
|
+
function createMetafieldsFilterTransformer() {
|
|
82
|
+
// One (maybe-negated) $elemMatch over the leaf columns: positive is "a leaf pinned
|
|
83
|
+
// by these matches", `$not` is "no leaf does".
|
|
84
|
+
function buildElemMatch(target, value, negate) {
|
|
85
|
+
const match = { metafield_key: target.key };
|
|
86
|
+
if (value !== undefined) {
|
|
87
|
+
match.path = target.path ?? ROOT_PATH;
|
|
88
|
+
match.value_text = value;
|
|
89
|
+
}
|
|
90
|
+
else if (target.path !== ANY_PATH) {
|
|
91
|
+
match.path = target.path;
|
|
92
|
+
}
|
|
93
|
+
if (negate) {
|
|
94
|
+
return { [RELATION]: { $not: { $elemMatch: match } } };
|
|
95
|
+
}
|
|
96
|
+
return { [RELATION]: { $elemMatch: match } };
|
|
97
|
+
}
|
|
98
|
+
function toElemMatch(clauses) {
|
|
99
|
+
let target;
|
|
100
|
+
let value;
|
|
101
|
+
let hasValue = false;
|
|
102
|
+
for (const clause of clauses) {
|
|
103
|
+
const [attribute, clauseValue] = Object.entries(clause)[0];
|
|
104
|
+
if (attribute === KEY_ATTRIBUTE) {
|
|
105
|
+
if (negatedString(clauseValue) !== null) {
|
|
106
|
+
throw new errors_1.default.BadRequestError({
|
|
107
|
+
message: `A negated "${KEY_ATTRIBUTE}" stands alone; it cannot be grouped with a value.`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
target = parseLeafAddress(clauseValue);
|
|
111
|
+
}
|
|
112
|
+
else if (attribute === VALUE_ATTRIBUTE) {
|
|
113
|
+
if (hasValue) {
|
|
114
|
+
throw new errors_1.default.BadRequestError({
|
|
115
|
+
message: `A metafield filter takes one "${VALUE_ATTRIBUTE}" clause.`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
hasValue = true;
|
|
119
|
+
value = clauseValue;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
throw new errors_1.default.BadRequestError({
|
|
123
|
+
message: `Unsupported metafield filter clause "${attribute}".`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!target) {
|
|
128
|
+
throw new errors_1.default.BadRequestError({
|
|
129
|
+
message: `A metafield filter needs a "${KEY_ATTRIBUTE}" clause.`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return buildElemMatch(target, hasValue ? value : undefined, false);
|
|
133
|
+
}
|
|
134
|
+
function fromKeyClause(value) {
|
|
135
|
+
const negatedKey = negatedString(value);
|
|
136
|
+
if (negatedKey !== null) {
|
|
137
|
+
return buildElemMatch(parseLeafAddress(negatedKey), undefined, true);
|
|
138
|
+
}
|
|
139
|
+
return buildElemMatch(parseLeafAddress(value), undefined, false);
|
|
140
|
+
}
|
|
141
|
+
function transform(node) {
|
|
142
|
+
if (Array.isArray(node)) {
|
|
143
|
+
return node.map(transform);
|
|
144
|
+
}
|
|
145
|
+
if (!isPlainObject(node)) {
|
|
146
|
+
return node;
|
|
147
|
+
}
|
|
148
|
+
if (isMetafieldCompound(node.$and)) {
|
|
149
|
+
return toElemMatch(node.$and);
|
|
150
|
+
}
|
|
151
|
+
const out = {};
|
|
152
|
+
for (const [key, value] of Object.entries(node)) {
|
|
153
|
+
if (key === KEY_ATTRIBUTE) {
|
|
154
|
+
Object.assign(out, fromKeyClause(value));
|
|
155
|
+
}
|
|
156
|
+
else if (key.startsWith(PREFIX)) {
|
|
157
|
+
throw new errors_1.default.BadRequestError({
|
|
158
|
+
message: `A "${key}" filter must be grouped with its "${KEY_ATTRIBUTE}" clause, for example (${KEY_ATTRIBUTE}:'custom.a_field'+${VALUE_ATTRIBUTE}:'a value').`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
out[key] = transform(value);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
return (query) => transform(query);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The Member filter relation that exposes metafield values. A member has many leaf
|
|
171
|
+
* rows (one per field, or per part of a composite field), and a predicate asks whether
|
|
172
|
+
* one of them matches, so this joins the values table on member_id and mongo-knex emits
|
|
173
|
+
* it as a correlated `members.id IN (…)` subquery — composing with every other member
|
|
174
|
+
* filter.
|
|
175
|
+
*/
|
|
176
|
+
exports.METAFIELDS_RELATION = {
|
|
177
|
+
tableName: 'members_metafield_values',
|
|
178
|
+
tableNameAs: RELATION,
|
|
179
|
+
type: 'oneToOne',
|
|
180
|
+
joinFrom: 'member_id',
|
|
181
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bindings = exports.values = exports.definitions = exports.actingContext = void 0;
|
|
3
|
+
exports.bindings = exports.values = exports.definitions = exports.readableFields = exports.canWrite = exports.MEMBERS = exports.INTERNAL = exports.ADMIN = 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");
|
|
@@ -9,6 +9,15 @@ const actions_1 = require("./actions");
|
|
|
9
9
|
const config_1 = require("./config");
|
|
10
10
|
var actions_2 = require("./actions");
|
|
11
11
|
Object.defineProperty(exports, "actingContext", { enumerable: true, get: function () { return actions_2.actingContext; } });
|
|
12
|
+
// Which door a request came through, which is what decides how much of a member's
|
|
13
|
+
// answers it may see or change. Required wherever that is asked, so a new caller
|
|
14
|
+
// has to name itself rather than inherit an answer by default.
|
|
15
|
+
var access_1 = require("./access");
|
|
16
|
+
Object.defineProperty(exports, "ADMIN", { enumerable: true, get: function () { return access_1.ADMIN; } });
|
|
17
|
+
Object.defineProperty(exports, "INTERNAL", { enumerable: true, get: function () { return access_1.INTERNAL; } });
|
|
18
|
+
Object.defineProperty(exports, "MEMBERS", { enumerable: true, get: function () { return access_1.MEMBERS; } });
|
|
19
|
+
Object.defineProperty(exports, "canWrite", { enumerable: true, get: function () { return access_1.canWrite; } });
|
|
20
|
+
Object.defineProperty(exports, "readableFields", { enumerable: true, get: function () { return access_1.readableFields; } });
|
|
12
21
|
function init() {
|
|
13
22
|
// The three are constructed together below, so checking all of them keeps the "all or
|
|
14
23
|
// none" invariant explicit rather than trusting one to stand in for the rest.
|
|
@@ -17,25 +26,25 @@ function init() {
|
|
|
17
26
|
}
|
|
18
27
|
const { knex } = require('../../data/db');
|
|
19
28
|
const models = require('../../models');
|
|
20
|
-
const recordAction = ({ context, verb, subject, details }) => (0, actions_1.
|
|
29
|
+
const recordAction = ({ context, verb, subject, details }) => (0, actions_1.recordMetafieldAction)({ Action: models.Action, context, verb, subject, details });
|
|
21
30
|
// Resolved here, not in the service: reading config is this module's job, and
|
|
22
31
|
// the service is handed a number. A getter rather than a value because the
|
|
23
32
|
// ceiling is an operator setting that can change between requests, and a Ghost
|
|
24
33
|
// container holds no state across them.
|
|
25
34
|
const config = require('../../../shared/config');
|
|
26
|
-
exports.definitions = new definitions_service_1.
|
|
35
|
+
exports.definitions = new definitions_service_1.MetafieldDefinitionsService({
|
|
27
36
|
knex,
|
|
28
37
|
recordAction,
|
|
29
|
-
getMaxDefinitions: () => (0, config_1.resolveMaxDefinitions)(config.get('members:
|
|
38
|
+
getMaxDefinitions: () => (0, config_1.resolveMaxDefinitions)(config.get('members:metafields:maxDefinitions')),
|
|
30
39
|
});
|
|
31
40
|
// The values service reads the field definitions straight from the table, so
|
|
32
41
|
// it needs knex and the same ceiling — no handle on the definitions service.
|
|
33
|
-
exports.values = new values_service_1.
|
|
42
|
+
exports.values = new values_service_1.MetafieldValuesService({
|
|
34
43
|
knex,
|
|
35
|
-
getMaxDefinitions: () => (0, config_1.resolveMaxDefinitions)(config.get('members:
|
|
44
|
+
getMaxDefinitions: () => (0, config_1.resolveMaxDefinitions)(config.get('members:metafields:maxDefinitions')),
|
|
36
45
|
});
|
|
37
46
|
// Built after the values, which is what a binding routes into. It has no handle on the
|
|
38
47
|
// definitions: making a field is not part of binding to one, so a caller that needs both
|
|
39
48
|
// asks for both.
|
|
40
|
-
exports.bindings = new bindings_service_1.
|
|
49
|
+
exports.bindings = new bindings_service_1.MetafieldBindingsService({ knex, values: exports.values });
|
|
41
50
|
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KEY_CHARACTERS = void 0;
|
|
4
4
|
exports.mintableKey = mintableKey;
|
|
5
|
+
const identity_1 = require("@tryghost/metafield-types/identity");
|
|
5
6
|
// Neither package ships types.
|
|
6
7
|
const { stripInvisibleChars } = require('@tryghost/string');
|
|
7
8
|
const unidecode = require('unidecode');
|
|
8
9
|
/** Every character a key may contain. Nothing else survives minting. */
|
|
9
|
-
exports.KEY_CHARACTERS =
|
|
10
|
+
exports.KEY_CHARACTERS = identity_1.IDENTITY_SEGMENT;
|
|
10
11
|
/**
|
|
11
12
|
* Mint the key a field is addressed by, from the name a publisher chose.
|
|
12
13
|
*
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Metafield = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const metafield_types_1 = require("@tryghost/metafield-types");
|
|
6
6
|
const schema_1 = require("./schema");
|
|
7
|
-
|
|
8
|
-
exports.CustomField = zod_1.z.object({
|
|
7
|
+
exports.Metafield = zod_1.z.object({
|
|
9
8
|
id: zod_1.z.string(),
|
|
9
|
+
namespace: zod_1.z.string(),
|
|
10
10
|
key: zod_1.z.string(),
|
|
11
11
|
name: zod_1.z.string(),
|
|
12
|
-
type:
|
|
12
|
+
type: metafield_types_1.FieldTypeSchema,
|
|
13
13
|
status: schema_1.FieldStatusSchema,
|
|
14
14
|
createdAt: zod_1.z.date(),
|
|
15
15
|
updatedAt: zod_1.z.date().nullable(),
|
|
@@ -0,0 +1,34 @@
|
|
|
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.definableByPublisher = definableByPublisher;
|
|
7
|
+
exports.assertDefinable = assertDefinable;
|
|
8
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
9
|
+
const identity_1 = require("@tryghost/metafield-types/identity");
|
|
10
|
+
/**
|
|
11
|
+
* Who may define fields in a namespace.
|
|
12
|
+
*
|
|
13
|
+
* This is a property of the namespace, not of whoever is asking, so it is settled before
|
|
14
|
+
* any question about the caller. Asking the caller first gets the order wrong in a way
|
|
15
|
+
* that shows: a request to define a field in a namespace nobody owns would be refused for
|
|
16
|
+
* want of a permission, telling the caller to go and get one, when no permission would
|
|
17
|
+
* have helped.
|
|
18
|
+
*
|
|
19
|
+
* The publisher owns `custom` and nothing owns anything else yet. An app owning its own
|
|
20
|
+
* namespace resolves here too, and will not resolve to a permission at all: the caller
|
|
21
|
+
* has to *be* that app rather than hold a role, which is why the staff permission stays
|
|
22
|
+
* named after the publisher's fields rather than after metafields at large.
|
|
23
|
+
*/
|
|
24
|
+
function definableByPublisher(namespace) {
|
|
25
|
+
return namespace === identity_1.CUSTOM_NAMESPACE;
|
|
26
|
+
}
|
|
27
|
+
function assertDefinable(namespace) {
|
|
28
|
+
if (!definableByPublisher(namespace)) {
|
|
29
|
+
throw new errors_1.default.ValidationError({
|
|
30
|
+
message: `Fields cannot be defined in the "${namespace}" namespace.`,
|
|
31
|
+
property: 'namespace',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -4,7 +4,7 @@ exports.activeFields = activeFields;
|
|
|
4
4
|
exports.fieldByKey = fieldByKey;
|
|
5
5
|
exports.inFieldOrder = inFieldOrder;
|
|
6
6
|
const schema_1 = require("./schema");
|
|
7
|
-
const FIELDS_TABLE = '
|
|
7
|
+
const FIELDS_TABLE = 'members_metafields';
|
|
8
8
|
// Archived fields must stay out of every read and write, and nothing in the database
|
|
9
9
|
// enforces that — no constraint stops a value row referencing an archived field. A query
|
|
10
10
|
// that forgets the filter is a silent bug, so the filter lives in one place.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbBoundField = exports.
|
|
3
|
+
exports.DbBoundField = exports.DbMetafieldBinding = exports.DbMetafieldLeaf = exports.DbMetafieldValue = exports.WrittenBy = exports.DbMetafield = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const metafield_types_1 = require("@tryghost/metafield-types");
|
|
6
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
|
|
@@ -11,11 +11,11 @@ exports.FIELD_STATUS = { active: 'active', archived: 'archived' };
|
|
|
11
11
|
exports.FieldStatusSchema = zod_1.z.enum([exports.FIELD_STATUS.active, exports.FIELD_STATUS.archived]);
|
|
12
12
|
// The single source for the read projection and the knex table type below. `type` parses
|
|
13
13
|
// as the field-type enum, so the row carries the narrow type and no codec needs a cast.
|
|
14
|
-
exports.
|
|
14
|
+
exports.DbMetafield = zod_1.z.object({
|
|
15
15
|
id: zod_1.z.string(),
|
|
16
16
|
key: zod_1.z.string(),
|
|
17
17
|
name: zod_1.z.string(),
|
|
18
|
-
type:
|
|
18
|
+
type: metafield_types_1.FieldTypeSchema,
|
|
19
19
|
status: exports.FieldStatusSchema,
|
|
20
20
|
created_at: date_1.DbDate,
|
|
21
21
|
updated_at: date_1.DbDate.nullable(),
|
|
@@ -23,7 +23,7 @@ exports.DbCustomField = zod_1.z.object({
|
|
|
23
23
|
/**
|
|
24
24
|
* How a value arrived, not who caused it: who edited a member's fields is already an
|
|
25
25
|
* action, and Stripe is not a person. `type` is the namespace that makes `id` resolvable —
|
|
26
|
-
* `users`, `integrations`, `
|
|
26
|
+
* `users`, `integrations`, `members_metafield_bindings` — so the one writer that
|
|
27
27
|
* resolves in no table is the one with no id to give.
|
|
28
28
|
*/
|
|
29
29
|
exports.WrittenBy = zod_1.z.discriminatedUnion('type', [
|
|
@@ -31,12 +31,16 @@ exports.WrittenBy = zod_1.z.discriminatedUnion('type', [
|
|
|
31
31
|
zod_1.z.object({ type: zod_1.z.literal('integration'), id: zod_1.z.string() }),
|
|
32
32
|
zod_1.z.object({ type: zod_1.z.literal('binding'), id: zod_1.z.string() }),
|
|
33
33
|
zod_1.z.object({ type: zod_1.z.literal('import'), id: zod_1.z.null() }),
|
|
34
|
+
// A member writing their own answers. Resolvable in `members`, like the others,
|
|
35
|
+
// and the only writer whose changes leave nothing in the staff action log: that
|
|
36
|
+
// log records what staff did.
|
|
37
|
+
zod_1.z.object({ type: zod_1.z.literal('member'), id: zod_1.z.string() }),
|
|
34
38
|
]);
|
|
35
39
|
// One part of a member's value. What a `path` means is storage.ts's business, so the row
|
|
36
40
|
// carries it as a plain string.
|
|
37
|
-
exports.
|
|
41
|
+
exports.DbMetafieldValue = zod_1.z.object({
|
|
38
42
|
id: zod_1.z.string(),
|
|
39
|
-
|
|
43
|
+
metafield_key: zod_1.z.string(),
|
|
40
44
|
member_id: zod_1.z.string(),
|
|
41
45
|
path: zod_1.z.string(),
|
|
42
46
|
// Nullable like the column, though nothing here writes a null: a part with no value
|
|
@@ -55,18 +59,18 @@ exports.DbCustomFieldValue = zod_1.z.object({
|
|
|
55
59
|
// `type` takes no part in the assembly and is here as a gate: a value whose type has left
|
|
56
60
|
// the catalog is one the definitions list no longer returns either, so failing to parse
|
|
57
61
|
// is what drops it.
|
|
58
|
-
exports.
|
|
62
|
+
exports.DbMetafieldLeaf = zod_1.z.object({
|
|
59
63
|
member_id: zod_1.z.string(),
|
|
60
64
|
key: zod_1.z.string(),
|
|
61
|
-
type:
|
|
65
|
+
type: metafield_types_1.FieldTypeSchema,
|
|
62
66
|
path: zod_1.z.string(),
|
|
63
67
|
value_text: zod_1.z.string(),
|
|
64
68
|
});
|
|
65
|
-
exports.
|
|
69
|
+
exports.DbMetafieldBinding = zod_1.z.object({
|
|
66
70
|
id: zod_1.z.string(),
|
|
67
71
|
product_id: zod_1.z.string(),
|
|
68
72
|
port: zod_1.z.string(),
|
|
69
|
-
|
|
73
|
+
metafield_key: zod_1.z.string(),
|
|
70
74
|
created_at: date_1.DbDate,
|
|
71
75
|
updated_at: date_1.DbDate.nullable(),
|
|
72
76
|
});
|
|
@@ -74,5 +78,5 @@ exports.DbCustomFieldBinding = zod_1.z.object({
|
|
|
74
78
|
exports.DbBoundField = zod_1.z.object({
|
|
75
79
|
binding_id: zod_1.z.string(),
|
|
76
80
|
key: zod_1.z.string(),
|
|
77
|
-
type:
|
|
81
|
+
type: metafield_types_1.FieldTypeSchema,
|
|
78
82
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toMetafieldsResponse = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const case_keys_1 = require("../../lib/case-keys");
|
|
6
|
+
const models_1 = require("./models");
|
|
7
|
+
const MetafieldResource = zod_1.z.object({
|
|
8
|
+
namespace: zod_1.z.string(),
|
|
9
|
+
key: zod_1.z.string(),
|
|
10
|
+
name: zod_1.z.string(),
|
|
11
|
+
type: zod_1.z.string(),
|
|
12
|
+
status: zod_1.z.string(),
|
|
13
|
+
created_at: zod_1.z.date(),
|
|
14
|
+
updated_at: zod_1.z.date().nullable(),
|
|
15
|
+
});
|
|
16
|
+
const MetafieldsResponse = zod_1.z.object({ members_metafields: zod_1.z.array(MetafieldResource) });
|
|
17
|
+
exports.toMetafieldsResponse = zod_1.z
|
|
18
|
+
.array(models_1.Metafield)
|
|
19
|
+
.transform((fields) => ({
|
|
20
|
+
members_metafields: fields.map((field) => (0, case_keys_1.snakeKeys)(field)),
|
|
21
|
+
}))
|
|
22
|
+
.pipe(MetafieldsResponse);
|