ghost 6.57.1 → 6.58.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/LICENSE +22 -0
- package/bin/validate-adapters.js +1 -5
- 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.2.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.2.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
- package/core/boot.js +35 -3
- package/core/built/admin/assets/{about-BNojRaxf.js → about-B_BvSyLF.js} +1 -1
- package/core/built/admin/assets/{account-migration-DMS8JsM-.js → account-migration-oJUGxnzT.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DHFbePl6.js → account-migration-BpDeIF3G.js} +8 -8
- package/core/built/admin/assets/activitypub/activitypub.js +4 -4
- package/core/built/admin/assets/activitypub/{avatar-CT4tS2_S.js → avatar-B0SpBPVL.js} +3 -3
- package/core/built/admin/assets/activitypub/{avatar-flipboard-ByYWKipo.js → avatar-flipboard-DKt1LGcE.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-srVnSHDz.js → bluesky-sharing-aPMtrS3L.js} +10 -10
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-D4HEcEwi.js → chunk-KS7C4IRE-CnTrsgmM.js} +26 -15
- package/core/built/admin/assets/activitypub/{content-formatters-C2sos5Ht.js → content-formatters-BORfuA9G.js} +318 -284
- package/core/built/admin/assets/activitypub/{copy-B1UBTOoZ.js → copy-D-YvNi2E.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-MPsb2WZ1.js → dist-Dqggkccr.js} +5 -5
- package/core/built/admin/assets/activitypub/{domain-DzcM9Dzo.js → domain-CPdiB27_.js} +10 -10
- package/core/built/admin/assets/activitypub/{edit-profile-CsQ_4xAk.js → edit-profile-BY2PawIe.js} +7 -7
- package/core/built/admin/assets/activitypub/{explore-CJe0DZBK.js → explore-Sp9bgAwq.js} +11 -11
- package/core/built/admin/assets/activitypub/{feed-CKV3qWON.js → feed-BjtF4ohL.js} +10 -10
- package/core/built/admin/assets/activitypub/{field-DckHfDJf.js → field-BDOYVTg6.js} +4 -4
- package/core/built/admin/assets/activitypub/{hash-Dlp8RM55.js → hash-DqXYHgzG.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-CsPy4R0M.js → inbox-CZ7IOAMn.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DAsjzUUe.js → inbox-z1o2j1Tb.js} +11 -11
- package/core/built/admin/assets/activitypub/{label-DWyo9E5T.js → label-1jhN-qhD.js} +4 -4
- package/core/built/admin/assets/activitypub/layout-BRMezuty.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-B0Inc03M.js → moderation-CZudJi8N.js} +9 -9
- package/core/built/admin/assets/activitypub/{note-CSE54lER.js → note-nr_m_5yb.js} +8 -8
- package/core/built/admin/assets/activitypub/{notifications-RSwbKqdh.js → notifications-BdcyivbT.js} +10 -10
- package/core/built/admin/assets/activitypub/{onboarding-BFDkzeR2.js → onboarding-B4kwav9u.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-D6rcBdPe.js → onboarding-DZNElJYP.js} +3 -3
- package/core/built/admin/assets/activitypub/{preferences-CE231n2_.js → preferences-CNqy1x9C.js} +13 -13
- package/core/built/admin/assets/activitypub/{profile-aaQ1j1Eo.js → profile-DlY4rhbO.js} +15 -15
- package/core/built/admin/assets/activitypub/{reply-Cjb0Vs3m.js → reply-DASIN0Ec.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-Djb0mc9k.js → routes-CEHdEZzS.js} +31 -31
- package/core/built/admin/assets/activitypub/{separator-D8pROI5n.js → separator-B3k35sUV.js} +4 -4
- package/core/built/admin/assets/activitypub/{settings-DMc32IQl.js → settings-CrSV1cux.js} +6 -6
- package/core/built/admin/assets/activitypub/{site-DVIcsohX.js → site-CtBUCPBz.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-1-9Yey9usF.js → step-1-D9jGWyAy.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-2-DdYmKRXV.js → step-2-Jz_enuo9.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-hdSkcjZU.js → step-3-KpUqpGWi.js} +12 -12
- package/core/built/admin/assets/activitypub/{tabs-CUqseYzQ.js → tabs-DvlsxTCI.js} +7 -7
- package/core/built/admin/assets/activitypub/{topic-filter-Coni6OXM.js → topic-filter-CIN1E2jg.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-MeHTPMQv.js → use-activity-pub-queries-CATFd6YV.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-B046AM38.js → use-navigate-with-base-path-iCkcbMGk.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-1AO-Xob6.js → use-reply-chain-data-DJ0vhA8V.js} +6 -6
- package/core/built/admin/assets/activitypub/{users-BLeiOTHc.js → users-sgJ6GNZX.js} +12 -11
- package/core/built/admin/assets/activitypub/{x-BRfRYd16.js → x-Cy2jUiYM.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BW23PDYs.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-M0ls8BKO.js +1 -0
- package/core/built/admin/assets/{add-recommendation-modal-D-3LOrdd.js → add-recommendation-modal-B0NeBMzl.js} +1 -1
- package/core/built/admin/assets/{analytics-DTIMRhWw.js → analytics-BC2oR8ay.js} +1 -1
- package/core/built/admin/assets/announcement-bar-modal-pXBqjQE_.js +1 -0
- package/core/built/admin/assets/{api-keys-_iCT4dxr.js → api-keys-BgKr003d.js} +1 -1
- package/core/built/admin/assets/{api-keys-Dx4NeNpK.js → api-keys-Df6uv1dT.js} +1 -1
- package/core/built/admin/assets/{audience-DkknNqhZ.js → audience-BY8od8If.js} +1 -1
- package/core/built/admin/assets/{automated-emails-PayPCT8L.js → automated-emails-28c2Lbb3.js} +1 -1
- package/core/built/admin/assets/{automations-DvSU7QhW.js → automations-DCs6Wady.js} +1 -1
- package/core/built/admin/assets/{automations-162q5HjL.js → automations-Dzp_QnON.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-yZ9A_jL8.js → bluesky-sharing-D0P42Cws.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-CjOH7LCU.js → breadcrumb-MTj_cKLb.js} +1 -1
- package/core/built/admin/assets/card-CiiE7QfM.js +1 -0
- package/core/built/admin/assets/{chart-helpers-DGxvWnBT.js → chart-helpers-DcO3k_vA.js} +1 -1
- package/core/built/admin/assets/{checkbox-BSUcnNq_.js → checkbox-LmClhqcS.js} +1 -1
- package/core/built/admin/assets/{chunk.424.4a27628efdade15c2dab.js → chunk.408.1a258af360eb8d905508.js} +40 -37
- package/core/built/admin/assets/{chunk.571.40f093ed6d883993714e.js → chunk.492.a9da46ca956b10116774.js} +1342 -1342
- package/core/built/admin/assets/{chunk.571.40f093ed6d883993714e.js.LICENSE.txt → chunk.492.a9da46ca956b10116774.js.LICENSE.txt} +1 -1
- package/core/built/admin/assets/chunk.524.4c693c3a00ac91df34fa.js +25 -0
- package/core/built/admin/assets/chunk.582.20b600e329e58d12d7c2.js +27 -0
- package/core/built/admin/assets/code-editor-view-DqTOUnbM.js +1 -0
- package/core/built/admin/assets/code-editor-view-bA0lfxUL.js +1 -0
- package/core/built/admin/assets/{debounce-Cx5B_9OI.js → color-picker-CBlJMv3y.js} +2 -2
- package/core/built/admin/assets/color-picker-field-Cv9GOJQZ.js +1 -0
- package/core/built/admin/assets/{combobox-5rdgDrDV.js → combobox-BbPJzjCp.js} +1 -1
- package/core/built/admin/assets/command-BrhXIlDr.js +1 -0
- package/core/built/admin/assets/comments-Cu4rD1aJ.js +1 -0
- package/core/built/admin/assets/confirmation-provider-BEdrnHns.js +1 -0
- package/core/built/admin/assets/{content-api-modal-BtW4Z1-1.js → content-api-modal-BOFWRUkC.js} +1 -1
- package/core/built/admin/assets/{copy-field-CEp_XieW.js → copy-field-8biQvNMD.js} +1 -1
- package/core/built/admin/assets/custom-field-type-option-CNhAgHbJ.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-CrGOI8IB.js +1 -0
- package/core/built/admin/assets/{date-range-select-B3Ij1Rqg.js → date-range-select-Bsh4gIek.js} +1 -1
- package/core/built/admin/assets/debounce-pscB46sW.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-DWZguwWX.js +23 -0
- package/core/built/admin/assets/{dirty-confirm-dialog-YE9L21gY.js → dirty-confirm-dialog-DmdMpgyN.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-uIzbWL7K.js → disabled-sources-indicator-BLnmyWgt.js} +1 -1
- package/core/built/admin/assets/dist-4m6pHdc0.js +23 -0
- package/core/built/admin/assets/{dist-DCk0UkGx.js → dist-B6bJuxL3.js} +1 -1
- package/core/built/admin/assets/{dist-CbHbGtQ-.js → dist-BtaTA7Ii.js} +1 -1
- package/core/built/admin/assets/{dist-Q1p9iXwH.js → dist-CZgIBXQN.js} +1 -1
- package/core/built/admin/assets/dist-CiHx7vA5.js +6 -0
- package/core/built/admin/assets/{dist-ARN6XTcR.js → dist-ECAUrdGc.js} +1 -1
- package/core/built/admin/assets/dist-Z97pCNqS.js +11 -0
- package/core/built/admin/assets/{dist-t6AXpf_4.js → dist-zwwGt8Yf.js} +1 -1
- package/core/built/admin/assets/{domain-DzrOAPY8.js → domain-Dp9TRrFp.js} +1 -1
- package/core/built/admin/assets/{dropzone-CzhP33wa.js → dropzone-B21Entqz.js} +1 -1
- package/core/built/admin/assets/{edit-profile-oufs4R4n.js → edit-profile-DG6lpMsr.js} +1 -1
- package/core/built/admin/assets/editor-CPQOrhrn.js +8 -0
- package/core/built/admin/assets/{editor-DtxX27Jz.css → editor-DLioOiRN.css} +1 -1
- package/core/built/admin/assets/{embed-signup-form-modal-Ch3s6lqA.js → embed-signup-form-modal-CgWniDha.js} +2 -2
- package/core/built/admin/assets/esm-CXo5SEPy.js +2 -0
- package/core/built/admin/assets/{explore-CA-R01Br.js → explore-C3rzONJv.js} +1 -1
- package/core/built/admin/assets/{feed-ClC8_Gld.js → feed-DpUwS1cU.js} +1 -1
- package/core/built/admin/assets/{field-B3OPenbP.js → field-BQqLTgHM.js} +1 -1
- package/core/built/admin/assets/{filter-sources-CiPLnG9A.js → filter-sources-FgqoUIL7.js} +1 -1
- package/core/built/admin/assets/{filters-yFvybRMe.js → filters-D0uIMD8n.js} +1 -1
- package/core/built/admin/assets/filters-DZJT29GW.js +1 -0
- package/core/built/admin/assets/{first-promoter-modal-BC0UpYCD.js → first-promoter-modal-DJhQQheG.js} +1 -1
- package/core/built/admin/assets/framework-provider-CFLwK5uS.js +4 -0
- package/core/built/admin/assets/{ghost-d38c30b866e59f2e06a357b5eb844fe5.css → ghost-68f447a1796b93a1f128b649bac8506d.css} +1 -1
- package/core/built/admin/assets/{ghost-d683fbeb945b13155ea669caa5d8db41.js → ghost-ba408063a5922b458b4a8f216cf23251.js} +124 -342
- package/core/built/admin/assets/{ghost-dark-1bffd5099ba7a749303133efacf8480b.css → ghost-dark-28ae4899c92022857f3584694bcb50ef.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-BKRuqo-k.js → gift-link-modal-sdFLjND9.js} +1 -1
- package/core/built/admin/assets/{gift-links-CVuqLhsZ.js → gift-links-D7BJ9qfe.js} +1 -1
- package/core/built/admin/assets/{global-data-provider-RzW6Bnlf.js → global-data-provider-CixmTX00.js} +1 -1
- package/core/built/admin/assets/{growth-ClNAldfL.js → growth-0YYkBEeS.js} +1 -1
- package/core/built/admin/assets/{growth-Dz8OSG06.js → growth-DpjAPeQT.js} +2 -2
- package/core/built/admin/assets/{history-modal-DIVuQ5Ct.js → history-modal-BWJ4-cJ0.js} +1 -1
- package/core/built/admin/assets/{hooks-D6EE7BIt.js → hooks-CJepWnbB.js} +1 -1
- package/core/built/admin/assets/{html-field-CVZ2sMxw.js → html-field-C-oa5DEl.js} +1 -1
- package/core/built/admin/assets/{iframe-buffering-e8vH4-Qn.js → iframe-buffering-Ba9sDpi2.js} +1 -1
- package/core/built/admin/assets/{images-Ok8FFJ3A.js → images-3cQgwfTD.js} +1 -1
- package/core/built/admin/assets/{inbox-CMUVI6qb.js → inbox-Dz_JblyJ.js} +6 -6
- package/core/built/admin/assets/index-BOJzlYiz.js +67 -0
- package/core/built/admin/assets/index-Bid6LAyc.css +1 -0
- package/core/built/admin/assets/{input-group-2XU-iuh_.js → input-group-DpogNUIt.js} +1 -1
- package/core/built/admin/assets/{integrations-BT6_iJvu.js → integrations-B9yYgci5.js} +1 -1
- package/core/built/admin/assets/{invite-user-modal-DrZ7xxrZ.js → invite-user-modal-DCiQSiuD.js} +1 -1
- package/core/built/admin/assets/{koenig-editor-base-BPj4S760.js → koenig-editor-base-BApy8Q7C.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +8328 -8294
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +84 -84
- package/core/built/admin/assets/{koenig-lexical-Cdjv-m3_.js → koenig-lexical-2KuqRwZI.js} +33 -33
- package/core/built/admin/assets/{kpi-card-J8Mj1sDX.js → kpi-card-Ck3tT2Wg.js} +1 -1
- package/core/built/admin/assets/{kpi-card-awos0BMH.js → kpi-card-DDIxptaS.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-SEhvH-8E.js → kpi-helpers-ssbuy-hD.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-R6BjsKhd.js → kpi-tabs-BqoIM6bI.js} +1 -1
- package/core/built/admin/assets/{label-BMC3dzLI.js → label-DodDsMzn.js} +1 -1
- package/core/built/admin/assets/label-picker-Cvvbmzb5.js +1 -0
- package/core/built/admin/assets/{labels-DAPjKPy2.js → labels-DqvSR6JF.js} +1 -1
- package/core/built/admin/assets/layout-DkkGyev2.js +1 -0
- package/core/built/admin/assets/limit-service-BhiEvqcP.js +20 -0
- package/core/built/admin/assets/{link-helpers-Bzkk68GD.js → link-helpers-kpbU8TWh.js} +1 -1
- package/core/built/admin/assets/luxon-D1RVjjpq.js +1 -0
- package/core/built/admin/assets/member-custom-fields-CvGcpaiW.js +1 -0
- package/core/built/admin/assets/member-detail-CMCMZ47_.js +1 -0
- package/core/built/admin/assets/members-sRl6fNfq.js +11 -0
- package/core/built/admin/assets/{moderation-mPjYHgZW.js → moderation-KhKwGqI1.js} +1 -1
- package/core/built/admin/assets/{multi-select-combobox-CsoyHAiX.js → multi-select-combobox-nhCff0_K.js} +1 -1
- package/core/built/admin/assets/{navigation-modal-D_c0badj.js → navigation-modal-MDJExWDJ.js} +1 -1
- package/core/built/admin/assets/newsletter-BIBHumfV.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-CfjBMH3v.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-B-OLfrvr.js → newsletter-emails-ClDL-7dA.js} +1 -1
- package/core/built/admin/assets/newsletters-CBe-IPzK.js +1 -0
- package/core/built/admin/assets/{newsletters-Du0lzskS.js → newsletters-ILsexy-5.js} +1 -1
- package/core/built/admin/assets/{note-CSVsUKBm.js → note-HnNUOt4d.js} +1 -1
- package/core/built/admin/assets/{notifications-DV9FRtDw.js → notifications-Cj5e-258.js} +1 -1
- package/core/built/admin/assets/nql-lang-CaWrLesS.js +19 -0
- package/core/built/admin/assets/{offers-Um6ab71O.js → offers-DMxebaoK.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BHwHtixC.js +1 -0
- package/core/built/admin/assets/{onboarding-route--jL5KLPD.js → onboarding-route-D_4jp7Nq.js} +1 -1
- package/core/built/admin/assets/overview-CezDdg-F.js +1 -0
- package/core/built/admin/assets/overview-D5mM51Lj.js +1 -0
- package/core/built/admin/assets/{page-header-DAvDYKO9.js → page-header-VLPJfj_d.js} +1 -1
- package/core/built/admin/assets/{pages-BVMnpI2O.js → pages-Gchxp5c7.js} +1 -1
- package/core/built/admin/assets/{pintura-modal-jiUCxX0Q.js → pintura-modal-BXXJ8Iaa.js} +1 -1
- package/core/built/admin/assets/{portal-frame-D4D4nEO6.js → portal-frame-BLCvF8f-.js} +1 -1
- package/core/built/admin/assets/portal-modal-DN80tAcy.js +1 -0
- package/core/built/admin/assets/{post-analytics-BhAtiFJp.js → post-analytics-Gv81eN_g.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-_ydSY8qO.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-DGg0FUqs.js → post-analytics-provider-O2YjZMhN.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DtFAC132.js → post-share-modal-CzYak_a5.js} +1 -1
- package/core/built/admin/assets/{posts-CxG7v0-M.js → posts-DgcUi2rc.js} +1 -1
- package/core/built/admin/assets/preferences-gneLb6LV.js +5 -0
- package/core/built/admin/assets/{preview-modal-DctYx20m.js → preview-modal-Bkq-tgVm.js} +1 -1
- package/core/built/admin/assets/profile-CzXmCNTC.js +1 -0
- package/core/built/admin/assets/{radio-group-CI5_Kf_d.js → radio-group-Dsq5ZEbV.js} +1 -1
- package/core/built/admin/assets/{referrers-B9QDAfYm.js → referrers-C3a2K8qB.js} +1 -1
- package/core/built/admin/assets/{router-provider-DSQfprxH.js → router-provider-CkB_ArLF.js} +1 -1
- package/core/built/admin/assets/{select-CkXqNJDJ.js → select-xuZAuDyS.js} +1 -1
- package/core/built/admin/assets/{settings-ZCj-W56o.js → settings-Ct8pabhr.js} +1 -1
- package/core/built/admin/assets/settings-KSWWQanQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-DIq31FwA.js → settings-breadcrumbs-DbPGu-Tu.js} +1 -1
- package/core/built/admin/assets/{share-modal-BNj14g79.js → share-modal-CdiUx9WM.js} +1 -1
- package/core/built/admin/assets/{slack-modal-C6SpMQ-q.js → slack-modal-BP--Z3jE.js} +1 -1
- package/core/built/admin/assets/{sort-button-B-k54ow2.js → sort-button-CjfKdlsL.js} +1 -1
- package/core/built/admin/assets/{source-icon-Cej3sZM6.js → source-icon-DHj1h6Ts.js} +1 -1
- package/core/built/admin/assets/{stats-B2KW8E2y.js → stats-C615kulJ.js} +1 -1
- package/core/built/admin/assets/{stats-view-CVzU2-p5.js → stats-view-BSMRdYiN.js} +1 -1
- package/core/built/admin/assets/{step-1-Dbn1iIMU.js → step-1-pSdGYtIF.js} +1 -1
- package/core/built/admin/assets/{step-2-BahnSuN6.js → step-2-T9Uy4eBD.js} +1 -1
- package/core/built/admin/assets/{step-3-CW04e2ZJ.js → step-3-D4uXe_XG.js} +2 -2
- package/core/built/admin/assets/{stripe-button-BkBZqaGG.js → stripe-button-Bh4p6Wud.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-C2vTvepi.js +1 -0
- package/core/built/admin/assets/{table-BtLcEi1M.js → table-DghJxZMH.js} +1 -1
- package/core/built/admin/assets/{tabs-BaWRfg8j.js → tabs-l9c7FjzI.js} +1 -1
- package/core/built/admin/assets/tag-detail-DBw_M2-p.js +2 -0
- package/core/built/admin/assets/{tags-BbygUvNB.js → tags-DoWsAG6l.js} +1 -1
- package/core/built/admin/assets/{tags-BwCgmr5v.js → tags-EmEvJN75.js} +1 -1
- package/core/built/admin/assets/{testimonials-modal--bdzppyh.js → testimonials-modal-BZKePE2T.js} +1 -1
- package/core/built/admin/assets/tier-detail-modal-D65pVWsN.js +1 -0
- package/core/built/admin/assets/{tiers-Da6kxHQP.js → tiers-8iTFjJWg.js} +1 -1
- package/core/built/admin/assets/{toggle-group-CVl-Xigr.js → toggle-group-uJnl42w3.js} +1 -1
- package/core/built/admin/assets/{topic-filter-BoRHRbL1.js → topic-filter-BM7hz0kW.js} +1 -1
- package/core/built/admin/assets/transistor-modal-VZOGN3od.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-CixoRAO2.js → unsplash-modal-CKGfACxF.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-BiWvcf1u.js → use-analytics-data-IxJcSjc9.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-CGp3CGT5.js +1 -0
- package/core/built/admin/assets/use-feature-flag-CSq2Lwzt.js +1 -0
- package/core/built/admin/assets/use-filter-params-0hcJJuEJ.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-iMpDZBxD.js → use-filterable-api-CQBtVG-l.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-Dom8xVzo.js → use-growth-stats-1NW-8eEZ.js} +1 -1
- package/core/built/admin/assets/use-limiter-ffwpsZPp.js +2 -0
- package/core/built/admin/assets/{use-pintura-config-Bj4QmiVk.js → use-pintura-config-BjFChXV1.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-DvrslmPE.js → use-pintura-editor-Cb41oJyL.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-pxIHTXFs.js → use-post-referrers-Dp1BnTAK.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-CmFe0N6F.js → use-reply-chain-data-C0ZUTkfk.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-Cmp_sy-d.js → use-setting-group-Bx8M6FL2.js} +1 -1
- package/core/built/admin/assets/{use-settings-navigation-BmROPrls.js → use-settings-navigation-BOZcat_V.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-D5rL3YwO.js → use-simple-pagination-DsEGSJhe.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-DP2dMaq7.js → use-tinybird-query-Ci_DiPkX.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-DrK_skPQ.js → use-tinybird-token-CsUwc722.js} +1 -1
- package/core/built/admin/assets/use-upgrade-route-B3_DUiuT.js +1 -0
- package/core/built/admin/assets/{use-url-input-ChrUZBFf.js → use-url-input-BEZJpVgN.js} +2 -2
- package/core/built/admin/assets/user-detail-modal-595z1N5r.js +1 -0
- package/core/built/admin/assets/{users-B6hIds6n.js → users-DmGMPfw7.js} +1 -1
- package/core/built/admin/assets/virtual-list-D5ugXyZY.js +1 -0
- package/core/built/admin/assets/{web-D71mjB2r.js → web-CAta3lRE.js} +1 -1
- package/core/built/admin/assets/web-Ds9_wWJK.js +2 -0
- package/core/built/admin/assets/zapier-modal-CcfDZY53.js +1 -0
- package/core/built/admin/index.html +12 -12
- package/core/frontend/apps/private-blogging/lib/middleware.js +5 -58
- package/core/frontend/helpers/ghost_head.js +52 -0
- package/core/frontend/meta/asset-url.js +20 -9
- package/core/frontend/public/cards.manifest.json +1 -0
- package/core/frontend/public/ghost-stats.min.js +1 -1
- package/core/frontend/services/assets-minification/card-assets.js +121 -46
- package/core/frontend/services/assets-minification/index.js +13 -4
- package/core/frontend/services/data/fetch-data.js +7 -14
- package/core/frontend/services/llms/service.js +27 -6
- package/core/frontend/services/proxy.js +1 -0
- package/core/frontend/services/routing/api-adapter.js +11 -5
- package/core/frontend/services/routing/collection-router.js +1 -2
- package/core/frontend/services/routing/controllers/entry/markdown.js +63 -5
- package/core/frontend/services/routing/controllers/entry.js +1 -1
- package/core/frontend/services/routing/controllers/previews.js +0 -3
- package/core/frontend/services/routing/events.js +2 -1
- package/core/frontend/services/routing/parent-router.js +0 -22
- package/core/frontend/services/routing/router-manager.js +2 -2
- package/core/frontend/services/routing/static-pages-router.js +0 -1
- package/core/frontend/services/sitemap/handler.js +3 -4
- package/core/frontend/services/sitemap/site-map-manager.js +1 -2
- package/core/frontend/services/theme-engine/i18n/i18n.js +0 -33
- package/core/frontend/web/middleware/llms-discovery.js +2 -2
- package/core/frontend/web/routers/serve-public-file.js +33 -3
- package/core/frontend/web/site.js +5 -4
- package/core/server/adapters/jobs/InMemoryJobsBackend.js +113 -0
- package/core/server/adapters/redirects/S3RedirectsStore.js +33 -13
- package/core/server/adapters/route-settings/S3RouteSettingsStore.js +33 -13
- package/core/server/adapters/scheduling/scheduling-default.js +6 -7
- package/core/server/adapters/storage/LocalStorageBase.js +1 -3
- package/core/server/api/endpoints/index.js +0 -4
- package/core/server/api/endpoints/member-custom-fields.js +15 -4
- package/core/server/api/endpoints/pages.js +3 -11
- package/core/server/api/endpoints/posts.js +3 -11
- package/core/server/api/endpoints/utils/api-filter-utils.js +15 -1
- package/core/server/api/endpoints/utils/serializers/input/settings.js +4 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/email-batches.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/snippets.js +0 -1
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +1 -0
- package/core/server/data/exporter/table-lists.js +4 -1
- package/core/server/data/migrations/utils/schema.js +5 -3
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +19 -7
- package/core/server/data/migrations/versions/6.58/2026-08-10-08-20-50-add-machine-payments-settings.js +41 -0
- package/core/server/data/migrations/versions/6.58/2026-08-10-08-21-00-add-machine-payment-events-table.js +18 -0
- package/core/server/data/migrations/versions/6.58/2026-08-10-15-24-09-reset-custom-field-definitions-for-underscored-keys.js +66 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-10-00-00-swap-custom-field-values-to-key-fk.js +65 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-10-34-25-remove-legacy-explore-integration.js +131 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-16-00-00-add-sort-order-to-members-custom-fields.js +13 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-21-22-48-rename-email-batches-provider-id.js +8 -0
- package/core/server/data/migrations/versions/6.58/2026-08-13-16-07-33-add-automation-runs-automation-id-created-at-index.js +28 -0
- package/core/server/data/schema/README.md +87 -0
- package/core/server/data/schema/commands.js +5 -2
- package/core/server/data/schema/default-settings/default-settings.json +27 -0
- package/core/server/data/schema/fixtures/fixtures.json +0 -21
- package/core/server/data/schema/schema.js +37 -9
- package/core/server/data/seeders/data-generator.js +3 -1
- package/core/server/data/seeders/importers/automation-action-edges-importer.js +38 -0
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +139 -0
- package/core/server/data/seeders/importers/automation-actions-importer.js +84 -0
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +134 -0
- package/core/server/data/seeders/importers/automation-runs-importer.js +97 -0
- package/core/server/data/seeders/importers/automations-importer.js +78 -0
- package/core/server/data/seeders/importers/benefits-importer.js +1 -1
- package/core/server/data/seeders/importers/comment-reports-importer.js +5 -5
- package/core/server/data/seeders/importers/comments-importer.js +6 -6
- package/core/server/data/seeders/importers/email-batches-importer.js +9 -8
- package/core/server/data/seeders/importers/email-recipient-failures-importer.js +1 -1
- package/core/server/data/seeders/importers/email-recipients-importer.js +12 -12
- package/core/server/data/seeders/importers/emails-importer.js +7 -7
- package/core/server/data/seeders/importers/index.js +6 -0
- package/core/server/data/seeders/importers/labels-importer.js +4 -4
- package/core/server/data/seeders/importers/members-click-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-created-events-importer.js +5 -5
- package/core/server/data/seeders/importers/members-feedback-importer.js +7 -7
- package/core/server/data/seeders/importers/members-importer.js +5 -5
- package/core/server/data/seeders/importers/members-labels-importer.js +1 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-products-importer.js +3 -3
- package/core/server/data/seeders/importers/members-status-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +3 -3
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +8 -8
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +3 -3
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +4 -4
- package/core/server/data/seeders/importers/newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +6 -6
- package/core/server/data/seeders/importers/offers-importer.js +4 -4
- package/core/server/data/seeders/importers/posts-authors-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-importer.js +5 -5
- package/core/server/data/seeders/importers/posts-products-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-tags-importer.js +1 -1
- package/core/server/data/seeders/importers/products-benefits-importer.js +1 -1
- package/core/server/data/seeders/importers/products-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendation-click-events-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendation-subscribe-events-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendations-importer.js +1 -1
- package/core/server/data/seeders/importers/redirects-importer.js +1 -1
- package/core/server/data/seeders/importers/roles-users-importer.js +1 -1
- package/core/server/data/seeders/importers/stripe-prices-importer.js +1 -1
- package/core/server/data/seeders/importers/stripe-products-importer.js +1 -1
- package/core/server/data/seeders/importers/table-importer.js +92 -91
- package/core/server/data/seeders/importers/tags-importer.js +3 -3
- package/core/server/data/seeders/importers/users-importer.js +3 -3
- package/core/server/data/seeders/importers/web-mentions-importer.js +3 -3
- package/core/server/data/seeders/utils/database-date.js +14 -18
- package/core/server/data/seeders/utils/json-importer.js +1 -1
- package/core/server/data/seeders/utils/random.js +8 -7
- package/core/server/data/seeders/utils/topological-sort.js +5 -3
- package/core/server/data/tinybird/ARCHITECTURE.md +1 -2
- package/core/server/models/base/plugins/crud.js +0 -5
- package/core/server/models/index.js +2 -0
- package/core/server/models/machine-payment-event.js +18 -0
- package/core/server/models/member.js +24 -0
- package/core/server/models/settings.js +32 -3
- package/core/server/models/user.js +1 -1
- package/core/server/services/README.md +30 -0
- package/core/server/services/activitypub/activity-pub-service.js +2 -3
- package/core/server/services/adapter-manager/base-classes.js +3 -1
- package/core/server/services/auth/passwordreset.js +47 -51
- package/core/server/services/automations/database-automations-repository.js +19 -3
- package/core/server/services/automations/poll.js +2 -8
- package/core/server/services/comments/comments-service.js +4 -16
- package/core/server/services/comments/index.js +1 -3
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +68 -119
- package/core/server/services/email-analytics/email-analytics-service.js +73 -229
- package/core/server/services/email-analytics/event-processing-result.js +0 -10
- package/core/server/services/email-analytics/fetch-mailgun-events.js +25 -0
- package/core/server/services/email-analytics/index.js +35 -67
- package/core/server/services/email-analytics/lib/queries.js +88 -51
- package/core/server/services/email-analytics/newsletter-email-analytics-batch-processor.js +4 -1
- package/core/server/services/email-service/batch-sending-service.js +1 -1
- package/core/server/services/email-service/email-event-processor.js +12 -12
- package/core/server/services/email-service/events/email-bounced-event.js +8 -46
- package/core/server/services/email-service/events/email-delivered-event.js +8 -36
- package/core/server/services/email-service/events/email-opened-event.js +8 -36
- package/core/server/services/email-service/events/email-temporary-bounced-event.js +8 -46
- package/core/server/services/email-service/events/email-unsubscribed-event.js +8 -31
- package/core/server/services/email-service/events/spam-complaint-event.js +8 -31
- package/core/server/services/email-service/newsletter-email-event-storage.js +8 -7
- package/core/server/services/email-service/sending-service.js +1 -1
- package/core/server/services/email-suppression-list/mailgun-email-suppression-list.js +2 -2
- package/core/server/services/email-suppression-list/service.js +1 -3
- package/core/server/services/explore-ping/explore-ping-service.js +1 -6
- package/core/server/services/explore-ping/index.js +0 -2
- package/core/server/services/gifts/index.js +109 -3
- package/core/server/services/gifts/jobs/clean-gifts-job.js +1 -1
- package/core/server/services/gifts/jobs/send-gift-reminders-job.js +1 -1
- package/core/server/services/jobs-service/index.js +36 -0
- package/core/server/services/jobs-service/job.js +6 -0
- package/core/server/services/jobs-service/jobs-service.js +91 -0
- package/core/server/services/jobs-service/register-job-handlers.js +6 -0
- package/core/server/services/lib/mailgun-client.js +1 -1
- package/core/server/services/machine-payments/README.md +23 -0
- package/core/server/services/machine-payments/adapters/mpp-adapter.js +195 -0
- package/core/server/services/machine-payments/content-loader.js +89 -0
- package/core/server/services/machine-payments/eligibility.js +6 -0
- package/core/server/services/machine-payments/events/machine-payment-event-repository.js +58 -0
- package/core/server/services/machine-payments/events/machine-payment-event.js +46 -0
- package/core/server/services/machine-payments/index.js +87 -0
- package/core/server/services/machine-payments/pricing.js +54 -0
- package/core/server/services/machine-payments/service.js +275 -0
- package/core/server/services/machine-payments/stripe/deposit-address-store.js +157 -0
- package/core/server/services/machine-payments/stripe/payment-recorder.js +85 -0
- package/core/server/services/machine-payments/stripe/stripe-client-options.js +25 -0
- package/core/server/services/machine-payments/types.js +2 -0
- package/core/server/services/members/import-export/csv/parse.js +9 -1
- package/core/server/services/members/import-export/import/completion-email.js +61 -27
- package/core/server/services/members/import-export/import/email-template.js +28 -15
- package/core/server/services/members/import-export/import/importer.js +99 -51
- package/core/server/services/members/import-export/import/spool.js +1 -1
- package/core/server/services/members/import-export/index.js +24 -1
- package/core/server/services/members/members-api/services/member-bread-service.js +2 -2
- package/core/server/services/members/members-config-provider.js +12 -2
- package/core/server/services/members-custom-fields/actions.js +2 -1
- package/core/server/services/members-custom-fields/definitions-service.js +109 -27
- package/core/server/services/members-custom-fields/filter.js +199 -0
- package/core/server/services/members-custom-fields/key.js +37 -0
- package/core/server/services/members-custom-fields/queries.js +15 -0
- package/core/server/services/members-custom-fields/schema.js +1 -1
- package/core/server/services/members-custom-fields/values-service.js +18 -14
- package/core/server/services/members-events/last-seen-at-updater.js +1 -1
- package/core/server/services/oembed/oembed-service.js +8 -2
- package/core/server/services/permissions/can-this.js +4 -1
- package/core/server/services/posts/posts-service.js +18 -16
- package/core/server/services/public-config/config.js +1 -1
- package/core/server/services/route-settings/dynamic-routing-service.js +2 -0
- package/core/server/services/settings-helpers/settings-helpers.js +0 -1
- package/core/server/services/stats/posts-stats-service.js +0 -2
- package/core/server/services/stripe/stripe-api.js +2 -9
- package/core/server/web/api/endpoints/admin/middleware.js +0 -1
- package/core/server/web/api/endpoints/admin/routes.js +2 -3
- package/core/server/web/comments/routes.js +31 -8
- package/core/shared/config/defaults.json +17 -2
- package/core/shared/config/loader.js +11 -2
- package/core/shared/config/overrides.json +2 -0
- package/core/shared/config/secrets.js +71 -0
- package/core/shared/labs.js +4 -13
- package/core/shared/machine-payments.js +30 -0
- package/core/shared/private-site-access/index.js +68 -0
- package/package.json +48 -43
- package/pnpm-lock.yaml +1412 -1583
- package/pnpm-workspace.yaml +85 -48
- package/bin/validate-adapters.ts +0 -87
- package/components/tryghost-adapter-base-scheduling-0.2.1.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.3.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.3.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.4.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.4.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.5.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.3.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.3.tgz +0 -0
- package/content/themes/casper/assets/built/casper.js.map +0 -1
- package/content/themes/casper/assets/built/global.css.map +0 -1
- package/content/themes/casper/assets/built/screen.css.map +0 -1
- package/content/themes/source/assets/built/screen.css.map +0 -1
- package/content/themes/source/assets/built/source.js.map +0 -1
- package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +0 -1
- package/core/built/admin/assets/add-integration-modal-DU9ghGLS.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-DFxuQrYu.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-DGCcezB6.js +0 -1
- package/core/built/admin/assets/card-aUHX0hyR.js +0 -1
- package/core/built/admin/assets/chunk.524.dce1c51e0bc30fb2718e.js +0 -25
- package/core/built/admin/assets/chunk.582.3a430d5c6a744fd475b6.js +0 -27
- package/core/built/admin/assets/code-editor-view-BcyKABrG.js +0 -1
- package/core/built/admin/assets/color-picker-field-8NoSnhNI.js +0 -1
- package/core/built/admin/assets/command-BOYMbZa_.js +0 -1
- package/core/built/admin/assets/comments-BhNM5CYl.js +0 -1
- package/core/built/admin/assets/confirmation-modal-CYD7x3UQ.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-bk3lNEuQ.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-B2xYKS1I.js +0 -23
- package/core/built/admin/assets/dist-Bg8bSvLA.js +0 -6
- package/core/built/admin/assets/dist-BnFdlebk.js +0 -11
- package/core/built/admin/assets/dist-CsvXCJSe.js +0 -23
- package/core/built/admin/assets/editor-BBKrunMK.js +0 -8
- package/core/built/admin/assets/filters-Bz0HsRie.js +0 -1
- package/core/built/admin/assets/framework-provider-CmtnPXIT.js +0 -4
- package/core/built/admin/assets/index-DUXM0NlS.js +0 -67
- package/core/built/admin/assets/index-Ds2iUoF6.css +0 -1
- package/core/built/admin/assets/label-picker-Bnlru5tO.js +0 -1
- package/core/built/admin/assets/layout-yR9rlvEw.js +0 -1
- package/core/built/admin/assets/limit-modal-COmLevuY.js +0 -1
- package/core/built/admin/assets/limit-service-9fwD33lU.js +0 -20
- package/core/built/admin/assets/member-custom-fields-BvFKEpxJ.js +0 -1
- package/core/built/admin/assets/member-detail-CWdPAe3p.js +0 -1
- package/core/built/admin/assets/members-Bnt0ih2R.js +0 -11
- package/core/built/admin/assets/newsletter-DR49NyGS.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-B45pL1pb.js +0 -1
- package/core/built/admin/assets/newsletters-DGs5BaKH.js +0 -1
- package/core/built/admin/assets/nql-lang-B7faUf8D.js +0 -19
- package/core/built/admin/assets/offers-container-modal-B0hFS8nk.js +0 -1
- package/core/built/admin/assets/overview-B-ZPNqMo.js +0 -1
- package/core/built/admin/assets/overview-D96iwoJi.js +0 -1
- package/core/built/admin/assets/portal-modal-BLFdYeDH.js +0 -1
- package/core/built/admin/assets/post-analytics-header-DK27VjJm.js +0 -1
- package/core/built/admin/assets/preferences-CuWLISHa.js +0 -5
- package/core/built/admin/assets/profile-BsKYYtuL.js +0 -1
- package/core/built/admin/assets/settings-B4vM-rgV.js +0 -4
- package/core/built/admin/assets/stripe-connect-modal-CYDDrSJm.js +0 -1
- package/core/built/admin/assets/tag-detail-CmqiC32L.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BZmAsIy4.js +0 -1
- package/core/built/admin/assets/transistor-modal-CBPDqEGy.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B14lZukR.js +0 -2
- package/core/built/admin/assets/use-feature-flag-BmJoc5WN.js +0 -1
- package/core/built/admin/assets/use-filter-params-ChoV72Hw.js +0 -1
- package/core/built/admin/assets/use-limiter-Dwg8ivgQ.js +0 -2
- package/core/built/admin/assets/user-detail-modal-XODpOqNC.js +0 -1
- package/core/built/admin/assets/virtual-list-Dnoafqbk.js +0 -1
- package/core/built/admin/assets/web-z71lA1Y3.js +0 -2
- package/core/built/admin/assets/zapier-modal-Bp507Um7.js +0 -1
- package/core/frontend/meta/og-type.ts +0 -13
- package/core/frontend/services/routing/api-adapter.ts +0 -157
- package/core/frontend/services/routing/config.ts +0 -59
- package/core/frontend/services/routing/controllers/entry/canonical-url.ts +0 -14
- package/core/frontend/services/routing/controllers/entry/gift-links.ts +0 -56
- package/core/frontend/services/routing/controllers/entry/markdown.ts +0 -72
- package/core/frontend/services/routing/controllers/entry.ts +0 -120
- package/core/frontend/services/routing/controllers/rss.ts +0 -66
- package/core/frontend/services/routing/permalink-adapter.ts +0 -10
- package/core/frontend/src/utils/privacy.ts +0 -66
- package/core/server/adapters/redirects/FileStore.ts +0 -113
- package/core/server/adapters/redirects/S3RedirectsStore.ts +0 -185
- package/core/server/adapters/route-settings/FileStore.ts +0 -144
- package/core/server/adapters/route-settings/S3RouteSettingsStore.ts +0 -199
- package/core/server/adapters/route-settings/utils.ts +0 -17
- package/core/server/adapters/scheduling/deps.d.ts +0 -1
- package/core/server/adapters/scheduling/error-capture.ts +0 -91
- package/core/server/adapters/scheduling/scheduling-default.ts +0 -248
- package/core/server/adapters/scheduling/utils.ts +0 -36
- package/core/server/adapters/sso/DefaultSSOAdapter.ts +0 -15
- package/core/server/adapters/storage/LocalFilesStorage.ts +0 -24
- package/core/server/adapters/storage/LocalImagesStorage.ts +0 -24
- package/core/server/adapters/storage/LocalMediaStorage.ts +0 -24
- package/core/server/adapters/storage/LocalStorageBase.ts +0 -342
- package/core/server/adapters/storage/S3Storage.ts +0 -506
- package/core/server/adapters/storage/utils.ts +0 -60
- package/core/server/api/endpoints/automation-action-links.ts +0 -46
- package/core/server/api/endpoints/explore.js +0 -18
- package/core/server/api/endpoints/gift-links.ts +0 -87
- package/core/server/api/endpoints/libraries.d.ts +0 -2
- package/core/server/api/endpoints/member-commenting.ts +0 -92
- package/core/server/api/endpoints/member-custom-fields.ts +0 -106
- package/core/server/api/endpoints/utils/api-filter-utils.ts +0 -22
- package/core/server/api/endpoints/utils/gift-link-access.ts +0 -82
- package/core/server/api/endpoints/utils/public-cache-keys.ts +0 -47
- package/core/server/api/endpoints/utils/serializers/input/member-commenting.ts +0 -29
- package/core/server/api/endpoints/utils/serializers/output/explore.js +0 -11
- package/core/server/api/endpoints/utils/serializers/output/gift-links.ts +0 -21
- package/core/server/api/endpoints/utils/serializers/output/member-commenting.ts +0 -8
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.ts +0 -25
- package/core/server/api/endpoints/utils/serializers/output/members.d.ts +0 -20
- package/core/server/api/endpoints/utils/serializers/output/posts-csv-transform.ts +0 -46
- package/core/server/api/endpoints/utils/serializers/output/stream-csv-response.ts +0 -47
- package/core/server/api/endpoints/utils/validate-email-sender-fields.ts +0 -42
- package/core/server/data/importer/email-template.ts +0 -213
- package/core/server/data/seeders/utils/topological-sort.ts +0 -48
- package/core/server/lib/auto-filling-map.ts +0 -23
- package/core/server/lib/bootstrap-socket.ts +0 -114
- package/core/server/lib/case-keys.ts +0 -30
- package/core/server/lib/common/to-plain.ts +0 -25
- package/core/server/lib/db-date.ts +0 -8
- package/core/server/lib/get-inbox-links.ts +0 -276
- package/core/server/lib/post-revisions.ts +0 -152
- package/core/server/lib/soonest-timer.ts +0 -57
- package/core/server/models/base/plugins/bulk-filters.ts +0 -35
- package/core/server/models/role-utils.ts +0 -56
- package/core/server/services/activitypub/activity-pub-service.ts +0 -216
- package/core/server/services/adapter-manager/adapter-manager.ts +0 -253
- package/core/server/services/adapter-manager/adapter-paths.ts +0 -26
- package/core/server/services/adapter-manager/base-classes.ts +0 -22
- package/core/server/services/adapter-manager/index.ts +0 -16
- package/core/server/services/adapter-manager/types.ts +0 -45
- package/core/server/services/adapter-manager/utils.ts +0 -308
- package/core/server/services/auth/reset-authentication.ts +0 -72
- package/core/server/services/automations/automations-api.ts +0 -396
- package/core/server/services/automations/automations-repository.ts +0 -218
- package/core/server/services/automations/constants.ts +0 -4
- package/core/server/services/automations/database-automations-repository.ts +0 -1513
- package/core/server/services/automations/database-date.ts +0 -26
- package/core/server/services/automations/fake-wait-hours-multiplier.ts +0 -12
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +0 -8
- package/core/server/services/automations/mailgun-message-id.ts +0 -27
- package/core/server/services/automations/poll.ts +0 -325
- package/core/server/services/automations/scheduler-poll-time.ts +0 -38
- package/core/server/services/automations/service.ts +0 -131
- package/core/server/services/automations/stale-lock-cutoff.ts +0 -5
- package/core/server/services/comments/email-templates/new-comment-reply.txt.ts +0 -25
- package/core/server/services/comments/email-templates/new-comment.txt.ts +0 -25
- package/core/server/services/comments/email-templates/report.txt.ts +0 -34
- package/core/server/services/custom-redirects/redirect-config-parser.ts +0 -148
- package/core/server/services/custom-redirects/redirects-service.ts +0 -181
- package/core/server/services/custom-redirects/utils.ts +0 -7
- package/core/server/services/donations/donation-bookshelf-repository.ts +0 -63
- package/core/server/services/donations/donation-payment-event.ts +0 -51
- package/core/server/services/email-address/email-address-parser.d.ts +0 -12
- package/core/server/services/email-address/email-address-service.ts +0 -241
- package/core/server/services/email-analytics/automation-email-analytics-batch-processor.ts +0 -130
- package/core/server/services/email-analytics/batch-event-processor.ts +0 -18
- package/core/server/services/email-analytics/email-analytics-provider-mailgun.js +0 -57
- package/core/server/services/email-analytics/event-processing-result.ts +0 -66
- package/core/server/services/email-analytics/events/start-automation-email-analytics-job-event.ts +0 -14
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.ts +0 -15
- package/core/server/services/email-analytics/index.ts +0 -109
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.ts +0 -126
- package/core/server/services/email-analytics/lib/queries.ts +0 -341
- package/core/server/services/email-service/domain-warming-service.ts +0 -91
- package/core/server/services/explore/explore-service.js +0 -59
- package/core/server/services/explore/index.js +0 -18
- package/core/server/services/explore-ping/explore-ping-service.ts +0 -198
- package/core/server/services/explore-ping/index.ts +0 -42
- package/core/server/services/files/file-type-utils.ts +0 -56
- package/core/server/services/gift-links/actions.ts +0 -51
- package/core/server/services/gift-links/codec.ts +0 -16
- package/core/server/services/gift-links/index.ts +0 -20
- package/core/server/services/gift-links/models.ts +0 -22
- package/core/server/services/gift-links/schema.ts +0 -33
- package/core/server/services/gift-links/serializers.ts +0 -20
- package/core/server/services/gift-links/service.ts +0 -95
- package/core/server/services/gifts/constants.ts +0 -3
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.ts +0 -43
- package/core/server/services/gifts/email-templates/gift-reminder.ts +0 -50
- package/core/server/services/gifts/gift-bookshelf-repository.ts +0 -366
- package/core/server/services/gifts/gift-checkout-offer.ts +0 -127
- package/core/server/services/gifts/gift-codec.ts +0 -62
- package/core/server/services/gifts/gift-controller.ts +0 -51
- package/core/server/services/gifts/gift-email-renderer.ts +0 -97
- package/core/server/services/gifts/gift-email-service.ts +0 -158
- package/core/server/services/gifts/gift-reminder-scheduler.ts +0 -107
- package/core/server/services/gifts/gift-schema.ts +0 -39
- package/core/server/services/gifts/gift-service-wrapper.js +0 -142
- package/core/server/services/gifts/gift-service.ts +0 -1034
- package/core/server/services/gifts/gift.ts +0 -234
- package/core/server/services/identity-tokens/identity-token-service.ts +0 -36
- package/core/server/services/indexnow-ping/constants.ts +0 -32
- package/core/server/services/indexnow-ping/index.ts +0 -35
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +0 -311
- package/core/server/services/internal-keys/index.ts +0 -53
- package/core/server/services/lib/dynamic-redirect-manager.d.ts +0 -20
- package/core/server/services/lib/in-memory-repository.ts +0 -78
- package/core/server/services/lib/member-signup-contexts.ts +0 -22
- package/core/server/services/link-redirection/link-redirect.ts +0 -24
- package/core/server/services/member-welcome-emails/constants.ts +0 -35
- package/core/server/services/members/commenting/index.ts +0 -2
- package/core/server/services/members/commenting/member-commenting-codec.ts +0 -73
- package/core/server/services/members/commenting/member-commenting.ts +0 -69
- package/core/server/services/members/create-paid-member-shim.ts +0 -40
- package/core/server/services/members/import-export/config.ts +0 -34
- package/core/server/services/members/import-export/csv/formula.ts +0 -14
- package/core/server/services/members/import-export/csv/index.ts +0 -3
- package/core/server/services/members/import-export/csv/parse.ts +0 -69
- package/core/server/services/members/import-export/csv/serialize.ts +0 -18
- package/core/server/services/members/import-export/export/exporter.ts +0 -325
- package/core/server/services/members/import-export/import/completion-email.ts +0 -146
- package/core/server/services/members/import-export/import/importer.ts +0 -489
- package/core/server/services/members/import-export/import/reader.ts +0 -33
- package/core/server/services/members/import-export/import/row.ts +0 -56
- package/core/server/services/members/import-export/import/spool.ts +0 -38
- package/core/server/services/members/import-export/index.ts +0 -143
- package/core/server/services/members/index.d.ts +0 -32
- package/core/server/services/members-custom-fields/actions.ts +0 -62
- package/core/server/services/members-custom-fields/codec.ts +0 -13
- package/core/server/services/members-custom-fields/config.ts +0 -38
- package/core/server/services/members-custom-fields/definitions-service.ts +0 -413
- package/core/server/services/members-custom-fields/index.ts +0 -50
- package/core/server/services/members-custom-fields/models.ts +0 -15
- package/core/server/services/members-custom-fields/queries.ts +0 -13
- package/core/server/services/members-custom-fields/schema.ts +0 -71
- package/core/server/services/members-custom-fields/serializers.ts +0 -22
- package/core/server/services/members-custom-fields/storage.ts +0 -122
- package/core/server/services/members-custom-fields/values-service.ts +0 -269
- package/core/server/services/notifications/notification-email.ts +0 -66
- package/core/server/services/notifications/sanitize-email-html.ts +0 -35
- package/core/server/services/post-scheduling/index.ts +0 -13
- package/core/server/services/post-scheduling/post-scheduling.ts +0 -133
- package/core/server/services/recommendations/service/bookshelf-click-event-repository.ts +0 -49
- package/core/server/services/recommendations/service/bookshelf-recommendation-repository.ts +0 -117
- package/core/server/services/recommendations/service/bookshelf-repository.ts +0 -192
- package/core/server/services/recommendations/service/bookshelf-subscribe-event-repository.ts +0 -49
- package/core/server/services/recommendations/service/click-event.ts +0 -32
- package/core/server/services/recommendations/service/in-memory-recommendation-repository.ts +0 -20
- package/core/server/services/recommendations/service/incoming-recommendation-controller.ts +0 -51
- package/core/server/services/recommendations/service/incoming-recommendation-email-renderer.ts +0 -37
- package/core/server/services/recommendations/service/incoming-recommendation-service.ts +0 -159
- package/core/server/services/recommendations/service/index.ts +0 -15
- package/core/server/services/recommendations/service/libraries.d.ts +0 -2
- package/core/server/services/recommendations/service/recommendation-controller.ts +0 -257
- package/core/server/services/recommendations/service/recommendation-metadata-service.ts +0 -132
- package/core/server/services/recommendations/service/recommendation-repository.ts +0 -13
- package/core/server/services/recommendations/service/recommendation-service.ts +0 -285
- package/core/server/services/recommendations/service/recommendation.ts +0 -200
- package/core/server/services/recommendations/service/subscribe-event.ts +0 -32
- package/core/server/services/recommendations/service/unsafe-data.ts +0 -217
- package/core/server/services/recommendations/service/well-known-service.ts +0 -47
- package/core/server/services/remote-flags/index.ts +0 -101
- package/core/server/services/remote-flags/remote-flags-service.ts +0 -236
- package/core/server/services/remote-flags/resolve.ts +0 -108
- package/core/server/services/route-settings/route-settings-parser.ts +0 -455
- package/core/server/services/route-settings/validation-errors.ts +0 -271
- package/core/server/services/route-settings/yaml-parser.d.ts +0 -4
- package/core/server/services/slack-ping/constants.ts +0 -15
- package/core/server/services/slack-ping/index.ts +0 -35
- package/core/server/services/slack-ping/slack-ping-service.ts +0 -295
- package/core/server/services/staff/email-templates/donation.txt.ts +0 -26
- package/core/server/services/staff/email-templates/gift.txt.ts +0 -27
- package/core/server/services/staff/email-templates/new-free-signup.txt.ts +0 -15
- package/core/server/services/staff/email-templates/new-gift-subscription.txt.ts +0 -26
- package/core/server/services/staff/email-templates/new-milestone-received.txt.ts +0 -20
- package/core/server/services/staff/email-templates/new-paid-cancellation.txt.ts +0 -15
- package/core/server/services/staff/email-templates/new-paid-started.txt.ts +0 -15
- package/core/server/services/staff/email-templates/recommendation-received.txt.ts +0 -23
- package/core/server/services/staff/email-templates/types.ts +0 -13
- package/core/server/services/url/lazy-find-resource.ts +0 -104
- package/core/server/services/url/lazy-url-service.ts +0 -647
- package/core/server/services/url/permalink-matcher.ts +0 -119
- package/core/server/services/url/router-filter.ts +0 -80
- package/core/server/services/verification/verification-webhook-service.ts +0 -137
- package/core/shared/config/helpers.ts +0 -133
- package/core/shared/config/index.ts +0 -12
- package/core/shared/config/loader.ts +0 -87
- package/core/shared/config/utils.ts +0 -104
- package/core/shared/errify.ts +0 -31
- package/core/shared/events-ts/index.ts +0 -8
- package/core/shared/events-ts/post-deleted-event.ts +0 -15
- package/core/shared/events-ts/posts-bulk-add-tags-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-destroyed-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-featured-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unfeatured-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unpublished-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unscheduled-event.ts +0 -13
- package/core/shared/labs-flag-overrides.ts +0 -35
- package/core/shared/one-at-a-time.ts +0 -47
- package/core/shared/url-utils.ts +0 -20
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
import ObjectID from 'bson-objectid';
|
|
2
|
-
import errors from '@tryghost/errors';
|
|
3
|
-
import type {Knex} from 'knex';
|
|
4
|
-
import {z} from 'zod';
|
|
5
|
-
import {CustomField} from './models';
|
|
6
|
-
import {FieldTypeSchema} from '@tryghost/custom-field-types';
|
|
7
|
-
import {customFieldCodec} from './codec';
|
|
8
|
-
import {FIELD_STATUS, FieldStatusSchema} from './schema';
|
|
9
|
-
import {activeFields} from './queries';
|
|
10
|
-
import {type RecordCustomFieldAction, type RequestContext} from './actions';
|
|
11
|
-
|
|
12
|
-
// @tryghost/string ships no types; slugify is the same helper tags/labels use.
|
|
13
|
-
const {slugify} = require('@tryghost/string') as {slugify(input: string): string};
|
|
14
|
-
|
|
15
|
-
// The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to
|
|
16
|
-
// our raw-knex query: nql parses the `filter` string to a Mongo query, mongo-knex
|
|
17
|
-
// turns that into parametrised WHERE clauses. Neither needs a Bookshelf model.
|
|
18
|
-
const nql = require('@tryghost/nql') as (filter: string) => {toJSON(): object};
|
|
19
|
-
const knexify = require('@tryghost/mongo-knex') as
|
|
20
|
-
<T extends Knex.QueryBuilder>(qb: T, mongoQuery: object, config: {tableName: string}) => T;
|
|
21
|
-
|
|
22
|
-
const TABLE = 'members_custom_fields';
|
|
23
|
-
|
|
24
|
-
// Column limits come from the canonical schema — the same source the migration and
|
|
25
|
-
// Bookshelf models read — so the service can never drift from the database. The
|
|
26
|
-
// name cap turns an over-long input into a clean 422 (not a DB error), and the key
|
|
27
|
-
// base is capped so even the longest collision suffix stays within the column.
|
|
28
|
-
const columns = require('../../data/schema').tables[TABLE];
|
|
29
|
-
const MAX_NAME_LENGTH: number = columns.name.maxlength;
|
|
30
|
-
const MAX_KEY_LENGTH: number = columns.key.maxlength;
|
|
31
|
-
const MAX_SLUG_ITERATIONS = 1000;
|
|
32
|
-
// Reserve room for a `-<n>` suffix (n up to MAX_SLUG_ITERATIONS).
|
|
33
|
-
const MAX_KEY_BASE_LENGTH = MAX_KEY_LENGTH - (String(MAX_SLUG_ITERATIONS).length + 1);
|
|
34
|
-
|
|
35
|
-
// A key becomes a property name on the plain objects that carry a member's values —
|
|
36
|
-
// on both sides of the wire, since `custom_fields` is JSON and a client gets a plain
|
|
37
|
-
// object from JSON.parse. A key naming a member of Object.prototype reads back as
|
|
38
|
-
// inherited rather than absent wherever one of those objects is indexed, and
|
|
39
|
-
// `__proto__` holds no value at all: the values schema drops it during parse, and
|
|
40
|
-
// assigning it sets a prototype rather than a property.
|
|
41
|
-
//
|
|
42
|
-
// Derived rather than listed, because the set is a consequence of how keys are
|
|
43
|
-
// minted: slugifying lowercases, so only an already-lowercase prototype name can
|
|
44
|
-
// survive to become a key. Currently `constructor` and `__proto__`.
|
|
45
|
-
const RESERVED_KEYS = Object.getOwnPropertyNames(Object.prototype)
|
|
46
|
-
.filter(name => slugify(name) === name);
|
|
47
|
-
|
|
48
|
-
const FieldName = z.string().trim().min(1, {message: 'Custom field name is required.'}).max(MAX_NAME_LENGTH, {message: 'Custom field name is too long.'});
|
|
49
|
-
|
|
50
|
-
// The backend mints the key from the name, so create takes just a name and type.
|
|
51
|
-
const AddFieldInput = z.object({
|
|
52
|
-
name: FieldName,
|
|
53
|
-
type: FieldTypeSchema
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// A bound on the work one request can ask for, separate from how many definitions
|
|
57
|
-
// a site may hold in total. Every definition in a batch costs several queries
|
|
58
|
-
// inside one open write transaction, so an operator raising the site ceiling must
|
|
59
|
-
// not also mean a single request can ask for an unbounded amount of that work.
|
|
60
|
-
const MAX_FIELDS_PER_REQUEST = 100;
|
|
61
|
-
|
|
62
|
-
// Create accepts a batch. The framework guarantees a non-empty array by the time a
|
|
63
|
-
// query runs, but the service validates the whole payload up front so a bad item
|
|
64
|
-
// anywhere fails the request before anything is written.
|
|
65
|
-
const AddFieldsInput = z.array(AddFieldInput)
|
|
66
|
-
.min(1)
|
|
67
|
-
.max(MAX_FIELDS_PER_REQUEST, {message: `Custom fields can only be created ${MAX_FIELDS_PER_REQUEST} at a time.`});
|
|
68
|
-
|
|
69
|
-
// Name and status are mutable. `key` and `type` are accepted so the immutability
|
|
70
|
-
// rules can reject a change loudly; they are never persisted.
|
|
71
|
-
const EditFieldInput = z.object({
|
|
72
|
-
name: FieldName.optional(),
|
|
73
|
-
status: FieldStatusSchema.optional(),
|
|
74
|
-
key: z.string().optional(),
|
|
75
|
-
type: FieldTypeSchema.optional()
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
export class CustomFieldDefinitionsService {
|
|
79
|
-
private knex: Knex;
|
|
80
|
-
private recordAction: RecordCustomFieldAction;
|
|
81
|
-
private getMaxDefinitions: () => number;
|
|
82
|
-
|
|
83
|
-
constructor({knex, recordAction, getMaxDefinitions}: {knex: Knex; recordAction: RecordCustomFieldAction; getMaxDefinitions: () => number}) {
|
|
84
|
-
this.knex = knex;
|
|
85
|
-
this.recordAction = recordAction;
|
|
86
|
-
// A getter, not a value: the ceiling can be raised or lowered at any time,
|
|
87
|
-
// and a Ghost container holds no state across requests, so the limit that
|
|
88
|
-
// applies is whatever it resolves to when the request lands. Asking on
|
|
89
|
-
// every create means a change takes effect on the next one, with no
|
|
90
|
-
// restart. Where the number comes from is the caller's business.
|
|
91
|
-
this.getMaxDefinitions = getMaxDefinitions;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async browse(options: {filter?: string} = {}): Promise<CustomField[]> {
|
|
95
|
-
// Archived fields are hidden by default: most surfaces (member details, the
|
|
96
|
-
// filter picker, the importer) only ever want active fields. A caller-
|
|
97
|
-
// supplied `filter` can widen that — Settings pulls active and archived
|
|
98
|
-
// together in one request (`filter=status:[active,archived]`).
|
|
99
|
-
//
|
|
100
|
-
// Insertion order for now — when the UI needs a persistent user-defined
|
|
101
|
-
// order, add a `sort_order` column and order by it.
|
|
102
|
-
const query = options.filter
|
|
103
|
-
? applyFilter(this.knex(TABLE), options.filter)
|
|
104
|
-
: activeFields(this.knex);
|
|
105
|
-
const rows = await query
|
|
106
|
-
.orderBy('created_at', 'asc')
|
|
107
|
-
.orderBy('id', 'asc')
|
|
108
|
-
.select('*');
|
|
109
|
-
return rows.map(row => z.decode(customFieldCodec, row));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async read(key: string): Promise<CustomField> {
|
|
113
|
-
const row = await this.knex(TABLE).where('key', key).first();
|
|
114
|
-
if (!row) {
|
|
115
|
-
throw new errors.NotFoundError({message: 'Custom field not found.'});
|
|
116
|
-
}
|
|
117
|
-
return z.decode(customFieldCodec, row);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Create one or more field definitions. All-or-nothing: the batch runs in a
|
|
122
|
-
* single transaction, so a name clash or the operational cap on the third item
|
|
123
|
-
* leaves the first two unwritten rather than half-applying the request.
|
|
124
|
-
*
|
|
125
|
-
* Running inside the transaction also makes a batch self-consistent for free —
|
|
126
|
-
* `assertNameAvailable` and `mintKey` see the rows inserted earlier in the same
|
|
127
|
-
* batch, so two items sharing a name are caught and two items sharing a slug
|
|
128
|
-
* get distinct keys, exactly as if they had arrived as separate requests.
|
|
129
|
-
*/
|
|
130
|
-
async add(context: RequestContext, input: unknown): Promise<CustomField[]> {
|
|
131
|
-
const requestedCount = Array.isArray(input) ? input.length : 0;
|
|
132
|
-
|
|
133
|
-
const parsed = AddFieldsInput.safeParse(input);
|
|
134
|
-
if (!parsed.success) {
|
|
135
|
-
const issue = parsed.error.issues[0];
|
|
136
|
-
throw new errors.ValidationError({
|
|
137
|
-
message: issue.message,
|
|
138
|
-
property: propertyOf(issue.path),
|
|
139
|
-
context: batchContext(issue.path[0], requestedCount)
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
const fields = parsed.data;
|
|
143
|
-
|
|
144
|
-
// Slugify before opening the transaction: it needs no database access, and
|
|
145
|
-
// an unusable name is a payload problem worth reporting on its own terms.
|
|
146
|
-
const bases = fields.map((field, index) => {
|
|
147
|
-
const base = slugify(field.name);
|
|
148
|
-
if (!base) {
|
|
149
|
-
throw new errors.ValidationError({
|
|
150
|
-
message: 'Custom field name must contain at least one usable character.',
|
|
151
|
-
property: 'name',
|
|
152
|
-
context: batchContext(index, requestedCount)
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
return base;
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
let created: CustomField[];
|
|
159
|
-
try {
|
|
160
|
-
created = await this.knex.transaction(async (trx) => {
|
|
161
|
-
await this.assertWithinLimit(trx, fields.length);
|
|
162
|
-
|
|
163
|
-
const keys: string[] = [];
|
|
164
|
-
for (const [index, field] of fields.entries()) {
|
|
165
|
-
await this.assertNameAvailable(trx, field.name);
|
|
166
|
-
const key = await this.mintKey(trx, bases[index]);
|
|
167
|
-
await trx(TABLE).insert({id: new ObjectID().toHexString(), key, name: field.name, type: field.type, created_at: new Date()});
|
|
168
|
-
keys.push(key);
|
|
169
|
-
}
|
|
170
|
-
return this.readMany(trx, keys);
|
|
171
|
-
});
|
|
172
|
-
} catch (err) {
|
|
173
|
-
// mintKey already picked a free key, so a unique violation here only
|
|
174
|
-
// means a concurrent create claimed the same key in between. The index
|
|
175
|
-
// is the final arbiter; surface a retryable conflict rather than a 500.
|
|
176
|
-
if (isUniqueConstraintViolation(err)) {
|
|
177
|
-
throw new errors.ConflictError({message: 'Could not create the custom field, please try again.'});
|
|
178
|
-
}
|
|
179
|
-
throw err;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Logged after the commit: the action log is a separate Bookshelf write
|
|
183
|
-
// outside this transaction, so recording inside it would leave orphaned
|
|
184
|
-
// "added" entries for fields a rollback never created.
|
|
185
|
-
for (const field of created) {
|
|
186
|
-
await this.recordAction({context, verb: 'create', subject: field.id, details: {primary_name: field.name, key: field.key}});
|
|
187
|
-
}
|
|
188
|
-
return created;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* The operational ceiling on how many definitions a site can hold. This is a
|
|
193
|
-
* safeguard against the database load unbounded definitions would create, not
|
|
194
|
-
* a pricing or packaging limit, so it applies wherever the feature is available
|
|
195
|
-
* and is deliberately not routed through the entitlement-driven limit service.
|
|
196
|
-
*
|
|
197
|
-
* Both active and archived definitions count: an archived field still occupies
|
|
198
|
-
* a row and still carries its members' values, so archiving alone frees no
|
|
199
|
-
* space. Deleting an archived field is what releases a slot.
|
|
200
|
-
*
|
|
201
|
-
* The count is a consistent read, not a locking one, so two creates landing at
|
|
202
|
-
* the same instant can both pass and take a site one over. That is deliberate:
|
|
203
|
-
* this is a ceiling on database load, and holding a table lock across every
|
|
204
|
-
* create to make it exact would cost more than the overshoot it prevents.
|
|
205
|
-
*
|
|
206
|
-
*/
|
|
207
|
-
private async assertWithinLimit(db: Knex, addedCount: number): Promise<void> {
|
|
208
|
-
const max = this.getMaxDefinitions();
|
|
209
|
-
|
|
210
|
-
const row = await db(TABLE).count({count: '*'}).first();
|
|
211
|
-
const total = Number(row?.count ?? 0);
|
|
212
|
-
if (total + addedCount <= max) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Two different situations reach here and they need different advice. At or
|
|
217
|
-
// over the ceiling there is nothing to do but free a slot. With slots still
|
|
218
|
-
// free the request was simply too big, and telling that operator to delete
|
|
219
|
-
// something is wrong: they have room, just not this much.
|
|
220
|
-
const remaining = max - total;
|
|
221
|
-
const advice = remaining > 0
|
|
222
|
-
? `You can add ${remaining} more.`
|
|
223
|
-
: 'Delete a field you no longer need to make room.';
|
|
224
|
-
|
|
225
|
-
throw new errors.HostLimitError({
|
|
226
|
-
message: `Custom fields are limited to ${max} per site. ${advice}`,
|
|
227
|
-
code: 'CUSTOM_FIELDS_LIMIT_REACHED',
|
|
228
|
-
// `requested` is carried alongside the limit-service shape so a batch
|
|
229
|
-
// rejection is self-describing: without it a client sees free slots and
|
|
230
|
-
// a refusal, and has to re-derive its own payload size to explain why.
|
|
231
|
-
errorDetails: {limit: max, total, requested: addedCount}
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/** Read back a batch in the order its keys were created, not the table's order. */
|
|
236
|
-
private async readMany(db: Knex, keys: string[]): Promise<CustomField[]> {
|
|
237
|
-
const rows = await db(TABLE).whereIn('key', keys).select('*');
|
|
238
|
-
const byKey = new Map(rows.map(row => [row.key, row]));
|
|
239
|
-
return keys.map(key => z.decode(customFieldCodec, byKey.get(key)!));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Pick a free key from the name's slug: `base`, then `base-2`, `base-3`, ...
|
|
244
|
-
* Reads the keys already taken by that base — including archived fields, so a
|
|
245
|
-
* slug is never reused once minted. Mirrors how tags/labels generate slugs.
|
|
246
|
-
*/
|
|
247
|
-
private async mintKey(db: Knex, base: string): Promise<string> {
|
|
248
|
-
const safeBase = base.slice(0, MAX_KEY_BASE_LENGTH);
|
|
249
|
-
const taken = new Set([
|
|
250
|
-
...RESERVED_KEYS,
|
|
251
|
-
...await db(TABLE).where('key', 'like', `${safeBase}%`).pluck('key')
|
|
252
|
-
]);
|
|
253
|
-
if (!taken.has(safeBase)) {
|
|
254
|
-
return safeBase;
|
|
255
|
-
}
|
|
256
|
-
for (let suffix = 2; suffix <= MAX_SLUG_ITERATIONS; suffix += 1) {
|
|
257
|
-
const candidate = `${safeBase}-${suffix}`;
|
|
258
|
-
if (!taken.has(candidate)) {
|
|
259
|
-
return candidate;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
throw new errors.ValidationError({message: 'Could not mint a unique key for this custom field.', property: 'name'});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Names are globally unique (across active and archived) so the label is
|
|
267
|
-
* never ambiguous — a unique index on `name` enforces it. This read gives a
|
|
268
|
-
* clean `property: name` 422 for the common case (the raw index violation
|
|
269
|
-
* can't be told apart from a key clash); the index is the race backstop.
|
|
270
|
-
*
|
|
271
|
-
* The LOWER() normalises case in application code because the engines
|
|
272
|
-
* disagree: MySQL's default collation is case-insensitive, SQLite's is not,
|
|
273
|
-
* so relying on the index alone would reject "Company"/"company" in prod but
|
|
274
|
-
* allow it in the SQLite test/dev suites. `exceptKey` lets a field keep its
|
|
275
|
-
* own name on an unrelated edit.
|
|
276
|
-
*/
|
|
277
|
-
private async assertNameAvailable(db: Knex, name: string, exceptKey?: string): Promise<void> {
|
|
278
|
-
const query = db(TABLE).whereRaw('LOWER(name) = ?', [name.toLowerCase()]);
|
|
279
|
-
if (exceptKey) {
|
|
280
|
-
query.whereNot('key', exceptKey);
|
|
281
|
-
}
|
|
282
|
-
const clash = await query.first();
|
|
283
|
-
if (clash) {
|
|
284
|
-
throw new errors.ValidationError({message: 'A custom field with this name already exists.', property: 'name'});
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async edit(context: RequestContext, key: string, input: unknown): Promise<CustomField> {
|
|
289
|
-
const parsed = EditFieldInput.safeParse(input);
|
|
290
|
-
if (!parsed.success) {
|
|
291
|
-
throw new errors.ValidationError({message: parsed.error.issues[0].message, property: parsed.error.issues[0].path[0]?.toString()});
|
|
292
|
-
}
|
|
293
|
-
const patch = parsed.data;
|
|
294
|
-
|
|
295
|
-
const existing = await this.read(key);
|
|
296
|
-
|
|
297
|
-
// Key and type are immutable after creation: values are addressed by key
|
|
298
|
-
// and interpreted by type, so changing either would silently orphan or
|
|
299
|
-
// corrupt stored values. Reject a differing value rather than ignore it.
|
|
300
|
-
if (patch.key !== undefined && patch.key !== existing.key) {
|
|
301
|
-
throw new errors.ValidationError({message: 'Custom field keys cannot be changed once created.', property: 'key'});
|
|
302
|
-
}
|
|
303
|
-
if (patch.type !== undefined && patch.type !== existing.type) {
|
|
304
|
-
throw new errors.ValidationError({message: 'Custom field types cannot be changed once created.', property: 'type'});
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Only write (and log a rename) when the name actually changes, so
|
|
308
|
-
// re-saving an unchanged field is a no-op rather than a spurious edit.
|
|
309
|
-
if (patch.name !== undefined && patch.name !== existing.name) {
|
|
310
|
-
await this.assertNameAvailable(this.knex, patch.name, key);
|
|
311
|
-
try {
|
|
312
|
-
await this.knex(TABLE)
|
|
313
|
-
.where('key', key)
|
|
314
|
-
.update({name: patch.name, updated_at: new Date()});
|
|
315
|
-
} catch (err) {
|
|
316
|
-
if (isUniqueConstraintViolation(err)) {
|
|
317
|
-
throw new errors.ConflictError({message: 'Could not rename the custom field, please try again.'});
|
|
318
|
-
}
|
|
319
|
-
throw err;
|
|
320
|
-
}
|
|
321
|
-
await this.recordAction({context, verb: 'rename', subject: existing.id, details: {primary_name: patch.name, key, previous_name: existing.name}});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// A status change is the archive/restore transition. Only write (and log)
|
|
325
|
-
// when it actually flips, so re-sending the current status is a no-op.
|
|
326
|
-
if (patch.status !== undefined && patch.status !== existing.status) {
|
|
327
|
-
await this.knex(TABLE)
|
|
328
|
-
.where('key', key)
|
|
329
|
-
.update({status: patch.status, updated_at: new Date()});
|
|
330
|
-
const verb = patch.status === FIELD_STATUS.archived ? 'archive' : 'restore';
|
|
331
|
-
await this.recordAction({context, verb, subject: existing.id, details: {primary_name: patch.name ?? existing.name, key}});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return this.read(key);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Permanently delete a field and every member value attached to it (the FK
|
|
339
|
-
* cascades the values). Destructive and irreversible, so it's gated on the
|
|
340
|
-
* field already being archived: a publisher must archive first, then delete,
|
|
341
|
-
* which makes accidental data loss a deliberate two-step. Archiving is the
|
|
342
|
-
* reversible soft state (see `edit` with a status change).
|
|
343
|
-
*/
|
|
344
|
-
async destroy(context: RequestContext, key: string): Promise<void> {
|
|
345
|
-
const field = await this.knex(TABLE).where('key', key).first();
|
|
346
|
-
if (!field) {
|
|
347
|
-
throw new errors.NotFoundError({message: 'Custom field not found.'});
|
|
348
|
-
}
|
|
349
|
-
if (field.status !== FIELD_STATUS.archived) {
|
|
350
|
-
throw new errors.ValidationError({message: 'Only archived custom fields can be deleted. Archive the field first.'});
|
|
351
|
-
}
|
|
352
|
-
await this.knex(TABLE).where('key', key).del();
|
|
353
|
-
await this.recordAction({context, verb: 'delete', subject: field.id, details: {primary_name: field.name, key}});
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// The field a zod issue points at. Create validates an array, so an issue's path
|
|
358
|
-
// is prefixed with the item's index (`[0, 'name']`); `property` names the field
|
|
359
|
-
// that is wrong, so the numeric prefix is dropped. Which item it was is reported
|
|
360
|
-
// separately by batchContext, keeping `property` the bare field name a client can
|
|
361
|
-
// map straight onto its form input.
|
|
362
|
-
function propertyOf(path: PropertyKey[]): string | undefined {
|
|
363
|
-
return path.find(segment => typeof segment === 'string');
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Which definition of a batch an error belongs to. Only set when the request
|
|
367
|
-
// carried more than one: a lone definition needs no pointer, and every client
|
|
368
|
-
// today sends exactly one, so this stays absent on the common path.
|
|
369
|
-
function batchContext(index: PropertyKey | undefined, requestedCount: number): string | undefined {
|
|
370
|
-
if (requestedCount <= 1 || typeof index !== 'number') {
|
|
371
|
-
return undefined;
|
|
372
|
-
}
|
|
373
|
-
return `Custom field ${index + 1} of ${requestedCount}.`;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function isUniqueConstraintViolation(error: unknown): boolean {
|
|
377
|
-
const code = (error as {code?: string})?.code;
|
|
378
|
-
return code === 'ER_DUP_ENTRY' || code === 'SQLITE_CONSTRAINT';
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Apply a caller-supplied NQL filter to the definition query. A malformed filter
|
|
382
|
-
// is a client error (400), not a 500. The active-only default is preserved unless
|
|
383
|
-
// the filter itself constrains status, so a filter on another field (e.g. type)
|
|
384
|
-
// can never surface archived fields — this is the invariant queries.ts centralises,
|
|
385
|
-
// held here as the per-field default override Bookshelf's filter plugin does.
|
|
386
|
-
function applyFilter<T extends Knex.QueryBuilder>(query: T, filter: string): T {
|
|
387
|
-
let mongoQuery: Record<string, unknown>;
|
|
388
|
-
try {
|
|
389
|
-
mongoQuery = nql(filter).toJSON() as Record<string, unknown>;
|
|
390
|
-
knexify(query, mongoQuery, {tableName: TABLE});
|
|
391
|
-
} catch (err) {
|
|
392
|
-
throw new errors.BadRequestError({message: 'Could not parse the filter parameter.', property: 'filter', err: err as Error});
|
|
393
|
-
}
|
|
394
|
-
if (!filterReferencesStatus(mongoQuery)) {
|
|
395
|
-
query.where('status', FIELD_STATUS.active);
|
|
396
|
-
}
|
|
397
|
-
return query;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Whether an NQL-parsed filter constrains `status` anywhere, including inside the
|
|
401
|
-
// $and/$or/$nor combinators — so a status filter at any nesting counts as the
|
|
402
|
-
// caller opting in to (or out of) archived fields.
|
|
403
|
-
function filterReferencesStatus(query: Record<string, unknown>): boolean {
|
|
404
|
-
return Object.entries(query).some(([key, value]) => {
|
|
405
|
-
if (key === 'status') {
|
|
406
|
-
return true;
|
|
407
|
-
}
|
|
408
|
-
if ((key === '$and' || key === '$or' || key === '$nor') && Array.isArray(value)) {
|
|
409
|
-
return value.some(sub => filterReferencesStatus(sub as Record<string, unknown>));
|
|
410
|
-
}
|
|
411
|
-
return false;
|
|
412
|
-
});
|
|
413
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import {CustomFieldDefinitionsService} from './definitions-service';
|
|
2
|
-
import {CustomFieldValuesService} from './values-service';
|
|
3
|
-
import {recordCustomFieldAction, type RecordCustomFieldAction} from './actions';
|
|
4
|
-
import {resolveMaxDefinitions} from './config';
|
|
5
|
-
|
|
6
|
-
export type {CustomField} from './models';
|
|
7
|
-
export type {RequestContext} from './actions';
|
|
8
|
-
|
|
9
|
-
// Two services from one module, split along an aggregate boundary rather than a
|
|
10
|
-
// technical layer: `definitions` owns the field definitions, which belong to the
|
|
11
|
-
// site's settings, and `values` owns the per-member values, which belong to the
|
|
12
|
-
// member. The values service reads the definitions table directly for the
|
|
13
|
-
// reference data it needs — a value referencing its definition, not a boundary
|
|
14
|
-
// crossing.
|
|
15
|
-
//
|
|
16
|
-
// Constructed by init() at boot, not at import: knex is only available once the DB has connected.
|
|
17
|
-
export let definitions: CustomFieldDefinitionsService | undefined;
|
|
18
|
-
export let values: CustomFieldValuesService | undefined;
|
|
19
|
-
|
|
20
|
-
export function init(): void {
|
|
21
|
-
// The two are constructed together below, so checking both keeps the "both or
|
|
22
|
-
// neither" invariant explicit rather than trusting one to stand in for the pair.
|
|
23
|
-
if (definitions && values) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const {knex} = require('../../data/db');
|
|
28
|
-
const models = require('../../models');
|
|
29
|
-
|
|
30
|
-
const recordAction: RecordCustomFieldAction = ({context, verb, subject, details}) =>
|
|
31
|
-
recordCustomFieldAction({Action: models.Action, context, verb, subject, details});
|
|
32
|
-
|
|
33
|
-
// Resolved here, not in the service: reading config is this module's job, and
|
|
34
|
-
// the service is handed a number. A getter rather than a value because the
|
|
35
|
-
// ceiling is an operator setting that can change between requests, and a Ghost
|
|
36
|
-
// container holds no state across them.
|
|
37
|
-
const config = require('../../../shared/config');
|
|
38
|
-
|
|
39
|
-
definitions = new CustomFieldDefinitionsService({
|
|
40
|
-
knex,
|
|
41
|
-
recordAction,
|
|
42
|
-
getMaxDefinitions: () => resolveMaxDefinitions(config.get('members:customFields:maxDefinitions'))
|
|
43
|
-
});
|
|
44
|
-
// The values service reads the field definitions straight from the table, so
|
|
45
|
-
// it needs knex and the same ceiling — no handle on the definitions service.
|
|
46
|
-
values = new CustomFieldValuesService({
|
|
47
|
-
knex,
|
|
48
|
-
getMaxDefinitions: () => resolveMaxDefinitions(config.get('members:customFields:maxDefinitions'))
|
|
49
|
-
});
|
|
50
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {z} from 'zod';
|
|
2
|
-
import {FieldTypeSchema} from '@tryghost/custom-field-types';
|
|
3
|
-
import {FieldStatusSchema} from './schema';
|
|
4
|
-
|
|
5
|
-
// The domain shape of a field definition (camelCase; distinct from the DB row).
|
|
6
|
-
export const CustomField = z.object({
|
|
7
|
-
id: z.string(),
|
|
8
|
-
key: z.string(),
|
|
9
|
-
name: z.string(),
|
|
10
|
-
type: FieldTypeSchema,
|
|
11
|
-
status: FieldStatusSchema,
|
|
12
|
-
createdAt: z.date(),
|
|
13
|
-
updatedAt: z.date().nullable()
|
|
14
|
-
});
|
|
15
|
-
export type CustomField = z.infer<typeof CustomField>;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type {Knex} from 'knex';
|
|
2
|
-
import {FIELD_STATUS} from './schema';
|
|
3
|
-
|
|
4
|
-
const FIELDS_TABLE = 'members_custom_fields';
|
|
5
|
-
|
|
6
|
-
// Archived fields must stay out of every read and write, and nothing in the database
|
|
7
|
-
// enforces that — no constraint stops a value row referencing an archived field. A query
|
|
8
|
-
// that forgets the filter is a silent bug, so the filter lives in one place.
|
|
9
|
-
|
|
10
|
-
/** Takes the executor so the same query runs standalone or inside a write's transaction. */
|
|
11
|
-
export function activeFields(db: Knex) {
|
|
12
|
-
return db(FIELDS_TABLE).where('status', FIELD_STATUS.active);
|
|
13
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import {z} from 'zod';
|
|
2
|
-
import type {Knex} from 'knex';
|
|
3
|
-
import {FieldTypeSchema} from '@tryghost/custom-field-types';
|
|
4
|
-
import {DbDate} from '../../lib/db-date';
|
|
5
|
-
|
|
6
|
-
// `archived` is soft: the field drops out of the values path but stays in the definition
|
|
7
|
-
// list so it can be renamed, restored or deleted. Mirrors schema.js's `isIn` on the
|
|
8
|
-
// column, which is static config and cannot import this.
|
|
9
|
-
export const FIELD_STATUS = {active: 'active', archived: 'archived'} as const;
|
|
10
|
-
export type FieldStatus = typeof FIELD_STATUS[keyof typeof FIELD_STATUS];
|
|
11
|
-
export const FieldStatusSchema = z.enum([FIELD_STATUS.active, FIELD_STATUS.archived]);
|
|
12
|
-
|
|
13
|
-
// The single source for the read projection and the knex table type below. `type` parses
|
|
14
|
-
// as the field-type enum, so the row carries the narrow type and no codec needs a cast.
|
|
15
|
-
export const DbCustomField = z.object({
|
|
16
|
-
id: z.string(),
|
|
17
|
-
key: z.string(),
|
|
18
|
-
name: z.string(),
|
|
19
|
-
type: FieldTypeSchema,
|
|
20
|
-
status: FieldStatusSchema,
|
|
21
|
-
created_at: DbDate,
|
|
22
|
-
updated_at: DbDate.nullable()
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
type CustomFieldRow = z.infer<typeof DbCustomField>;
|
|
26
|
-
|
|
27
|
-
// One part of a member's value. What a `path` means is storage.ts's business, so the row
|
|
28
|
-
// carries it as a plain string.
|
|
29
|
-
export const DbCustomFieldValue = z.object({
|
|
30
|
-
id: z.string(),
|
|
31
|
-
custom_field_id: z.string(),
|
|
32
|
-
member_id: z.string(),
|
|
33
|
-
path: z.string(),
|
|
34
|
-
// Nullable like the column, though nothing here writes a null: a part with no value
|
|
35
|
-
// has no row.
|
|
36
|
-
value_text: z.string().nullable(),
|
|
37
|
-
created_at: DbDate,
|
|
38
|
-
updated_at: DbDate.nullable()
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
type CustomFieldValueRow = z.infer<typeof DbCustomFieldValue>;
|
|
42
|
-
|
|
43
|
-
// The field's key travels with the row so a value assembles without a second lookup.
|
|
44
|
-
//
|
|
45
|
-
// `type` takes no part in the assembly and is here as a gate: a value whose type has left
|
|
46
|
-
// the catalog is one the definitions list no longer returns either, so failing to parse
|
|
47
|
-
// is what drops it.
|
|
48
|
-
export const DbCustomFieldLeaf = z.object({
|
|
49
|
-
member_id: z.string(),
|
|
50
|
-
key: z.string(),
|
|
51
|
-
type: FieldTypeSchema,
|
|
52
|
-
path: z.string(),
|
|
53
|
-
value_text: z.string()
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
declare module 'knex/types/tables' {
|
|
57
|
-
interface Tables {
|
|
58
|
-
members_custom_fields: Knex.CompositeTableType<
|
|
59
|
-
CustomFieldRow,
|
|
60
|
-
// `status` is DB-defaulted to 'active' on create and only ever set via
|
|
61
|
-
// update (archive/restore), so it's absent from the insert type.
|
|
62
|
-
Omit<z.input<typeof DbCustomField>, 'updated_at' | 'status'>,
|
|
63
|
-
Partial<CustomFieldRow>
|
|
64
|
-
>;
|
|
65
|
-
members_custom_field_values: Knex.CompositeTableType<
|
|
66
|
-
CustomFieldValueRow,
|
|
67
|
-
Omit<z.input<typeof DbCustomFieldValue>, 'updated_at'>,
|
|
68
|
-
Partial<CustomFieldValueRow>
|
|
69
|
-
>;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import {z} from 'zod';
|
|
2
|
-
import {snakeKeys} from '../../lib/case-keys';
|
|
3
|
-
import {CustomField} from './models';
|
|
4
|
-
|
|
5
|
-
// Response schema — the field-definition shape the admin endpoints emit. `id` is
|
|
6
|
-
// the DB primary key and deliberately never leaves the API; a field is addressed
|
|
7
|
-
// by its immutable `key`. The `.pipe` below strips id from the mapped output.
|
|
8
|
-
const CustomFieldResource = z.object({
|
|
9
|
-
key: z.string(),
|
|
10
|
-
name: z.string(),
|
|
11
|
-
type: z.string(),
|
|
12
|
-
status: z.string(),
|
|
13
|
-
created_at: z.date(),
|
|
14
|
-
updated_at: z.date().nullable()
|
|
15
|
-
});
|
|
16
|
-
const CustomFieldsResponse = z.object({members_custom_fields: z.array(CustomFieldResource)});
|
|
17
|
-
|
|
18
|
-
export const toCustomFieldsResponse = z.array(CustomField)
|
|
19
|
-
.transform((fields): z.input<typeof CustomFieldsResponse> => ({
|
|
20
|
-
members_custom_fields: fields.map(field => snakeKeys(field))
|
|
21
|
-
}))
|
|
22
|
-
.pipe(CustomFieldsResponse);
|