ghost 6.62.0 → 6.64.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/bin/lib/duplicate-deps.js +204 -0
- package/bin/validate-adapters.js +50 -6
- package/components/tryghost-adapter-base-jobs-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-api-framework-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.6.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.1.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/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/css/screen.css +1 -1
- package/content/themes/source/package.json +7 -7
- package/content/themes/source/partials/post-card.hbs +1 -1
- package/core/boot.js +17 -9
- package/core/built/admin/assets/Combination-DWfJPcz9.js +41 -0
- package/core/built/admin/assets/{PolarAngleAxis-pkMyqz8g.js → PolarAngleAxis-a81ieQUO.js} +2 -2
- package/core/built/admin/assets/RadialBarChart-CHRBhiBN.js +1 -0
- package/core/built/admin/assets/_baseAssignValue-DcPokYW8.js +1 -0
- package/core/built/admin/assets/about-39CAfroa.js +1 -0
- package/core/built/admin/assets/accordion-CJmRaqK7.js +1 -0
- package/core/built/admin/assets/account-migration-DYf7skbb.js +1 -0
- package/core/built/admin/assets/action-list-Crnh-_UI.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-By69CgCp.js → account-migration-KA6Jjf9L.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +3 -3
- 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-BJcPMrDd.js → bluesky-sharing-C7nTDMvw.js} +9 -9
- package/core/built/admin/assets/activitypub/{content-formatters-3rTHg3mp.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-DUR4xSkV.js → domain-fZ-X8i4-.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-BqtfzxCp.js → edit-profile-BqMLQV88.js} +6 -6
- package/core/built/admin/assets/activitypub/{explore-BxuJd-J4.js → explore-B4yPoP8E.js} +10 -10
- package/core/built/admin/assets/activitypub/{feed-BM1TcksA.js → feed-BRC_mvAe.js} +9 -9
- package/core/built/admin/assets/activitypub/{field-Dn7HHx2-.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-CCjkuow2.js → inbox-BW-yvUoN.js} +10 -10
- package/core/built/admin/assets/activitypub/{inbox-d8oJRTf4.js → inbox-CbTTdDQ3.js} +2 -2
- package/core/built/admin/assets/activitypub/{label-1AA_s9Fa.js → label-kPMmYI7l.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-CnmxEB8w.js → moderation-CuidJzc0.js} +10 -10
- package/core/built/admin/assets/activitypub/{note-ZKc_Bnba.js → note-CAMRRSqD.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-CB2tJZau.js → notifications-BmNlXGgp.js} +9 -9
- package/core/built/admin/assets/activitypub/{onboarding-BsJ9E5qf.js → onboarding-Bqg7fh_L.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-CSUJJIlh.js → onboarding-Dmun9jr_.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-0-JZnUkI.js → preferences--oH19wf9.js} +11 -11
- package/core/built/admin/assets/activitypub/{profile-C35cFIhM.js → profile-DH_tQx2D.js} +14 -14
- package/core/built/admin/assets/activitypub/{reply-DYK5vVqb.js → reply-Cap9PxSH.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-BD4vscyc.js → routes-DdyY9uFF.js} +30 -30
- package/core/built/admin/assets/activitypub/{separator-BbQqTm-p.js → separator-CAxxu8VV.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-BsMROdqh.js → settings-B6EEcVkW.js} +6 -6
- package/core/built/admin/assets/activitypub/{site-O0RL9YCN.js → site-BYps63oh.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-BMSx7c_X.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-CLlOZTyt.js → step-3-C-qBbcUo.js} +11 -11
- package/core/built/admin/assets/activitypub/{tabs-Dxk-b9M-.js → tabs-DLhZtVde.js} +6 -6
- package/core/built/admin/assets/activitypub/{topic-filter-5yZH6tId.js → topic-filter-DsUOuu0k.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CV50qe03.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-Cbvzgjzd.js → use-reply-chain-data-AJi9zkzm.js} +5 -5
- package/core/built/admin/assets/activitypub/{users-BjcoP_HC.js → users-inweyKL5.js} +186 -160
- package/core/built/admin/assets/activitypub/{x-DbQZOLiI.js → x-DR8AT3-4.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-B12oRifD.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-CTvXiIqY.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-CJf92edY.js +1 -0
- package/core/built/admin/assets/analytics-PHR1Fggi.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-Bql0CXdp.js +1 -0
- package/core/built/admin/assets/api-keys-CubH-7_H.js +1 -0
- package/core/built/admin/assets/{api-keys-3-0vJC3Y.js → api-keys-omLA6iYn.js} +1 -1
- package/core/built/admin/assets/{audience-CtbA54Sn.js → audience-6GXevic-.js} +1 -1
- package/core/built/admin/assets/automated-emails-DJQ4uNZo.js +1 -0
- package/core/built/admin/assets/automations-DcZSLFDU.js +1 -0
- package/core/built/admin/assets/automations-hDVcRPjh.js +1 -0
- package/core/built/admin/assets/avatar-flipboard-DiAGgshK.js +1 -0
- package/core/built/admin/assets/badge-qP06OT7C.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-CJw4d4Mf.js +1 -0
- package/core/built/admin/assets/box-CZ_LSVSy.js +1 -0
- package/core/built/admin/assets/brand-icon-D3ffDYaC.js +1 -0
- package/core/built/admin/assets/breadcrumb-Chn1Ip_q.js +1 -0
- package/core/built/admin/assets/button-DvIh2Bq-.js +1 -0
- package/core/built/admin/assets/calendar-1QGoStFM.js +1 -0
- package/core/built/admin/assets/card-FHQ4D10L.js +1 -0
- package/core/built/admin/assets/chart-helpers-CNpA7re_.js +1 -0
- package/core/built/admin/assets/{chart-BrhA41Zi.js → chart-jVz9Nf9t.js} +9 -9
- package/core/built/admin/assets/checkbox-DKht9xMl.js +1 -0
- package/core/built/admin/assets/{chunk-KS7C4IRE-DCsL4i4D.js → chunk-KS7C4IRE-DhOcHQOU.js} +1 -1
- package/core/built/admin/assets/chunk.368.22ce1b732c0b40abd3f1.js +479 -0
- package/core/built/admin/assets/chunk.524.cc9be98e44890cedc8c8.js +25 -0
- package/core/built/admin/assets/{chunk.582.5ed5d124cfca9c259b9c.js → chunk.582.e6fe742b3217207b11a6.js} +23 -30
- package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js → chunk.972.4b8c97f93d66db15e751.js} +13582 -11017
- package/core/built/admin/assets/clean-tracked-url-dkN05vOf.js +1 -0
- package/core/built/admin/assets/code-editor-view-BLQIV85-.js +1 -0
- package/core/built/admin/assets/code-editor-view-Cg5eJxcr.js +1 -0
- package/core/built/admin/assets/color-picker-DQoghc0t.js +3 -0
- package/core/built/admin/assets/color-picker-field-BbeK5RRs.js +1 -0
- package/core/built/admin/assets/combobox-weO4FBbv.js +1 -0
- package/core/built/admin/assets/command-DlLrCm9V.js +1 -0
- package/core/built/admin/assets/comments-BjTitLmP.js +1 -0
- package/core/built/admin/assets/components-CwL6a6As-CCRaoCeP.js +1 -0
- package/core/built/admin/assets/{config-DOMQDZGZ.js → config-BPljH6wF.js} +1 -1
- package/core/built/admin/assets/confirmation-context-IyqoXVk-.js +1 -0
- package/core/built/admin/assets/container-DAimjOF_.js +1 -0
- package/core/built/admin/assets/content-api-modal-0ZjuLcQ8.js +1 -0
- package/core/built/admin/assets/context-menu-BtioYFrg.js +1 -0
- package/core/built/admin/assets/copy-field-C6ujqJlP.js +1 -0
- package/core/built/admin/assets/create-ghost-browse-value-source-DAZMWZNq.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-Dm8QI_J4.js +1 -0
- package/core/built/admin/assets/data-list-C0CKkeWW.js +1 -0
- package/core/built/admin/assets/date-range-select-DrjHQdgz.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-CXVOzBWu.js +2 -0
- package/core/built/admin/assets/detail-page-QpLIL-EO.js +1 -0
- package/core/built/admin/assets/dirty-confirm-dialog-DWwxREGh.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-CLZhMv8J.js +1 -0
- package/core/built/admin/assets/dist-BRQkNYN1.js +11 -0
- package/core/built/admin/assets/{dist-B3keLvjj.js → dist-BXHrDV19.js} +2 -2
- package/core/built/admin/assets/dist-Bf-HQtVL.js +1 -0
- package/core/built/admin/assets/dist-Bm-PB47a.js +6 -0
- package/core/built/admin/assets/{dist-B6bJuxL3.js → dist-C2lYC76d.js} +1 -1
- package/core/built/admin/assets/dist-CUwte9v_.js +1 -0
- package/core/built/admin/assets/dist-Cm2qbZKL.js +1 -0
- package/core/built/admin/assets/{dist-BtaTA7Ii.js → dist-D7yMk931.js} +1 -1
- package/core/built/admin/assets/{dist-zwwGt8Yf.js → dist-DNAdeRyr.js} +1 -1
- package/core/built/admin/assets/dist-DS6zpxid.js +1 -0
- package/core/built/admin/assets/dist-DVVKsyPh.js +23 -0
- package/core/built/admin/assets/dist-DxfQlAmn.js +1 -0
- package/core/built/admin/assets/dist-DziNzWI2.js +1 -0
- package/core/built/admin/assets/dist-FEQQAwTe.js +1 -0
- package/core/built/admin/assets/dist-nmRzULcD.js +1 -0
- package/core/built/admin/assets/domain-DBrBic6q.js +1 -0
- package/core/built/admin/assets/dropzone-D61QVcEY.js +5 -0
- package/core/built/admin/assets/edit-profile-jCqJBxqw.js +1 -0
- package/core/built/admin/assets/editor-8Fm4UBEd.js +8 -0
- package/core/built/admin/assets/editor-screen-BSX_uC2B.js +27 -0
- package/core/built/admin/assets/email-previews-Bs-JDhIw.js +1 -0
- package/core/built/admin/assets/email-sending-status-context-C76KYL1V.js +1 -0
- package/core/built/admin/assets/email-sending-status-provider-DnNhlIvt.js +1 -0
- package/core/built/admin/assets/emails-CgvIIz3q.js +1 -0
- package/core/built/admin/assets/{embed-signup-form-modal-Ds6mPEi5.js → embed-signup-form-modal-DqtBs1WE.js} +1 -1
- package/core/built/admin/assets/empty-indicator-L4Ql5Db7.js +1 -0
- package/core/built/admin/assets/en-Cx75dJv5.js +1 -0
- package/core/built/admin/assets/esm--rhhUGIG.js +2 -0
- package/core/built/admin/assets/explore-DXhDCtKw.js +1 -0
- package/core/built/admin/assets/feature-flag-overrides-context-CrcQKm9Z.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-e2q6TcH6.js +1 -0
- package/core/built/admin/assets/feed-mmt4noNL.js +1 -0
- package/core/built/admin/assets/feedback-BTPBQLT5.js +1 -0
- package/core/built/admin/assets/field-p7KnP98B.js +1 -0
- package/core/built/admin/assets/filter-bar-CiIKBg3D.js +1 -0
- package/core/built/admin/assets/filter-sources-I-dRaQxl.js +1 -0
- package/core/built/admin/assets/filters-1injvyxJ.js +1 -0
- package/core/built/admin/assets/filters-DnpO6GWX.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-wr73Dldc.js +1 -0
- package/core/built/admin/assets/focus-koenig-editor-on-bottom-click-CkFA8EkI.js +1 -0
- package/core/built/admin/assets/framework-provider-DB-svAju.js +4 -0
- package/core/built/admin/assets/{ghost-8eabfc1ece81b03cf7b90780d31487e8.css → ghost-8fd5bb799356bbefb254210ba7e1236d.css} +1 -1
- package/core/built/admin/assets/{ghost-c2848508292937415f20f5a830eea59e.js → ghost-a1e6e172c6917e8ef8fa6b0007756d9b.js} +71 -46
- package/core/built/admin/assets/{ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css → ghost-dark-cac0cb2d5bb78e9bd14d8d1c2768ed45.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-C3H7YFXO.js +1 -0
- package/core/built/admin/assets/{gift-links-DLDS7ljO.js → gift-links-D8gl_yJS.js} +1 -1
- package/core/built/admin/assets/global-data-context-3MItMry2.js +1 -0
- package/core/built/admin/assets/grid-5vplBYri.js +1 -0
- package/core/built/admin/assets/growth-B1og1kQp.js +1 -0
- package/core/built/admin/assets/growth-Bf6OHZlK.js +3 -0
- package/core/built/admin/assets/heading-BB21QAbb.js +1 -0
- package/core/built/admin/assets/history-modal-t_qEnxPx.js +1 -0
- package/core/built/admin/assets/hooks-DYfTEQLk.js +2 -0
- package/core/built/admin/assets/html-field-COLQbV4r.js +1 -0
- package/core/built/admin/assets/iframe-buffering-Bq1el-gw.js +1 -0
- package/core/built/admin/assets/images-4mvoc3lB.js +1 -0
- package/core/built/admin/assets/{inbox-Mcp4DXZN.js → inbox-DgSZ6w2p.js} +34 -34
- package/core/built/admin/assets/index-B3HZU2i8.css +2 -0
- package/core/built/admin/assets/index-DDd-zhNL.js +4 -0
- package/core/built/admin/assets/index.es-Ct5XQNSj.js +1 -0
- package/core/built/admin/assets/inline-B8H6NzIw.js +1 -0
- package/core/built/admin/assets/input-group-CLIN2ykI.js +1 -0
- package/core/built/admin/assets/{input-surface-BsrIZeSF.js → input-surface-wSO5CRy5.js} +1 -1
- package/core/built/admin/assets/integration-header-CKGrXNVP.js +1 -0
- package/core/built/admin/assets/{integrations-Bjbu86Z0.js → integrations-DnJrZnHv.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-BaN2mdhI.js +1 -0
- package/core/built/admin/assets/jsx-runtime-BlNNh7jY.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +4 -4
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +2 -2
- package/core/built/admin/assets/{koenig-lexical-DBSqAuiI.js → koenig-lexical-CXVgQFUk.js} +2 -2
- package/core/built/admin/assets/koenig-loader-B1XPPY5C.js +2 -0
- package/core/built/admin/assets/kpi-card-CRLPVlFW.js +1 -0
- package/core/built/admin/assets/kpi-card-CRPbW8NZ.js +1 -0
- package/core/built/admin/assets/kpi-helpers-D7zT96bp.js +1 -0
- package/core/built/admin/assets/kpi-tabs-DE_yh9Go.js +1 -0
- package/core/built/admin/assets/label-CgeBxy4o.js +1 -0
- package/core/built/admin/assets/label-picker-DGDp4Vwr.js +1 -0
- package/core/built/admin/assets/labels-KMkFoRG6.js +1 -0
- package/core/built/admin/assets/layout-DWnOMly4.js +1 -0
- package/core/built/admin/assets/lib-_QYaUU89.js +2 -0
- package/core/built/admin/assets/link-helpers-8J7ixADV.js +1 -0
- package/core/built/admin/assets/links-FwNCVyVe.js +1 -0
- package/core/built/admin/assets/list-page-DLMP0PxC.js +1 -0
- package/core/built/admin/assets/luxon-BXCsmep3.js +1 -0
- package/core/built/admin/assets/member-activity-ClMWFyvN.js +1 -0
- package/core/built/admin/assets/member-activity-feed-BQY5Rwaj.js +1 -0
- package/core/built/admin/assets/member-custom-fields-Dd_ie1zG.js +1 -0
- package/core/built/admin/assets/member-detail-DjwkGVne.js +1 -0
- package/core/built/admin/assets/members--N9lhk1N.js +4 -0
- package/core/built/admin/assets/modal-page-Dz-9TP4E.js +1 -0
- package/core/built/admin/assets/moderation-DfYBKq9s.js +1 -0
- package/core/built/admin/assets/multi-select-combobox-BcBEzUW_.js +1 -0
- package/core/built/admin/assets/navigation-modal-Cd-scywg.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-CFRY6vg_.js +1 -0
- package/core/built/admin/assets/newsletter-emails-4ntck1AC.js +1 -0
- package/core/built/admin/assets/newsletter-o6eCJtab.js +1 -0
- package/core/built/admin/assets/newsletters-Bmlv0edG.js +1 -0
- package/core/built/admin/assets/newsletters-Dz77m__5.js +1 -0
- package/core/built/admin/assets/note-CWs3LF-0.js +1 -0
- package/core/built/admin/assets/notifications-BjSvTJqh.js +1 -0
- package/core/built/admin/assets/{nql-BLpCn7qX.js → nql-DnDc7UuU.js} +1 -1
- package/core/built/admin/assets/nql-lang-CeTB_Bhv.js +19 -0
- package/core/built/admin/assets/{offers-9J1CSiLy.js → offers-ByWSK0rR.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-Bc9A196w.js +1 -0
- package/core/built/admin/assets/onboarding-B4hfBi9a.js +1 -0
- package/core/built/admin/assets/onboarding-DV_XbHts.js +1 -0
- package/core/built/admin/assets/onboarding-route-Di1LJYEE.js +1 -0
- package/core/built/admin/assets/overview-DE7NHZQN.js +1 -0
- package/core/built/admin/assets/overview-DEF-ZY6x.js +1 -0
- package/core/built/admin/assets/page-header-B-G_g42C.js +1 -0
- package/core/built/admin/assets/pagemenu-CnkTDO71.js +1 -0
- package/core/built/admin/assets/pages-DZ_YGB1D.js +1 -0
- package/core/built/admin/assets/pages-route-VT7r7YhG.js +1 -0
- package/core/built/admin/assets/papaparse.min-BlhnCUXn.js +10 -0
- package/core/built/admin/assets/pintura-modal-BljM7hf8.js +1 -0
- package/core/built/admin/assets/portal-frame-D9V8iq5x.js +1 -0
- package/core/built/admin/assets/portal-modal-B5OOR9ZD.js +1 -0
- package/core/built/admin/assets/post-analytics-context-bt2YTBAC.js +1 -0
- package/core/built/admin/assets/post-analytics-header-J0WciLx6.js +1 -0
- package/core/built/admin/assets/post-analytics-provider-CgreZDjS.js +1 -0
- package/core/built/admin/assets/post-analytics-rGb-PgD-.js +1 -0
- package/core/built/admin/assets/post-helpers-C88JBa4D.js +1 -0
- package/core/built/admin/assets/post-share-modal-D0BbTo4W.js +1 -0
- package/core/built/admin/assets/post-utils-DKF4mfuo.js +1 -0
- package/core/built/admin/assets/posts-list-screen-CO5jAWoU.js +2 -0
- package/core/built/admin/assets/posts-route-DV-CXWK9.js +1 -0
- package/core/built/admin/assets/posts-tcoO3iRG.js +1 -0
- package/core/built/admin/assets/preferences-Tac29oG6.js +5 -0
- package/core/built/admin/assets/preview-chrome-C2lQgZBZ.js +1 -0
- package/core/built/admin/assets/preview-modal-CjRbBr8U.js +1 -0
- package/core/built/admin/assets/profile-yiM1f_Og.js +1 -0
- package/core/built/admin/assets/prop-types-6lTeqvom.js +1 -0
- package/core/built/admin/assets/radio-group-CKROWsw4.js +1 -0
- package/core/built/admin/assets/react-BXwhMTuc.js +1 -0
- package/core/built/admin/assets/{jsx-runtime-DX3WBWVO.js → react-dom-CMOtJ5MH.js} +2 -2
- package/core/built/admin/assets/{referrers-CnhCMWCs.js → referrers-1R8bPpoV.js} +1 -1
- package/core/built/admin/assets/router-provider-CU87qxXw.js +1 -0
- package/core/built/admin/assets/schemas-BoYdB6YU.js +64 -0
- package/core/built/admin/assets/select-CKaC4ggu.js +1 -0
- package/core/built/admin/assets/settings-81kxUa28.js +1 -0
- package/core/built/admin/assets/settings-BVXFqBVK.js +4 -0
- package/core/built/admin/assets/settings-breadcrumbs-czGRx-iu.js +1 -0
- package/core/built/admin/assets/{shade-app-CcS3f8MZ-vwURazk8.js → shade-app-CcS3f8MZ-D5BnfHck.js} +1 -1
- package/core/built/admin/assets/share-modal-BL-16PiB.js +1 -0
- package/core/built/admin/assets/skeleton-DwCva0-b.js +1 -0
- package/core/built/admin/assets/slack-modal-D7W_fNON.js +1 -0
- package/core/built/admin/assets/sort-button-Dquc83jY.js +1 -0
- package/core/built/admin/assets/source-icon-CQboPZQ3.js +1 -0
- package/core/built/admin/assets/stack-BURz77VS.js +1 -0
- package/core/built/admin/assets/stats-Cvp8Ep5m.js +1 -0
- package/core/built/admin/assets/stats-view-NDja_g96.js +1 -0
- package/core/built/admin/assets/step-1-BKhjy0PV.js +1 -0
- package/core/built/admin/assets/step-2-PxA1OM7R.js +1 -0
- package/core/built/admin/assets/step-3-BJbAiq16.js +2 -0
- package/core/built/admin/assets/stripe-button-C3cfCAou.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-FyE3Ey2c.js +1 -0
- package/core/built/admin/assets/style-Wd9nQubI.css +1 -0
- package/core/built/admin/assets/switch-zfUUy9Jh.js +1 -0
- package/core/built/admin/assets/table-dE4GTIRT.js +1 -0
- package/core/built/admin/assets/tabs-B3XK93sR.js +1 -0
- package/core/built/admin/assets/tag-detail-D4P-tZdM.js +2 -0
- package/core/built/admin/assets/tag-picker-BNSZ6Qxw.js +1 -0
- package/core/built/admin/assets/tags-BsZcaJ6h.js +1 -0
- package/core/built/admin/assets/{tags-zTFiKRpb.js → tags-NwZ7BPK6.js} +1 -1
- package/core/built/admin/assets/testimonials-modal-Dkt6ODLW.js +1 -0
- package/core/built/admin/assets/text-DC-ot6u2.js +1 -0
- package/core/built/admin/assets/textarea-B4FffFmE.js +1 -0
- package/core/built/admin/assets/themes-DtcEFQWm.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-a77Z2VMH.js +1 -0
- package/core/built/admin/assets/tiers-6mPzCXlM.js +1 -0
- package/core/built/admin/assets/toggle-group-D2p4okmJ.js +1 -0
- package/core/built/admin/assets/topic-filter-DVOe3D-b.js +1 -0
- package/core/built/admin/assets/transistor-modal-C8RMkhOD.js +1 -0
- package/core/built/admin/assets/trend-badge-B9v7kMF8.js +1 -0
- package/core/built/admin/assets/unsplash-modal-DXt7Brum.js +1 -0
- package/core/built/admin/assets/use-analytics-data-ChHqiZXv.js +1 -0
- package/core/built/admin/assets/use-availability-hPdeG76y.js +1 -0
- package/core/built/admin/assets/{use-check-theme-limit-error-Cw3WQHy8.js → use-check-theme-limit-error-JD-J5aaZ.js} +2 -2
- package/core/built/admin/assets/use-email-sending-status-DNMtL5B9.js +1 -0
- package/core/built/admin/assets/use-filter-params-BSFigDIn.js +1 -0
- package/core/built/admin/assets/use-filterable-api-CEMSAYPg.js +1 -0
- package/core/built/admin/assets/use-form-DJKZJhQh.js +1 -0
- package/core/built/admin/assets/use-growth-stats-CqqdnESN.js +1 -0
- package/core/built/admin/assets/use-host-limits-mpTkZIq8.js +1 -0
- package/core/built/admin/assets/use-limiter-DBGjYqu7.js +19 -0
- package/core/built/admin/assets/use-navigate-with-base-path-88EQpw2k.js +1 -0
- package/core/built/admin/assets/use-pintura-config-BmU45Ruy.js +1 -0
- package/core/built/admin/assets/use-pintura-editor-Bz2bZ4iT.js +1 -0
- package/core/built/admin/assets/use-post-referrers-DyPjyBSN.js +1 -0
- package/core/built/admin/assets/use-query-params-DlOHCo-R.js +1 -0
- package/core/built/admin/assets/use-reply-chain-data-DmD2yX5D.js +1 -0
- package/core/built/admin/assets/use-router-history-entry-CKx9LhqD.js +1 -0
- package/core/built/admin/assets/use-sending-eta-CQeG6OP_.js +1 -0
- package/core/built/admin/assets/use-setting-group-Dza6bQ7W.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-BIjI5mL3.js +1 -0
- package/core/built/admin/assets/use-simple-pagination-DwVxe-Lo.js +1 -0
- package/core/built/admin/assets/use-staff-users-i_Mq9OGR.js +1 -0
- package/core/built/admin/assets/{use-tinybird-query-CSnGXzZh.js → use-tinybird-query-DXoF74NM.js} +1 -1
- package/core/built/admin/assets/use-tinybird-token-Lk1RSn0t.js +1 -0
- package/core/built/admin/assets/use-unsaved-changes-guard-CKVPWz_F.js +1 -0
- package/core/built/admin/assets/use-upgrade-route-CVjDlNSs.js +1 -0
- package/core/built/admin/assets/{use-url-input-DFr8lCbS.js → use-url-input-DpmbDppC.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-BU4QCHE7.js +1 -0
- package/core/built/admin/assets/{users-Cg-OeGzC.js → users-C60_hpn6.js} +1 -1
- package/core/built/admin/assets/{validator-Cr0sMKuW.js → validator-DlFQP70j.js} +1 -1
- package/core/built/admin/assets/{vendor-5753232c7d6ffb3c44e1eb0a02133953.js → vendor-2dd263291938d407802568d3357ec69e.js} +19 -17
- package/core/built/admin/assets/virtual-list-DScLNuU8.js +1 -0
- package/core/built/admin/assets/web-BZS-t4H0.js +1 -0
- package/core/built/admin/assets/web-DxAUxnK3.js +2 -0
- package/core/built/admin/assets/zapier-modal-BafLUO4R.js +1 -0
- package/core/built/admin/index.html +38 -34
- package/core/cli/command.js +17 -2
- package/core/frontend/meta/asset-url.js +1 -16
- package/core/frontend/services/asset-hash/index.js +32 -2
- package/core/frontend/services/routing/controllers/unsubscribe.js +2 -1
- package/core/frontend/services/rss/generate-feed.js +1 -1
- package/core/frontend/services/theme-engine/active.js +2 -4
- package/core/frontend/services/theme-engine/i18n/i18n.js +11 -1
- package/core/frontend/services/theme-engine/i18next/theme-i18n.js +1 -1
- package/core/frontend/web/middleware/llms-discovery.js +34 -43
- package/core/server/adapters/jobs/InMemoryJobsBackend.js +74 -29
- package/core/server/adapters/route-settings/S3RouteSettingsStore.js +1 -1
- package/core/server/adapters/storage/LocalFilesStorage.js +12 -2
- package/core/server/adapters/storage/LocalStorageBase.js +13 -0
- package/core/server/api/endpoints/actions.js +2 -1
- package/core/server/api/endpoints/authentication.js +1 -2
- package/core/server/api/endpoints/authors-public.js +15 -5
- package/core/server/api/endpoints/automated-emails.js +3 -2
- package/core/server/api/endpoints/automations.js +77 -49
- package/core/server/api/endpoints/emails.js +21 -3
- package/core/server/api/endpoints/index.js +9 -1
- package/core/server/api/endpoints/invites.js +2 -1
- package/core/server/api/endpoints/labels.js +6 -2
- package/core/server/api/endpoints/member-metafields-members.js +23 -0
- package/core/server/api/endpoints/member-metafields.js +34 -21
- package/core/server/api/endpoints/members-account.js +42 -0
- package/core/server/api/endpoints/members.js +22 -6
- 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/pages.js +2 -6
- package/core/server/api/endpoints/posts-public.js +15 -5
- package/core/server/api/endpoints/posts.js +3 -11
- package/core/server/api/endpoints/snippets.js +2 -1
- package/core/server/api/endpoints/tags-public.js +22 -3
- package/core/server/api/endpoints/tags.js +6 -2
- 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 +4 -8
- package/core/server/api/endpoints/utils/api-filter-utils.js +232 -6
- 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 +16 -2
- 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 +4 -0
- package/core/server/api/endpoints/utils/serializers/output/member-metafields.js +3 -3
- 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/tiers.js +10 -2
- package/core/server/api/endpoints/utils/serializers/output/users.js +0 -10
- package/core/server/api/endpoints/utils/serializers/output/utils/clean.js +2 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/extra-attrs.js +3 -0
- package/core/server/api/endpoints/utils/validators/input/settings.js +15 -1
- package/core/server/data/exporter/exporter.js +2 -1
- package/core/server/data/exporter/table-lists.js +6 -8
- package/core/server/data/importer/importers/data/settings-importer.js +8 -2
- 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/migrations/versions/6.64/2026-09-03-15-40-25-add-member-access-to-members-metafields.js +12 -0
- package/core/server/data/migrations/versions/6.64/2026-09-08-11-01-12-add-posts-auto-excerpt-and-reading-time-columns.js +17 -0
- package/core/server/data/migrations/versions/6.64/2026-09-09-13-56-18-add-automation-run-updated-at-indexes.js +6 -0
- package/core/server/data/migrations/versions/6.64/2026-09-09-13-56-18-add-tinybird-syncs-table.js +10 -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 +46 -20
- package/core/server/data/seeders/README.md +48 -0
- package/core/server/data/seeders/importers/automations-importer.js +0 -1
- package/core/server/data/seeders/importers/comments-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/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-stripe-customers-subscriptions-importer.js +1 -1
- package/core/server/data/seeders/utils/blog-info.js +4 -3
- package/core/server/data/seeders/utils/event-generator.js +72 -46
- package/core/server/data/tinybird/datasources/_mv_automation_run_steps.datasource +10 -0
- package/core/server/data/tinybird/datasources/_mv_automation_runs.datasource +10 -0
- package/core/server/data/tinybird/datasources/_mv_pending_automation_runs.datasource +10 -0
- package/core/server/data/tinybird/datasources/automation_run_events.datasource +12 -0
- package/core/server/data/tinybird/datasources/automation_run_step_events.datasource +12 -0
- package/core/server/data/tinybird/endpoints/api_automation_browse_stats.pipe +43 -0
- package/core/server/data/tinybird/fixtures/automation_run_events.ndjson +7 -0
- package/core/server/data/tinybird/fixtures/automation_run_step_events.ndjson +8 -0
- package/core/server/data/tinybird/pipes/mv_automation_run_steps.pipe +13 -0
- package/core/server/data/tinybird/pipes/mv_automation_runs.pipe +15 -0
- package/core/server/data/tinybird/pipes/mv_hits.pipe +86 -50
- package/core/server/data/tinybird/pipes/mv_pending_automation_runs.pipe +7 -0
- package/core/server/data/tinybird/tests/api_automation_browse_stats.yaml +18 -0
- package/core/server/ghost-server.js +3 -3
- package/core/server/lib/db-types/count.js +16 -0
- package/core/server/lib/generate-password.js +54 -0
- package/core/server/lib/public-jwk.js +77 -0
- 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 +6 -6
- package/core/server/models/user.js +12 -10
- package/core/server/services/adapter-manager/base-classes.js +21 -3
- package/core/server/services/automations/automations-api.js +46 -1
- package/core/server/services/automations/database-automations-repository.js +11 -4
- package/core/server/services/automations/tinybird-automation-stats.js +56 -0
- package/core/server/services/content-import/import/media.js +1 -1
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +15 -29
- package/core/server/services/email-analytics/email-analytics-service.js +19 -3
- package/core/server/services/email-analytics/index.js +9 -4
- package/core/server/services/email-rendering/finalize.js +7 -2
- package/core/server/services/email-service/CONTEXT.md +37 -0
- package/core/server/services/email-service/README.md +45 -0
- package/core/server/services/email-service/batch-sending-service.js +2 -0
- package/core/server/services/email-service/email-controller.js +18 -2
- package/core/server/services/email-service/email-renderer.js +7 -2
- package/core/server/services/email-service/email-service-wrapper.js +4 -1
- package/core/server/services/email-service/email-service.js +43 -34
- 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 +117 -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/gift-email-service.js +10 -3
- package/core/server/services/gifts/gift-service.js +19 -57
- package/core/server/services/gifts/index.js +1 -2
- package/core/server/services/i18n.js +1 -1
- package/core/server/services/identity-tokens/identity-token-service-wrapper.js +3 -4
- 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 +13 -2
- package/core/server/services/limits.js +17 -1
- package/core/server/services/member-welcome-emails/service.js +1 -1
- package/core/server/services/members/account-service.js +98 -0
- package/core/server/services/members/api.js +4 -4
- package/core/server/services/members/import-export/export/exporter.js +20 -20
- package/core/server/services/members/import-export/import/completion-email.js +6 -6
- package/core/server/services/members/import-export/import/importer.js +13 -13
- package/core/server/services/members/import-export/index.js +10 -9
- package/core/server/services/members/members-api/controllers/router-controller.js +7 -2
- package/core/server/services/members/members-api/controllers/well-known-controller.js +2 -1
- package/core/server/services/members/members-api/members-api.js +26 -6
- package/core/server/services/members/members-api/repositories/event-repository.js +4 -0
- 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 +57 -48
- package/core/server/services/members/members-api/services/token-service.js +11 -12
- package/core/server/services/members/members-ssr.js +18 -0
- package/core/server/services/members/middleware.js +104 -83
- package/core/server/services/members/request-integrity-token-provider.js +2 -1
- package/core/server/services/members/service.js +70 -42
- package/core/server/services/members/utils.js +9 -0
- package/core/server/services/members-metafields/access.js +39 -0
- package/core/server/services/{members-custom-fields → members-metafields}/actions.js +6 -5
- package/core/server/services/members-metafields/bindings-service.js +147 -0
- package/core/server/services/members-metafields/codec.js +21 -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 +71 -67
- package/core/server/services/{members-custom-fields → members-metafields}/filter.js +9 -9
- package/core/server/services/{members-custom-fields → members-metafields}/index.js +16 -7
- package/core/server/services/{members-custom-fields → members-metafields}/key.js +1 -1
- package/core/server/services/{members-custom-fields → members-metafields}/models.js +6 -4
- package/core/server/services/members-metafields/namespaces.js +34 -0
- package/core/server/services/members-metafields/queries.js +60 -0
- package/core/server/services/{members-custom-fields → members-metafields}/schema.js +18 -12
- package/core/server/services/{members-custom-fields → members-metafields}/serializers.js +7 -6
- package/core/server/services/{members-custom-fields → members-metafields}/values-service.js +46 -27
- package/core/server/services/mentions/mention-discovery-service.js +1 -1
- package/core/server/services/mentions/mention-sending-service.js +27 -10
- package/core/server/services/mentions/mention.js +1 -1
- package/core/server/services/mentions/send-webmentions-job.js +18 -0
- package/core/server/services/mentions/service.js +1 -31
- package/core/server/services/oembed/oembed-service.js +36 -11
- package/core/server/services/posts/post-scheduling.js +20 -0
- package/core/server/services/staff/staff-service-emails.js +5 -1
- package/core/server/services/stats/stats-service.js +12 -3
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +2 -2
- package/core/server/services/stripe/stripe-service.js +4 -4
- package/core/server/services/tier-checkout-config/codec.js +2 -2
- package/core/server/services/tier-checkout-config/index.js +2 -1
- package/core/server/services/tier-checkout-config/models.js +2 -2
- package/core/server/services/tier-checkout-config/queries.js +8 -8
- package/core/server/services/tier-checkout-config/serializers.js +57 -0
- package/core/server/services/tier-checkout-config/service.js +18 -2
- package/core/server/services/tiers/service.js +3 -3
- package/core/server/services/tinybird/tinybird-service.js +1 -0
- package/core/server/services/tinybird-sync/get-ingest-config.js +39 -0
- package/core/server/services/tinybird-sync/index.js +28 -0
- package/core/server/services/tinybird-sync/sync-table-to-tinybird.js +149 -0
- package/core/server/services/tinybird-sync/tinybird-sync-service.js +62 -0
- package/core/server/services/url/router-filter.js +8 -10
- package/core/server/services/users.js +1 -19
- package/core/server/web/api/endpoints/admin/routes.js +34 -34
- 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/server/web/well-known.js +12 -16
- package/core/shared/config/defaults.json +2 -3
- package/core/shared/{flush-logs.js → flush.js} +7 -3
- package/core/shared/labs.js +6 -2
- package/core/shared/timing-safe-string-equal.js +26 -0
- package/package.json +46 -46
- package/patches/@elastic__elasticsearch@8.19.2.patch +67 -0
- package/patches/juice@11.1.1.patch +46 -0
- package/pnpm-lock.yaml +750 -1742
- package/pnpm-workspace.yaml +37 -24
- package/scripts/prune.mts +70 -3
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/core/built/admin/assets/Combination-B0l4MKVh.js +0 -41
- package/core/built/admin/assets/RadialBarChart-JAKoS4KT.js +0 -1
- package/core/built/admin/assets/_baseAssignValue-C_vooEQc.js +0 -1
- package/core/built/admin/assets/about-Dyae_UT6.js +0 -1
- package/core/built/admin/assets/account-migration-CESa0mnX.js +0 -1
- package/core/built/admin/assets/action-list-C4v0-du8.js +0 -1
- package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +0 -1
- package/core/built/admin/assets/add-integration-modal-BlF6rs_f.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-CglVQqah.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-wz7BsuaG.js +0 -1
- package/core/built/admin/assets/analytics-C4HE1gBg.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-Ci4zz8jO.js +0 -1
- package/core/built/admin/assets/api-keys-Ck-Yteiv.js +0 -1
- package/core/built/admin/assets/automated-emails-D5VVMND9.js +0 -1
- package/core/built/admin/assets/automations-CzKt5fb8.js +0 -1
- package/core/built/admin/assets/automations-sAcJ1334.js +0 -1
- package/core/built/admin/assets/avatar-flipboard-C6Kj3Awu.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-BEtJ_gL_.js +0 -1
- package/core/built/admin/assets/box-CdfQAP_F.js +0 -1
- package/core/built/admin/assets/brand-icon-CrIGOA3Z.js +0 -1
- package/core/built/admin/assets/breadcrumb-zUrZrBtW.js +0 -1
- package/core/built/admin/assets/build-CsiH26uS.js +0 -1
- package/core/built/admin/assets/button-eNMKQWXr.js +0 -1
- package/core/built/admin/assets/card--VNbfgo3.js +0 -1
- package/core/built/admin/assets/chart-helpers-CfBKt0vT.js +0 -1
- package/core/built/admin/assets/checkbox-CgOn_tKW.js +0 -1
- package/core/built/admin/assets/chunk.524.a697dee88db67fdf6021.js +0 -32
- package/core/built/admin/assets/chunk.598.7e056fabb47ce7dbd73f.js +0 -366
- package/core/built/admin/assets/code-editor-view-BYNoAoNV.js +0 -1
- package/core/built/admin/assets/code-editor-view-K2wI-I5f.js +0 -1
- package/core/built/admin/assets/color-picker-Ddw80xX6.js +0 -3
- package/core/built/admin/assets/color-picker-field-D9nUhvA7.js +0 -1
- package/core/built/admin/assets/combobox-w9j8PPQT.js +0 -1
- package/core/built/admin/assets/command-BfpHORBv.js +0 -1
- package/core/built/admin/assets/comments-BLmCmyCs.js +0 -1
- package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +0 -1
- package/core/built/admin/assets/confirmation-context-CBM8G0Vj.js +0 -1
- package/core/built/admin/assets/container-BuQG1wuP.js +0 -1
- package/core/built/admin/assets/content-api-modal-TXRz3U7S.js +0 -1
- package/core/built/admin/assets/context-menu-_QMdd3kp.js +0 -1
- package/core/built/admin/assets/copy-field-6N01S07E.js +0 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-qIBymvvf.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-DmNquxUV.js +0 -1
- package/core/built/admin/assets/data-list-CA2jYGPq.js +0 -1
- package/core/built/admin/assets/date-range-select-uvz_DtPh.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +0 -2
- package/core/built/admin/assets/detail-page-Dnv-CdR1.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-BZ1iytEd.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-DMRrxsiJ.js +0 -1
- package/core/built/admin/assets/dist-4m6pHdc0.js +0 -23
- package/core/built/admin/assets/dist-8YJUlf3B.js +0 -1
- package/core/built/admin/assets/dist-CZgIBXQN.js +0 -1
- package/core/built/admin/assets/dist-CiHx7vA5.js +0 -6
- package/core/built/admin/assets/dist-DIC8yz5C.js +0 -1
- package/core/built/admin/assets/dist-Dcsslyo72.js +0 -1
- package/core/built/admin/assets/dist-Dgt2cr6a.js +0 -1
- package/core/built/admin/assets/dist-ECAUrdGc.js +0 -1
- package/core/built/admin/assets/dist-JLOy8o0Q.js +0 -1
- package/core/built/admin/assets/dist-Ndy-PuGN.js +0 -1
- package/core/built/admin/assets/dist-Z97pCNqS.js +0 -11
- package/core/built/admin/assets/domain-BNYrt0aD.js +0 -1
- package/core/built/admin/assets/dropzone-D7nFdAe5.js +0 -5
- package/core/built/admin/assets/edit-profile-C5Asm4LA.js +0 -1
- package/core/built/admin/assets/editor-REokrzci.js +0 -8
- package/core/built/admin/assets/empty-indicator-D9QdpuNX.js +0 -1
- package/core/built/admin/assets/en-B1jX3WMs.js +0 -1
- package/core/built/admin/assets/esm-CXo5SEPy.js +0 -2
- package/core/built/admin/assets/explore-UY10Egsp.js +0 -1
- package/core/built/admin/assets/feature-image-placeholder-D1iF_8bc.js +0 -1
- package/core/built/admin/assets/feed-81C8mJWR.js +0 -1
- package/core/built/admin/assets/field-Dihp5p18.js +0 -1
- package/core/built/admin/assets/filter-bar-Da1qxybu.js +0 -1
- package/core/built/admin/assets/filter-sources-BO9xUPj7.js +0 -1
- package/core/built/admin/assets/filters-CRUWecfY.js +0 -1
- package/core/built/admin/assets/filters-DirqNXjH.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-CT6MxYZb.js +0 -1
- package/core/built/admin/assets/framework-provider-DqI25epG.js +0 -4
- package/core/built/admin/assets/gift-link-modal-CQVAye17.js +0 -1
- package/core/built/admin/assets/global-data-context-gAMDH9u1.js +0 -1
- package/core/built/admin/assets/grid-Dj29PY05.js +0 -1
- package/core/built/admin/assets/growth-BWIu-6xj.js +0 -3
- package/core/built/admin/assets/growth-DU29oxeA.js +0 -1
- package/core/built/admin/assets/heading-Dh5SFVjL.js +0 -1
- package/core/built/admin/assets/history-modal-CFd1Hvx6.js +0 -1
- package/core/built/admin/assets/hooks-CuEZV2-q.js +0 -2
- package/core/built/admin/assets/html-field-CJBCLTvv.js +0 -1
- package/core/built/admin/assets/iframe-buffering-B2OKJzbz.js +0 -1
- package/core/built/admin/assets/images-scfkWZiK.js +0 -1
- package/core/built/admin/assets/index-6MkPdCtr.js +0 -4
- package/core/built/admin/assets/index-Bn7QhcZu.css +0 -2
- package/core/built/admin/assets/index.es-CNnuNH0d.js +0 -1
- package/core/built/admin/assets/inline-BWSs_VzR.js +0 -1
- package/core/built/admin/assets/input-group-CMhe2DvY.js +0 -1
- package/core/built/admin/assets/integration-header-Bm6IDb1A.js +0 -1
- package/core/built/admin/assets/invite-user-modal-DfheMXs0.js +0 -1
- package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +0 -2
- package/core/built/admin/assets/kpi-card-DBRMfGOW.js +0 -1
- package/core/built/admin/assets/kpi-card-DFafD1qq.js +0 -1
- package/core/built/admin/assets/kpi-helpers-Cv8i8hns.js +0 -1
- package/core/built/admin/assets/kpi-tabs-BJItvLYm.js +0 -1
- package/core/built/admin/assets/label-BnSCvffu.js +0 -1
- package/core/built/admin/assets/label-picker-KVoo91sh.js +0 -1
- package/core/built/admin/assets/labels-CH3Kn8RV.js +0 -1
- package/core/built/admin/assets/layout-Bjv2Ll-5.js +0 -1
- package/core/built/admin/assets/limit-service-BhiEvqcP.js +0 -20
- package/core/built/admin/assets/link-helpers-MVDdnFCY.js +0 -1
- package/core/built/admin/assets/list-page-BucXJ0hi.js +0 -1
- package/core/built/admin/assets/luxon-D1RVjjpq.js +0 -1
- package/core/built/admin/assets/member-custom-fields-DDQV6QeG.js +0 -1
- package/core/built/admin/assets/member-detail-CdJF6Agw.js +0 -1
- package/core/built/admin/assets/members-DjQbq9Z2.js +0 -4
- package/core/built/admin/assets/modal-page-xCivTMvI.js +0 -1
- package/core/built/admin/assets/moderation-AgCIEso4.js +0 -1
- package/core/built/admin/assets/multi-select-combobox-BcITkUVr.js +0 -1
- package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +0 -1
- package/core/built/admin/assets/newsletter-BMMAuvgq.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +0 -1
- package/core/built/admin/assets/newsletter-emails-DKzwafuh.js +0 -1
- package/core/built/admin/assets/newsletters-BRBb_uQC.js +0 -1
- package/core/built/admin/assets/newsletters-DhNigJUh.js +0 -1
- package/core/built/admin/assets/note-DyGHtS9a.js +0 -1
- package/core/built/admin/assets/notifications-ZgWniKna.js +0 -1
- package/core/built/admin/assets/nql-lang-CaWrLesS.js +0 -19
- package/core/built/admin/assets/offers-container-modal-B-E8TIWr.js +0 -1
- package/core/built/admin/assets/onboarding-CzIsre-d.js +0 -1
- package/core/built/admin/assets/onboarding-qg8Akovm.js +0 -1
- package/core/built/admin/assets/onboarding-route-CaavZulg.js +0 -1
- package/core/built/admin/assets/overview-BW8GLeYi.js +0 -1
- package/core/built/admin/assets/overview-BkXZNzaZ.js +0 -1
- package/core/built/admin/assets/page-header-De0VzKBi.js +0 -1
- package/core/built/admin/assets/pagemenu-B5KKCpbA.js +0 -1
- package/core/built/admin/assets/pages-jgaFm69d.js +0 -1
- package/core/built/admin/assets/pages-route-CIUAQxtY.js +0 -1
- package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +0 -10
- package/core/built/admin/assets/pintura-modal-BZ2J9w3n.js +0 -1
- package/core/built/admin/assets/portal-frame-B6Hr9wHc.js +0 -1
- package/core/built/admin/assets/portal-modal-D4ceei3t.js +0 -1
- package/core/built/admin/assets/post-analytics-context-D0Mw-Pmv.js +0 -1
- package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +0 -1
- package/core/built/admin/assets/post-analytics-ot7f6pih.js +0 -1
- package/core/built/admin/assets/post-analytics-provider-Bo1gmYaP.js +0 -1
- package/core/built/admin/assets/post-helpers-C5OQMBoi.js +0 -1
- package/core/built/admin/assets/post-share-modal-C-PaYZ_e.js +0 -1
- package/core/built/admin/assets/posts-DuGazgTb.js +0 -1
- package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +0 -2
- package/core/built/admin/assets/posts-route-D6xmS7Vf.js +0 -1
- package/core/built/admin/assets/preferences-C5YJLp74.js +0 -5
- package/core/built/admin/assets/preview-chrome-BljUBlOk.js +0 -1
- package/core/built/admin/assets/preview-modal-BfeFqi5y.js +0 -1
- package/core/built/admin/assets/profile-B8mx2yAu.js +0 -1
- package/core/built/admin/assets/prop-types-DeUPn3oD.js +0 -1
- package/core/built/admin/assets/radio-group-DczlLKOn.js +0 -1
- package/core/built/admin/assets/react-Bb3IsNbi.js +0 -1
- package/core/built/admin/assets/rolldown-runtime-DAXXjFlN.js +0 -1
- package/core/built/admin/assets/router-provider-B0gZyiGJ.js +0 -1
- package/core/built/admin/assets/schemas-DppqmpeJ.js +0 -64
- package/core/built/admin/assets/select-cb0t_TAJ.js +0 -1
- package/core/built/admin/assets/settings-DZqxR0Da.js +0 -1
- package/core/built/admin/assets/settings-DvfTNdHQ.js +0 -4
- package/core/built/admin/assets/settings-breadcrumbs-DLzbRRSt.js +0 -1
- package/core/built/admin/assets/share-modal-D2-Bz1_I.js +0 -1
- package/core/built/admin/assets/skeleton-Ce9GgUpf.js +0 -1
- package/core/built/admin/assets/slack-modal-DYDTUria.js +0 -1
- package/core/built/admin/assets/sort-button-VVeQjjbu.js +0 -1
- package/core/built/admin/assets/source-icon-Ck5_5Kgq.js +0 -1
- package/core/built/admin/assets/stack-B2eszIiq.js +0 -1
- package/core/built/admin/assets/stats-Rjmk5owg.js +0 -1
- package/core/built/admin/assets/stats-view-BGGmkmgv.js +0 -1
- package/core/built/admin/assets/step-1-CsaBjeN4.js +0 -1
- package/core/built/admin/assets/step-2-D2vnxOOM.js +0 -1
- package/core/built/admin/assets/step-3-wBoLdGEb.js +0 -2
- package/core/built/admin/assets/stripe-button-CDgLz0S0.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-CSjMJetP.js +0 -1
- package/core/built/admin/assets/switch-CI4gLWqb.js +0 -1
- package/core/built/admin/assets/table-Do6iQ83Q.js +0 -1
- package/core/built/admin/assets/tabs-D2yWLMp_.js +0 -1
- package/core/built/admin/assets/tag-detail-BhXlOsCa.js +0 -2
- package/core/built/admin/assets/tags-ldukRApe.js +0 -1
- package/core/built/admin/assets/testimonials-modal-CtnZBJKl.js +0 -1
- package/core/built/admin/assets/text-BQDSDsKs.js +0 -1
- package/core/built/admin/assets/textarea-DJ1Q5V39.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +0 -1
- package/core/built/admin/assets/tiers-Nd_z4BZY.js +0 -1
- package/core/built/admin/assets/toggle-group-ByUJsxwD.js +0 -1
- package/core/built/admin/assets/topic-filter-BgC3g5_v.js +0 -1
- package/core/built/admin/assets/transistor-modal-Drr0SNfo.js +0 -1
- package/core/built/admin/assets/trend-badge-6ye93YAW.js +0 -1
- package/core/built/admin/assets/unsplash-modal-p6AoQoWp.js +0 -1
- package/core/built/admin/assets/use-analytics-data-CWCMAESo.js +0 -1
- package/core/built/admin/assets/use-filter-params-BIni9taJ.js +0 -1
- package/core/built/admin/assets/use-filterable-api-CzXcpUh8.js +0 -1
- package/core/built/admin/assets/use-form-vRjI0Kwk.js +0 -1
- package/core/built/admin/assets/use-growth-stats-DCVG3s9w.js +0 -1
- package/core/built/admin/assets/use-host-limits-DZtR08oa.js +0 -1
- package/core/built/admin/assets/use-limiter-D2WRRx0H.js +0 -2
- package/core/built/admin/assets/use-navigate-with-base-path-BHPA228f.js +0 -1
- package/core/built/admin/assets/use-pintura-config-h-J0Lvta.js +0 -1
- package/core/built/admin/assets/use-pintura-editor-BfN1UwzH.js +0 -1
- package/core/built/admin/assets/use-post-referrers-Czg47m8s.js +0 -1
- package/core/built/admin/assets/use-query-params-BfzuqbcD.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-ns0caK8k.js +0 -1
- package/core/built/admin/assets/use-router-history-entry-uldn7zJO.js +0 -1
- package/core/built/admin/assets/use-setting-group-DpcuCUDg.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-CZjGrhsp.js +0 -1
- package/core/built/admin/assets/use-simple-pagination-DzJWobPR.js +0 -1
- package/core/built/admin/assets/use-staff-users-Csipb-O0.js +0 -1
- package/core/built/admin/assets/use-tinybird-token-DTOKRR7e.js +0 -1
- package/core/built/admin/assets/use-unsaved-changes-guard-CBzs-KPi.js +0 -1
- package/core/built/admin/assets/use-upgrade-route-DhRSo7at.js +0 -1
- package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +0 -1
- package/core/built/admin/assets/virtual-list-BW-padMe.js +0 -1
- package/core/built/admin/assets/web-C1v33ehA.js +0 -2
- package/core/built/admin/assets/web-C4BKzn2T.js +0 -1
- package/core/built/admin/assets/zapier-modal-BBFfI6Ml.js +0 -1
- package/core/server/services/email-suppression-list/in-memory-email-suppression-list.js +0 -39
- package/core/server/services/members-custom-fields/bindings-service.js +0 -120
- package/core/server/services/members-custom-fields/codec.js +0 -14
- package/core/server/services/members-custom-fields/queries.js +0 -32
- /package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js.LICENSE.txt → chunk.972.4b8c97f93d66db15e751.js.LICENSE.txt} +0 -0
- /package/core/server/services/{members-custom-fields → members-metafields}/storage.js +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const errors = require('@tryghost/errors');
|
|
2
|
+
const { ADMIN } = require('../../../members-metafields');
|
|
2
3
|
const logging = require('@tryghost/logging');
|
|
3
4
|
const tpl = require('@tryghost/tpl');
|
|
4
5
|
const moment = require('moment');
|
|
@@ -7,9 +8,9 @@ const messages = {
|
|
|
7
8
|
stripeNotConnected: 'Missing Stripe connection.',
|
|
8
9
|
memberAlreadyExists: 'Member already exists.',
|
|
9
10
|
memberNotFound: 'Member not found.',
|
|
10
|
-
|
|
11
|
+
metafieldsOnAdd:
|
|
11
12
|
'Custom field values cannot be set while creating a member. Create the member, then set values with an edit.',
|
|
12
|
-
|
|
13
|
+
metafieldsWithoutWriter:
|
|
13
14
|
'Custom field values cannot be set by a request with no authenticated user or integration.',
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -47,8 +48,8 @@ module.exports = class MemberBREADService {
|
|
|
47
48
|
* @param {import('../../../settings-helpers/settings-helpers')} deps.settingsHelpers
|
|
48
49
|
* @param {import('./next-payment-calculator')} deps.nextPaymentCalculator
|
|
49
50
|
* @param {IGiftsModule} deps.giftService
|
|
50
|
-
* @param {import('../../../members-
|
|
51
|
-
* @param {import('../../../members-
|
|
51
|
+
* @param {import('../../../members-metafields/values-service').MetafieldValuesService} deps.metafieldValues Required: boot builds it before the members service
|
|
52
|
+
* @param {import('../../../members-metafields/definitions-service').MetafieldDefinitionsService} deps.metafieldDefinitions Required: boot builds it before the members service
|
|
52
53
|
*/
|
|
53
54
|
constructor({
|
|
54
55
|
memberRepository,
|
|
@@ -61,8 +62,8 @@ module.exports = class MemberBREADService {
|
|
|
61
62
|
nextPaymentCalculator,
|
|
62
63
|
commentsService,
|
|
63
64
|
giftService,
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
metafieldValues,
|
|
66
|
+
metafieldDefinitions,
|
|
66
67
|
}) {
|
|
67
68
|
this.offersAPI = offersAPI;
|
|
68
69
|
/** @private */
|
|
@@ -84,9 +85,9 @@ module.exports = class MemberBREADService {
|
|
|
84
85
|
/** @private */
|
|
85
86
|
this.giftService = giftService;
|
|
86
87
|
/** @private */
|
|
87
|
-
this.
|
|
88
|
+
this.metafieldValues = metafieldValues;
|
|
88
89
|
/** @private */
|
|
89
|
-
this.
|
|
90
|
+
this.metafieldDefinitions = metafieldDefinitions;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
/**
|
|
@@ -94,20 +95,21 @@ module.exports = class MemberBREADService {
|
|
|
94
95
|
* size or a delivery address. Their values live in their own table, so they are fetched
|
|
95
96
|
* here rather than loaded alongside the member.
|
|
96
97
|
*
|
|
97
|
-
* Returns null when
|
|
98
|
-
* `metafields` key off the member payload rather than send an empty object: a key
|
|
99
|
-
* an API response cannot be withdrawn without breaking whoever started reading it,
|
|
100
|
-
* most sites have never defined a field, so those sites keep the payload they had
|
|
101
|
-
* this feature existed.
|
|
98
|
+
* Returns null when this audience has no field to be told about, which tells the caller to
|
|
99
|
+
* leave the `metafields` key off the member payload rather than send an empty object: a key
|
|
100
|
+
* added to an API response cannot be withdrawn without breaking whoever started reading it,
|
|
101
|
+
* and most sites have never defined a field, so those sites keep the payload they had
|
|
102
|
+
* before this feature existed.
|
|
102
103
|
* @param {string[]} memberIds
|
|
104
|
+
* @param {import('../../../members-metafields').Audience} audience
|
|
103
105
|
* @returns {Promise<Map<string, Record<string, unknown>> | null>}
|
|
104
106
|
*/
|
|
105
|
-
async
|
|
106
|
-
if (!(await this.
|
|
107
|
+
async fetchMetafieldValues(memberIds, audience) {
|
|
108
|
+
if (!(await this.metafieldDefinitions.hasAnyReadable(audience))) {
|
|
107
109
|
return null;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
return this.
|
|
112
|
+
return this.metafieldValues.getValuesForMembers(memberIds, audience);
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
/**
|
|
@@ -384,13 +386,17 @@ module.exports = class MemberBREADService {
|
|
|
384
386
|
/**
|
|
385
387
|
* @param {object} data
|
|
386
388
|
* @param {object} [options]
|
|
387
|
-
* @param {
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
389
|
+
* @param {import('../../../members-metafields').Audience | null} options.metafieldsFor
|
|
390
|
+
* Who the extra fields a publisher defined are being read for, or null to leave them
|
|
391
|
+
* off entirely. Null is not the same as "nobody may see them": it means this caller
|
|
392
|
+
* never shows them, so fetching them is two database queries whose results are thrown
|
|
393
|
+
* away. Ghost identifies a signed-in reader on every page view of a themed site
|
|
394
|
+
* through this method, and that caller renders a member through a fixed list of
|
|
395
|
+
* fields which has never included these.
|
|
396
|
+
*
|
|
397
|
+
* Defaults to null, so a caller that does not ask gets none of them.
|
|
392
398
|
*/
|
|
393
|
-
async read(data, {
|
|
399
|
+
async read(data, { metafieldsFor = null, ...options } = {}) {
|
|
394
400
|
const defaultWithRelated = [
|
|
395
401
|
'labels',
|
|
396
402
|
'stripeSubscriptions',
|
|
@@ -451,10 +457,10 @@ module.exports = class MemberBREADService {
|
|
|
451
457
|
const unsubscribeUrl = this.settingsHelpers.createUnsubscribeUrl(member.uuid);
|
|
452
458
|
member.unsubscribe_url = unsubscribeUrl;
|
|
453
459
|
|
|
454
|
-
if (
|
|
455
|
-
const
|
|
456
|
-
if (
|
|
457
|
-
member.metafields =
|
|
460
|
+
if (metafieldsFor) {
|
|
461
|
+
const metafields = await this.fetchMetafieldValues([member.id], metafieldsFor);
|
|
462
|
+
if (metafields) {
|
|
463
|
+
member.metafields = metafields.get(member.id) ?? {};
|
|
458
464
|
}
|
|
459
465
|
}
|
|
460
466
|
|
|
@@ -462,9 +468,9 @@ module.exports = class MemberBREADService {
|
|
|
462
468
|
}
|
|
463
469
|
|
|
464
470
|
async add(data, options) {
|
|
465
|
-
if (this.
|
|
471
|
+
if (this.metafieldValues.namesValues(this.metafieldValues.unwrapWire(data.metafields))) {
|
|
466
472
|
throw new errors.ValidationError({
|
|
467
|
-
message: tpl(messages.
|
|
473
|
+
message: tpl(messages.metafieldsOnAdd),
|
|
468
474
|
property: 'metafields',
|
|
469
475
|
});
|
|
470
476
|
}
|
|
@@ -559,22 +565,22 @@ module.exports = class MemberBREADService {
|
|
|
559
565
|
await this.memberRepository.setComplimentarySubscription(model, sharedOptions);
|
|
560
566
|
}
|
|
561
567
|
|
|
562
|
-
return this.read({ id: model.id }, options);
|
|
568
|
+
return this.read({ id: model.id }, { ...options, metafieldsFor: ADMIN });
|
|
563
569
|
}
|
|
564
570
|
|
|
565
571
|
async edit(data, options) {
|
|
566
572
|
delete data.last_seen_at;
|
|
567
573
|
|
|
568
|
-
const
|
|
569
|
-
const
|
|
574
|
+
const metafields = this.metafieldValues.unwrapWire(data.metafields);
|
|
575
|
+
const writeMetafields = metafields !== undefined;
|
|
570
576
|
delete data.metafields;
|
|
571
577
|
|
|
572
578
|
// Plan (which validates) before the member is touched, so a bad value 422s
|
|
573
579
|
// here rather than after the member edit has been applied — and keep the
|
|
574
580
|
// plan to apply once below, so the values aren't resolved and validated
|
|
575
581
|
// twice.
|
|
576
|
-
const
|
|
577
|
-
? await this.
|
|
582
|
+
const plannedMetafields = writeMetafields
|
|
583
|
+
? await this.metafieldValues.planWrite(metafields, ADMIN)
|
|
578
584
|
: null;
|
|
579
585
|
|
|
580
586
|
let model;
|
|
@@ -627,7 +633,7 @@ module.exports = class MemberBREADService {
|
|
|
627
633
|
}
|
|
628
634
|
}
|
|
629
635
|
|
|
630
|
-
if (
|
|
636
|
+
if (plannedMetafields) {
|
|
631
637
|
// Every value reaching here was typed into the Admin API, so the writer is
|
|
632
638
|
// whoever made the request — the same pair the action log records, so the two
|
|
633
639
|
// agree about who did it rather than one saying only that it was "admin".
|
|
@@ -638,15 +644,15 @@ module.exports = class MemberBREADService {
|
|
|
638
644
|
const context = options.context || {};
|
|
639
645
|
if (!context.integration && !context.user) {
|
|
640
646
|
throw new errors.IncorrectUsageError({
|
|
641
|
-
message: tpl(messages.
|
|
647
|
+
message: tpl(messages.metafieldsWithoutWriter),
|
|
642
648
|
});
|
|
643
649
|
}
|
|
644
650
|
const writtenBy = context.integration
|
|
645
651
|
? { type: 'integration', id: context.integration.id }
|
|
646
652
|
: { type: 'user', id: context.user };
|
|
647
|
-
await this.
|
|
653
|
+
await this.metafieldValues.applyWrite(model.id, plannedMetafields, { writtenBy });
|
|
648
654
|
|
|
649
|
-
//
|
|
655
|
+
// Metafields aren't a member column or relation, so an edit touching
|
|
650
656
|
// only them leaves `model._changed` empty and the save fires nothing.
|
|
651
657
|
// Declare the change into `_changed` — as bookshelf-relations does for a
|
|
652
658
|
// labels change — so the member's edited lifecycle fires its usual signals
|
|
@@ -656,13 +662,13 @@ module.exports = class MemberBREADService {
|
|
|
656
662
|
// populated `_changed` and fired the edited event during update(), so
|
|
657
663
|
// re-firing would duplicate it (this also covers a full PUT that resends
|
|
658
664
|
// unchanged member fields — `_changed` stays empty there too). That
|
|
659
|
-
// combined event omits `
|
|
660
|
-
// reads:
|
|
665
|
+
// combined event omits `metafields` from `_changed`, which nothing
|
|
666
|
+
// reads: metafields aren't in the webhook payload (they're injected
|
|
661
667
|
// into read/browse responses, not the model), and `_changed` only gates
|
|
662
668
|
// whether the event fires.
|
|
663
669
|
const memberUnchanged = !model._changed || Object.keys(model._changed).length === 0;
|
|
664
|
-
if (memberUnchanged &&
|
|
665
|
-
model._changed = {
|
|
670
|
+
if (memberUnchanged && plannedMetafields.length > 0) {
|
|
671
|
+
model._changed = { metafields: true };
|
|
666
672
|
// A mixed edit keeps the generic label on purpose: relabelling the
|
|
667
673
|
// one action a member change already fired would bury that change
|
|
668
674
|
// behind this one.
|
|
@@ -675,7 +681,7 @@ module.exports = class MemberBREADService {
|
|
|
675
681
|
}
|
|
676
682
|
}
|
|
677
683
|
|
|
678
|
-
return this.read({ id: model.id }, options);
|
|
684
|
+
return this.read({ id: model.id }, { ...options, metafieldsFor: ADMIN });
|
|
679
685
|
}
|
|
680
686
|
|
|
681
687
|
/**
|
|
@@ -707,7 +713,7 @@ module.exports = class MemberBREADService {
|
|
|
707
713
|
);
|
|
708
714
|
}
|
|
709
715
|
|
|
710
|
-
return this.read({ id: memberId });
|
|
716
|
+
return this.read({ id: memberId }, { metafieldsFor: ADMIN });
|
|
711
717
|
}
|
|
712
718
|
|
|
713
719
|
/**
|
|
@@ -729,7 +735,7 @@ module.exports = class MemberBREADService {
|
|
|
729
735
|
|
|
730
736
|
await this.memberRepository.saveCommenting(memberId, updated, 'commenting_enabled', context);
|
|
731
737
|
|
|
732
|
-
return this.read({ id: memberId });
|
|
738
|
+
return this.read({ id: memberId }, { metafieldsFor: ADMIN });
|
|
733
739
|
}
|
|
734
740
|
|
|
735
741
|
async logout(options) {
|
|
@@ -791,8 +797,11 @@ module.exports = class MemberBREADService {
|
|
|
791
797
|
|
|
792
798
|
// One query for the whole page, not one per member. `null` when the flag
|
|
793
799
|
// is off or the caller didn't ask — the same truthiness guard read uses.
|
|
794
|
-
const
|
|
795
|
-
? await this.
|
|
800
|
+
const metafieldsByMember = options.includeMetafields
|
|
801
|
+
? await this.fetchMetafieldValues(
|
|
802
|
+
page.data.map((model) => model.id),
|
|
803
|
+
ADMIN,
|
|
804
|
+
)
|
|
796
805
|
: null;
|
|
797
806
|
|
|
798
807
|
const data = page.data.map((model, index) => {
|
|
@@ -804,8 +813,8 @@ module.exports = class MemberBREADService {
|
|
|
804
813
|
if (!originalWithRelated.includes('products')) {
|
|
805
814
|
delete member.products;
|
|
806
815
|
}
|
|
807
|
-
if (
|
|
808
|
-
member.metafields =
|
|
816
|
+
if (metafieldsByMember) {
|
|
817
|
+
member.metafields = metafieldsByMember.get(model.id) ?? {};
|
|
809
818
|
}
|
|
810
819
|
member.email_suppression = {
|
|
811
820
|
suppressed: bulkSuppressionData[index].suppressed || !!model.get('email_disabled'),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const jose = require('node-jose');
|
|
2
1
|
const jwt = require('jsonwebtoken');
|
|
3
2
|
const crypto = require('crypto');
|
|
3
|
+
const { getPublicKeyInfo } = require('../../../../lib/public-jwk');
|
|
4
4
|
|
|
5
5
|
// A token's `scope` declares its purpose. Only identity tokens may act as a
|
|
6
6
|
// member; entitlement tokens are read-only and handed to integrations.
|
|
@@ -9,24 +9,23 @@ const ENTITLEMENT_TOKEN_SCOPE = 'members:entitlements:read';
|
|
|
9
9
|
|
|
10
10
|
module.exports = class TokenService {
|
|
11
11
|
constructor({ privateKey, publicKey, issuer }) {
|
|
12
|
-
this.
|
|
13
|
-
this._keyStoreReady = this._keyStore.add(privateKey, 'pem');
|
|
12
|
+
this._keyReady = getPublicKeyInfo(privateKey);
|
|
14
13
|
this._privateKey = privateKey;
|
|
15
14
|
this._publicKey = publicKey;
|
|
16
15
|
this._issuer = issuer;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
async encodeIdentityToken({ sub }) {
|
|
20
|
-
const
|
|
19
|
+
const { kid } = await this._keyReady;
|
|
21
20
|
return jwt.sign(
|
|
22
21
|
{
|
|
23
22
|
sub,
|
|
24
|
-
kid
|
|
23
|
+
kid,
|
|
25
24
|
scope: IDENTITY_TOKEN_SCOPE,
|
|
26
25
|
},
|
|
27
26
|
this._privateKey,
|
|
28
27
|
{
|
|
29
|
-
keyid:
|
|
28
|
+
keyid: kid,
|
|
30
29
|
algorithm: 'RS512',
|
|
31
30
|
audience: this._issuer,
|
|
32
31
|
expiresIn: '10m',
|
|
@@ -36,12 +35,12 @@ module.exports = class TokenService {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
async encodeEntitlementToken({ sub, memberUuid, paid, activeTierIds = [] }) {
|
|
39
|
-
const
|
|
38
|
+
const { kid } = await this._keyReady;
|
|
40
39
|
|
|
41
40
|
return jwt.sign(
|
|
42
41
|
{
|
|
43
42
|
sub,
|
|
44
|
-
kid
|
|
43
|
+
kid,
|
|
45
44
|
scope: ENTITLEMENT_TOKEN_SCOPE,
|
|
46
45
|
member_uuid: memberUuid,
|
|
47
46
|
paid,
|
|
@@ -50,7 +49,7 @@ module.exports = class TokenService {
|
|
|
50
49
|
},
|
|
51
50
|
this._privateKey,
|
|
52
51
|
{
|
|
53
|
-
keyid:
|
|
52
|
+
keyid: kid,
|
|
54
53
|
algorithm: 'RS512',
|
|
55
54
|
audience: this._issuer,
|
|
56
55
|
expiresIn: '5m',
|
|
@@ -73,7 +72,7 @@ module.exports = class TokenService {
|
|
|
73
72
|
* @returns {Promise<jwt.JwtPayload>}
|
|
74
73
|
*/
|
|
75
74
|
async decodeToken(token) {
|
|
76
|
-
await this.
|
|
75
|
+
await this._keyReady;
|
|
77
76
|
|
|
78
77
|
const result = jwt.verify(token, this._publicKey, {
|
|
79
78
|
algorithms: ['RS512'],
|
|
@@ -92,7 +91,7 @@ module.exports = class TokenService {
|
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
async getPublicKeys() {
|
|
95
|
-
await this.
|
|
96
|
-
return
|
|
94
|
+
const { kid, jwk } = await this._keyReady;
|
|
95
|
+
return { keys: [{ kty: jwk.kty, kid, n: jwk.n, e: jwk.e }] };
|
|
97
96
|
}
|
|
98
97
|
};
|
|
@@ -307,6 +307,24 @@ class MembersSSR {
|
|
|
307
307
|
*
|
|
308
308
|
* @returns {Promise<Member>}
|
|
309
309
|
*/
|
|
310
|
+
/**
|
|
311
|
+
* Who this session belongs to, without loading them.
|
|
312
|
+
*
|
|
313
|
+
* The counterpart to `getMemberDataFromSession`, for callers that need to know a
|
|
314
|
+
* member is there rather than what is on their record.
|
|
315
|
+
*
|
|
316
|
+
* @method getMemberIdentityFromSession
|
|
317
|
+
* @param {Request} req
|
|
318
|
+
* @param {Response} res
|
|
319
|
+
*
|
|
320
|
+
* @returns {Promise<{id: string, email: string} | null>}
|
|
321
|
+
*/
|
|
322
|
+
async getMemberIdentityFromSession(req, res) {
|
|
323
|
+
const transientId = this._getSessionCookies(req, res);
|
|
324
|
+
const api = await this._getMembersApi();
|
|
325
|
+
return api.getMemberIdentity(transientId);
|
|
326
|
+
}
|
|
327
|
+
|
|
310
328
|
async getMemberDataFromSession(req, res) {
|
|
311
329
|
const transientId = this._getSessionCookies(req, res);
|
|
312
330
|
const member = await this._getMemberIdentityDataFromTransientId(transientId);
|
|
@@ -2,22 +2,25 @@ const crypto = require('crypto');
|
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const logging = require('@tryghost/logging');
|
|
4
4
|
const membersService = require('./service');
|
|
5
|
-
const emailSuppressionList = require('../email-suppression-list');
|
|
6
5
|
const models = require('../../models');
|
|
7
6
|
const urlUtils = require('../../../shared/url-utils').default;
|
|
8
7
|
const spamPrevention = require('../../web/shared/middleware/api/spam-prevention');
|
|
9
|
-
const {
|
|
8
|
+
const { formatNewsletterResponse } = require('./utils');
|
|
10
9
|
const errors = require('@tryghost/errors');
|
|
11
10
|
const tpl = require('@tryghost/tpl');
|
|
12
11
|
const onHeaders = require('on-headers');
|
|
13
12
|
const tiersService = require('../tiers/service');
|
|
14
13
|
const config = require('../../../shared/config');
|
|
15
14
|
const settingsHelpers = require('../settings-helpers');
|
|
15
|
+
const { timingSafeStringEqual } = require('../../../shared/timing-safe-string-equal');
|
|
16
16
|
|
|
17
17
|
const messages = {
|
|
18
18
|
missingUuid: 'Missing uuid.',
|
|
19
19
|
invalidUuid: 'Invalid uuid.',
|
|
20
20
|
invalidKey: 'Invalid key.',
|
|
21
|
+
// Says that nobody is signed in, without naming the cookie that would have said
|
|
22
|
+
// so. Which cookie carries a session is Ghost's business, not the caller's.
|
|
23
|
+
noMemberSession: 'You must be signed in to do this.',
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
const getFreeTier = async function getFreeTier() {
|
|
@@ -116,6 +119,94 @@ const getRedirectUrl = function getRedirectUrl({ action, referrer, searchParams,
|
|
|
116
119
|
return redirectUrl.href;
|
|
117
120
|
};
|
|
118
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Establish who is signed in, without loading them.
|
|
124
|
+
*
|
|
125
|
+
* Sets `req.identity` to an id and an email address, or to null when nobody is
|
|
126
|
+
* signed in. One lookup against an indexed column, with no relations attached and
|
|
127
|
+
* nothing fetched from outside Ghost.
|
|
128
|
+
*
|
|
129
|
+
* Deliberately not the member's record. Most endpoints that serve a member need to
|
|
130
|
+
* act on one rather than describe one, and the one that describes a member reads
|
|
131
|
+
* it again after writing anyway, because the response has to say what Ghost now
|
|
132
|
+
* holds rather than what was sent. What to load is the endpoint's own business.
|
|
133
|
+
*
|
|
134
|
+
* Distinct from `loadMemberSession`, which loads the whole member and is what a
|
|
135
|
+
* themed page renders from.
|
|
136
|
+
*/
|
|
137
|
+
const loadMemberIdentity = async function loadMemberIdentity(req, res, next) {
|
|
138
|
+
try {
|
|
139
|
+
Object.assign(req, {
|
|
140
|
+
identity: await membersService.ssr.getMemberIdentityFromSession(req, res),
|
|
141
|
+
});
|
|
142
|
+
} catch (err) {
|
|
143
|
+
// Only a missing or unreadable cookie means nobody is signed in. Establishing
|
|
144
|
+
// an identity also reaches the database, and answering "you are not signed in"
|
|
145
|
+
// to that failing would tell a signed-in member something false and leave the
|
|
146
|
+
// real fault unreported.
|
|
147
|
+
if (!errors.utils.isGhostError(err) || err.errorType !== 'BadRequestError') {
|
|
148
|
+
return next(err);
|
|
149
|
+
}
|
|
150
|
+
Object.assign(req, { identity: null });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return next();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Hand the identity to whatever comes next, in the place the API framework reads.
|
|
158
|
+
*
|
|
159
|
+
* The framework takes whoever is acting from `req.member` and nowhere else, so a
|
|
160
|
+
* controller behind either gate below finds the identity there.
|
|
161
|
+
*/
|
|
162
|
+
const carryIdentity = function carryIdentity(req, res) {
|
|
163
|
+
Object.assign(req, { member: req.identity });
|
|
164
|
+
res.locals.member = req.member;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Refuse a request that `loadMemberIdentity` could not put a name to.
|
|
169
|
+
*
|
|
170
|
+
* For endpoints where an unknown caller is asking for something they have no right
|
|
171
|
+
* to: changing a member's record, or reading what a publisher collects.
|
|
172
|
+
*
|
|
173
|
+
* Reads only `req.identity`, so a route using this must register
|
|
174
|
+
* `loadMemberIdentity` first. It looks nothing up itself: what a route needs is
|
|
175
|
+
* declared by the middleware it lists, rather than depending on what some earlier
|
|
176
|
+
* one happened to leave behind.
|
|
177
|
+
*/
|
|
178
|
+
const rejectWhenAnonymous = function rejectWhenAnonymous(req, res, next) {
|
|
179
|
+
if (!req.identity) {
|
|
180
|
+
return next(
|
|
181
|
+
new errors.UnauthorizedError({
|
|
182
|
+
message: tpl(messages.noMemberSession),
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
carryIdentity(req, res);
|
|
188
|
+
return next();
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Answer with nothing when `loadMemberIdentity` could not put a name to the request.
|
|
193
|
+
*
|
|
194
|
+
* For reading who you are, where not knowing is an ordinary answer rather than a
|
|
195
|
+
* failure: a themed page asks on every view and most of those views have no member.
|
|
196
|
+
*
|
|
197
|
+
* The counterpart to `rejectWhenAnonymous`, and separate from it because which of
|
|
198
|
+
* the two applies is a fact about the endpoint, not a setting on a shared one.
|
|
199
|
+
*/
|
|
200
|
+
const emptyWhenAnonymous = function emptyWhenAnonymous(req, res, next) {
|
|
201
|
+
if (!req.identity) {
|
|
202
|
+
res.writeHead(204);
|
|
203
|
+
return res.end();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
carryIdentity(req, res);
|
|
207
|
+
return next();
|
|
208
|
+
};
|
|
209
|
+
|
|
119
210
|
/**
|
|
120
211
|
* Require member authentication, and make it possible to authenticate via uuid + hashed key.
|
|
121
212
|
* You can chain this after loadMemberSession to make it possible to authenticate via both the uuid and the session.
|
|
@@ -147,13 +238,19 @@ const authMemberByUuid = async function authMemberByUuid(req, res, next) {
|
|
|
147
238
|
.createHmac('sha256', settingsHelpers.getMembersValidationKey())
|
|
148
239
|
.update(uuid)
|
|
149
240
|
.digest('hex');
|
|
150
|
-
if (memberHmac
|
|
241
|
+
if (!timingSafeStringEqual(memberHmac, key)) {
|
|
151
242
|
throw new errors.UnauthorizedError({
|
|
152
243
|
message: tpl(messages.invalidKey),
|
|
153
244
|
});
|
|
154
245
|
}
|
|
155
246
|
|
|
156
|
-
|
|
247
|
+
// Nothing reached through this middleware renders a member's extra fields, so
|
|
248
|
+
// they are not fetched. It authenticates by a signed link rather than a session,
|
|
249
|
+
// which makes anything loaded here readable without being signed in.
|
|
250
|
+
const member = await membersService.api.memberBREADService.read(
|
|
251
|
+
{ uuid },
|
|
252
|
+
{ metafieldsFor: null },
|
|
253
|
+
);
|
|
157
254
|
if (!member) {
|
|
158
255
|
throw new errors.UnauthorizedError({
|
|
159
256
|
message: tpl(messages.invalidUuid),
|
|
@@ -243,42 +340,6 @@ const deleteSession = async function deleteSession(req, res) {
|
|
|
243
340
|
}
|
|
244
341
|
};
|
|
245
342
|
|
|
246
|
-
const getMemberData = async function getMemberData(req, res) {
|
|
247
|
-
try {
|
|
248
|
-
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
249
|
-
if (member) {
|
|
250
|
-
res.json(formattedMemberResponse(member));
|
|
251
|
-
} else {
|
|
252
|
-
res.json(null);
|
|
253
|
-
}
|
|
254
|
-
} catch (err) {
|
|
255
|
-
res.writeHead(204);
|
|
256
|
-
res.end();
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const deleteSuppression = async function deleteSuppression(req, res) {
|
|
261
|
-
try {
|
|
262
|
-
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
263
|
-
const options = {
|
|
264
|
-
id: member.id,
|
|
265
|
-
};
|
|
266
|
-
await emailSuppressionList.removeEmail(member.email);
|
|
267
|
-
await membersService.api.members.update({ email_disabled: false }, options);
|
|
268
|
-
|
|
269
|
-
res.writeHead(204);
|
|
270
|
-
res.end();
|
|
271
|
-
} catch (err) {
|
|
272
|
-
if (!err.statusCode) {
|
|
273
|
-
logging.error(err);
|
|
274
|
-
}
|
|
275
|
-
res.writeHead(err.statusCode ?? 500, {
|
|
276
|
-
'Content-Type': 'text/plain;charset=UTF-8',
|
|
277
|
-
});
|
|
278
|
-
res.end(err.message);
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
282
343
|
const getMemberNewsletters = async function getMemberNewsletters(req, res) {
|
|
283
344
|
try {
|
|
284
345
|
const memberData = req.member; // validation assumed
|
|
@@ -351,46 +412,6 @@ const updateMemberNewsletters = async function updateMemberNewsletters(req, res)
|
|
|
351
412
|
}
|
|
352
413
|
};
|
|
353
414
|
|
|
354
|
-
const updateMemberData = async function updateMemberData(req, res) {
|
|
355
|
-
try {
|
|
356
|
-
const data = _.pick(
|
|
357
|
-
req.body,
|
|
358
|
-
'name',
|
|
359
|
-
'expertise',
|
|
360
|
-
'subscribed',
|
|
361
|
-
'newsletters',
|
|
362
|
-
'enable_comment_notifications',
|
|
363
|
-
'enable_updates_and_announcements',
|
|
364
|
-
);
|
|
365
|
-
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
366
|
-
if (member) {
|
|
367
|
-
const options = {
|
|
368
|
-
id: member.id,
|
|
369
|
-
withRelated: [
|
|
370
|
-
'stripeSubscriptions',
|
|
371
|
-
'stripeSubscriptions.customer',
|
|
372
|
-
'stripeSubscriptions.stripePrice',
|
|
373
|
-
'newsletters',
|
|
374
|
-
],
|
|
375
|
-
};
|
|
376
|
-
await membersService.api.members.update(data, options);
|
|
377
|
-
const updatedMember = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
378
|
-
|
|
379
|
-
res.json(formattedMemberResponse(updatedMember));
|
|
380
|
-
} else {
|
|
381
|
-
res.json(null);
|
|
382
|
-
}
|
|
383
|
-
} catch (err) {
|
|
384
|
-
if (!err.statusCode) {
|
|
385
|
-
logging.error(err);
|
|
386
|
-
}
|
|
387
|
-
res.writeHead(err.statusCode ?? 500, {
|
|
388
|
-
'Content-Type': 'text/plain;charset=UTF-8',
|
|
389
|
-
});
|
|
390
|
-
res.end(err.message);
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
415
|
const createSessionFromMagicLink = async function createSessionFromMagicLink(req, res, next) {
|
|
395
416
|
if (!req.url.includes('token=')) {
|
|
396
417
|
return next();
|
|
@@ -500,17 +521,17 @@ const createSessionFromMagicLink = async function createSessionFromMagicLink(req
|
|
|
500
521
|
// Set req.member & res.locals.member if a cookie is set
|
|
501
522
|
module.exports = {
|
|
502
523
|
loadMemberSession,
|
|
524
|
+
loadMemberIdentity,
|
|
525
|
+
rejectWhenAnonymous,
|
|
526
|
+
emptyWhenAnonymous,
|
|
503
527
|
authMemberByUuid,
|
|
504
528
|
createSessionFromMagicLink,
|
|
505
529
|
getIdentityToken,
|
|
506
530
|
getEntitlementToken,
|
|
507
531
|
getMemberNewsletters,
|
|
508
|
-
getMemberData,
|
|
509
|
-
updateMemberData,
|
|
510
532
|
updateMemberNewsletters,
|
|
511
533
|
deleteSession,
|
|
512
534
|
accessInfoSession,
|
|
513
|
-
deleteSuppression,
|
|
514
535
|
createIntegrityToken,
|
|
515
536
|
verifyIntegrityToken,
|
|
516
537
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const crypto = require('crypto');
|
|
2
|
+
const { timingSafeStringEqual } = require('../../../shared/timing-safe-string-equal');
|
|
2
3
|
|
|
3
4
|
class RequestIntegrityTokenProvider {
|
|
4
5
|
#themeSecret;
|
|
@@ -45,7 +46,7 @@ class RequestIntegrityTokenProvider {
|
|
|
45
46
|
hmac.update(`${timestamp.toString()}:${nonce}`);
|
|
46
47
|
const expectedHmac = hmac.digest('hex');
|
|
47
48
|
|
|
48
|
-
if (expectedHmac
|
|
49
|
+
if (!timingSafeStringEqual(expectedHmac, hmacDigest)) {
|
|
49
50
|
// HMAC mismatch
|
|
50
51
|
return false;
|
|
51
52
|
}
|