ghost 6.56.0 → 6.57.1
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/validate-adapters.js +74 -0
- package/bin/validate-adapters.ts +87 -0
- package/bin/warm-compile-cache.sh +60 -0
- package/components/tryghost-adapter-base-scheduling-0.2.1.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.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-parse-email-address-0.0.0.tgz +0 -0
- package/core/app.js +1 -1
- package/core/boot.js +21 -25
- package/core/bridge.js +10 -12
- package/core/built/admin/assets/Combination-BDvCdg1t.js +41 -0
- package/core/built/admin/assets/about-BNojRaxf.js +1 -0
- package/core/built/admin/assets/{account-migration-D3O_yVck.js → account-migration-DMS8JsM-.js} +1 -1
- package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-D8FbP-Z-.js → account-migration-DHFbePl6.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +3 -3
- package/core/built/admin/assets/activitypub/{avatar-11EA1VSs.js → avatar-CT4tS2_S.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-jfbLG-xe.js → avatar-flipboard-ByYWKipo.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-f1jTQe2P.js → bluesky-sharing-srVnSHDz.js} +8 -8
- package/core/built/admin/assets/activitypub/{content-formatters-nmTOPY9M.js → content-formatters-C2sos5Ht.js} +3 -3
- package/core/built/admin/assets/activitypub/{copy-BCwo9ep3.js → copy-B1UBTOoZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-BEfX-0aW.js → dist-MPsb2WZ1.js} +4 -4
- package/core/built/admin/assets/activitypub/{domain-AaDlZlrA.js → domain-DzcM9Dzo.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-DPi8Tl5M.js → edit-profile-CsQ_4xAk.js} +5 -5
- package/core/built/admin/assets/activitypub/{explore-CxTzjILQ.js → explore-CJe0DZBK.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-DPv5x2MX.js → feed-CKV3qWON.js} +9 -9
- package/core/built/admin/assets/activitypub/{field-DbgA8lat.js → field-DckHfDJf.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-BWuZoYkA.js → hash-Dlp8RM55.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-35VDQ2K2.js → inbox-CsPy4R0M.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DSrF_i1O.js → inbox-DAsjzUUe.js} +10 -10
- package/core/built/admin/assets/activitypub/{label-CPjFBbo7.js → label-DWyo9E5T.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-BkDC1XVZ.js → moderation-B0Inc03M.js} +7 -7
- package/core/built/admin/assets/activitypub/{note-xEyH4xT-.js → note-CSE54lER.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-DClaL3ui.js → notifications-RSwbKqdh.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-DpnOa-4Z.js → preferences-CE231n2_.js} +11 -11
- package/core/built/admin/assets/activitypub/{profile-Dvi9mz-6.js → profile-aaQ1j1Eo.js} +13 -13
- package/core/built/admin/assets/activitypub/{reply-Cs9ySd6Z.js → reply-Cjb0Vs3m.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-DFBeTnfn.js → routes-Djb0mc9k.js} +26 -26
- package/core/built/admin/assets/activitypub/{separator-C5u_VjzY.js → separator-D8pROI5n.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-CV2fFu6D.js → settings-DMc32IQl.js} +5 -5
- package/core/built/admin/assets/activitypub/{site-_TgDd_EM.js → site-DVIcsohX.js} +4 -4
- package/core/built/admin/assets/activitypub/{step-1-BfnNL2oF.js → step-1-9Yey9usF.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-DLndZkO-.js → step-2-DdYmKRXV.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-A-PZTtRE.js → step-3-hdSkcjZU.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-BUbz7tea.js → tabs-CUqseYzQ.js} +6 -6
- package/core/built/admin/assets/activitypub/{topic-filter-BdX_xYYT.js → topic-filter-Coni6OXM.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-EwuRemHh.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-cy4ACBc4.js → use-navigate-with-base-path-B046AM38.js} +24 -7
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-CyFcGfan.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-7zk8mV2p.js → x-BRfRYd16.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-DU9ghGLS.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-DFxuQrYu.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-D-3LOrdd.js +1 -0
- package/core/built/admin/assets/analytics-DTIMRhWw.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-DGCcezB6.js +1 -0
- package/core/built/admin/assets/api-keys-Dx4NeNpK.js +1 -0
- package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
- package/core/built/admin/assets/{audience-CkBb2LlO.js → audience-DkknNqhZ.js} +1 -1
- package/core/built/admin/assets/automated-emails-PayPCT8L.js +1 -0
- package/core/built/admin/assets/{automations-DhKeS_U5.js → automations-162q5HjL.js} +1 -1
- package/core/built/admin/assets/{automations-cMllU545.js → automations-DvSU7QhW.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-DPGpyD-G.js → bluesky-sharing-yZ9A_jL8.js} +1 -1
- package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
- package/core/built/admin/assets/{breadcrumb-DIfcF5ow.js → breadcrumb-CjOH7LCU.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-DV0FkdcD.js → chart-helpers-DGxvWnBT.js} +1 -1
- package/core/built/admin/assets/checkbox-BSUcnNq_.js +1 -0
- package/core/built/admin/assets/{chunk.332.05991a101d67f732dec3.js → chunk.424.4a27628efdade15c2dab.js} +28 -27
- package/core/built/admin/assets/chunk.524.dce1c51e0bc30fb2718e.js +25 -0
- package/core/built/admin/assets/{chunk.211.6f7328d63c41536451b3.js → chunk.571.40f093ed6d883993714e.js} +5 -5
- package/core/built/admin/assets/chunk.582.3a430d5c6a744fd475b6.js +27 -0
- package/core/built/admin/assets/code-editor-view-BcyKABrG.js +1 -0
- package/core/built/admin/assets/color-picker-field-8NoSnhNI.js +1 -0
- package/core/built/admin/assets/color-utils-Dzeeuj1F.js +1 -0
- package/core/built/admin/assets/combobox-5rdgDrDV.js +1 -0
- package/core/built/admin/assets/command-BOYMbZa_.js +1 -0
- package/core/built/admin/assets/comments-BhNM5CYl.js +1 -0
- package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
- package/core/built/admin/assets/confirmation-modal-CYD7x3UQ.js +1 -0
- package/core/built/admin/assets/content-api-modal-BtW4Z1-1.js +1 -0
- package/core/built/admin/assets/copy-field-CEp_XieW.js +1 -0
- package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-bk3lNEuQ.js +1 -0
- package/core/built/admin/assets/{date-range-select-dXc-GsVe.js → date-range-select-B3Ij1Rqg.js} +1 -1
- package/core/built/admin/assets/debounce-Cx5B_9OI.js +3 -0
- package/core/built/admin/assets/design-and-theme-modal-B2xYKS1I.js +23 -0
- package/core/built/admin/assets/dirty-confirm-dialog-YE9L21gY.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-uIzbWL7K.js +1 -0
- package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-ARN6XTcR.js} +1 -1
- package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
- package/core/built/admin/assets/{dist-DyViJwIy.js → dist-Bg8bSvLA.js} +3 -3
- package/core/built/admin/assets/dist-CsvXCJSe.js +23 -0
- package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
- package/core/built/admin/assets/dist-j1xxxnY8.js +1 -0
- package/core/built/admin/assets/dist-myQaY0ka.js +1 -0
- package/core/built/admin/assets/{domain-B5ogXUBa.js → domain-DzrOAPY8.js} +1 -1
- package/core/built/admin/assets/{dropzone-BnFG7dDP.js → dropzone-CzhP33wa.js} +1 -1
- package/core/built/admin/assets/{edit-profile-B07jCZy9.js → edit-profile-oufs4R4n.js} +1 -1
- package/core/built/admin/assets/editor-BBKrunMK.js +8 -0
- package/core/built/admin/assets/embed-signup-form-modal-Ch3s6lqA.js +9 -0
- package/core/built/admin/assets/{explore-Ca1y8HW_.js → explore-CA-R01Br.js} +1 -1
- package/core/built/admin/assets/{feed-x4iFMBP4.js → feed-ClC8_Gld.js} +1 -1
- package/core/built/admin/assets/{field-ChCAabZg.js → field-B3OPenbP.js} +1 -1
- package/core/built/admin/assets/{filter-sources-LdtH1e-T.js → filter-sources-CiPLnG9A.js} +1 -1
- package/core/built/admin/assets/filters-Bz0HsRie.js +1 -0
- package/core/built/admin/assets/{filters-CjVKRP8U.js → filters-yFvybRMe.js} +1 -1
- package/core/built/admin/assets/first-promoter-modal-BC0UpYCD.js +1 -0
- package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
- package/core/built/admin/assets/{ghost-7f01131115186d1af84ead76e79646dd.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +300 -298
- package/core/built/admin/assets/{gift-link-modal-Buqh2vXh.js → gift-link-modal-BKRuqo-k.js} +1 -1
- package/core/built/admin/assets/{gift-links-CsUhefWG.js → gift-links-CVuqLhsZ.js} +1 -1
- package/core/built/admin/assets/global-data-provider-RzW6Bnlf.js +1 -0
- package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
- package/core/built/admin/assets/{growth-DajOBdN0.js → growth-ClNAldfL.js} +1 -1
- package/core/built/admin/assets/{growth-Dy2UOSay.js → growth-Dz8OSG06.js} +2 -2
- package/core/built/admin/assets/history-modal-DIVuQ5Ct.js +1 -0
- package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
- package/core/built/admin/assets/html-field-CVZ2sMxw.js +1 -0
- package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
- package/core/built/admin/assets/images-Ok8FFJ3A.js +1 -0
- package/core/built/admin/assets/{inbox-BUOLDoji.js → inbox-CMUVI6qb.js} +13 -13
- package/core/built/admin/assets/index-DUXM0NlS.js +67 -0
- package/core/built/admin/assets/index-Ds2iUoF6.css +1 -0
- package/core/built/admin/assets/index.es-B9tvf-t4.js +1 -0
- package/core/built/admin/assets/{input-group-uSK3XT7b.js → input-group-2XU-iuh_.js} +1 -1
- package/core/built/admin/assets/integration-header-B4DyXV06.js +1 -0
- package/core/built/admin/assets/integrations-BT6_iJvu.js +1 -0
- package/core/built/admin/assets/invite-user-modal-DrZ7xxrZ.js +1 -0
- package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
- package/core/built/admin/assets/koenig-editor-base-BPj4S760.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9254 -9251
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
- package/core/built/admin/assets/{koenig-lexical-BFc4yFed.js → koenig-lexical-Cdjv-m3_.js} +98 -98
- package/core/built/admin/assets/{kpi-card-CCt-qeWD.js → kpi-card-J8Mj1sDX.js} +1 -1
- package/core/built/admin/assets/{kpi-card-BN-E6WYX.js → kpi-card-awos0BMH.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-DlxTbTs4.js → kpi-helpers-SEhvH-8E.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BlcUttpn.js → kpi-tabs-R6BjsKhd.js} +1 -1
- package/core/built/admin/assets/{label-AVrxwmsz.js → label-BMC3dzLI.js} +1 -1
- package/core/built/admin/assets/label-picker-Bnlru5tO.js +1 -0
- package/core/built/admin/assets/{labels-DAIqKed7.js → labels-DAPjKPy2.js} +1 -1
- package/core/built/admin/assets/layout-yR9rlvEw.js +1 -0
- package/core/built/admin/assets/limit-modal-COmLevuY.js +1 -0
- package/core/built/admin/assets/{link-helpers-D9LsDOkG.js → link-helpers-Bzkk68GD.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-BvFKEpxJ.js +1 -0
- package/core/built/admin/assets/member-detail-CWdPAe3p.js +1 -0
- package/core/built/admin/assets/members-Bnt0ih2R.js +11 -0
- package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
- package/core/built/admin/assets/{moderation-CQ3urTFE.js → moderation-mPjYHgZW.js} +1 -1
- package/core/built/admin/assets/multi-select-combobox-CsoyHAiX.js +1 -0
- package/core/built/admin/assets/navigation-modal-D_c0badj.js +1 -0
- package/core/built/admin/assets/newsletter-DR49NyGS.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-B45pL1pb.js +1 -0
- package/core/built/admin/assets/newsletter-emails-B-OLfrvr.js +1 -0
- package/core/built/admin/assets/{newsletters-vt3Ni1Ov.js → newsletters-DGs5BaKH.js} +1 -1
- package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
- package/core/built/admin/assets/{note-Dk877BTv.js → note-CSVsUKBm.js} +1 -1
- package/core/built/admin/assets/{notifications-CPlLjPxO.js → notifications-DV9FRtDw.js} +1 -1
- package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-B0hFS8nk.js +1 -0
- package/core/built/admin/assets/{onboarding-route-DdStOtjy.js → onboarding-route--jL5KLPD.js} +1 -1
- package/core/built/admin/assets/overview-B-ZPNqMo.js +1 -0
- package/core/built/admin/assets/overview-D96iwoJi.js +1 -0
- package/core/built/admin/assets/{page-header-pp4dWmOS.js → page-header-DAvDYKO9.js} +1 -1
- package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
- package/core/built/admin/assets/pintura-modal-jiUCxX0Q.js +1 -0
- package/core/built/admin/assets/portal-frame-D4D4nEO6.js +1 -0
- package/core/built/admin/assets/portal-modal-BLFdYeDH.js +1 -0
- package/core/built/admin/assets/{post-analytics-BMJrI81R.js → post-analytics-BhAtiFJp.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-DK27VjJm.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-Btml5vJI.js → post-analytics-provider-DGg0FUqs.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-AcUZkbfL.js → post-share-modal-DtFAC132.js} +1 -1
- package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
- package/core/built/admin/assets/preferences-CuWLISHa.js +5 -0
- package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
- package/core/built/admin/assets/preview-modal-DctYx20m.js +1 -0
- package/core/built/admin/assets/profile-BsKYYtuL.js +1 -0
- package/core/built/admin/assets/radio-group-CI5_Kf_d.js +1 -0
- package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
- package/core/built/admin/assets/{router-provider-BmEZf6c6.js → router-provider-DSQfprxH.js} +1 -1
- package/core/built/admin/assets/select-CkXqNJDJ.js +1 -0
- package/core/built/admin/assets/settings-B4vM-rgV.js +4 -0
- package/core/built/admin/assets/{settings-BGgk7oCI.js → settings-ZCj-W56o.js} +1 -1
- package/core/built/admin/assets/settings-breadcrumbs-DIq31FwA.js +1 -0
- package/core/built/admin/assets/{share-modal-BeuydaBf.js → share-modal-BNj14g79.js} +1 -1
- package/core/built/admin/assets/slack-modal-C6SpMQ-q.js +1 -0
- package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
- package/core/built/admin/assets/{sort-button-Df4t2AL8.js → sort-button-B-k54ow2.js} +1 -1
- package/core/built/admin/assets/{source-icon-ajoaKb41.js → source-icon-Cej3sZM6.js} +1 -1
- package/core/built/admin/assets/{stats-C_lfxZAw.js → stats-B2KW8E2y.js} +1 -1
- package/core/built/admin/assets/{stats-view-Cxot1iD8.js → stats-view-CVzU2-p5.js} +1 -1
- package/core/built/admin/assets/{step-1-ogAA4lDr.js → step-1-Dbn1iIMU.js} +1 -1
- package/core/built/admin/assets/{step-2-C5Xini-7.js → step-2-BahnSuN6.js} +1 -1
- package/core/built/admin/assets/{step-3-CZv1FV81.js → step-3-CW04e2ZJ.js} +2 -2
- package/core/built/admin/assets/stripe-button-BkBZqaGG.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-CYDDrSJm.js +1 -0
- package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
- package/core/built/admin/assets/{table-DFj1wiYv.js → table-BtLcEi1M.js} +1 -1
- package/core/built/admin/assets/tabs-BaWRfg8j.js +1 -0
- package/core/built/admin/assets/tag-detail-CmqiC32L.js +1 -0
- package/core/built/admin/assets/tags-BbygUvNB.js +1 -0
- package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
- package/core/built/admin/assets/testimonials-modal--bdzppyh.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-BZmAsIy4.js +1 -0
- package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
- package/core/built/admin/assets/toggle-group-CVl-Xigr.js +1 -0
- package/core/built/admin/assets/{topic-filter-DGDxDalo.js → topic-filter-BoRHRbL1.js} +1 -1
- package/core/built/admin/assets/transistor-modal-CBPDqEGy.js +1 -0
- package/core/built/admin/assets/unsplash-modal-CixoRAO2.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-C76v4LIp.js → use-analytics-data-BiWvcf1u.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B14lZukR.js +2 -0
- package/core/built/admin/assets/use-feature-flag-BmJoc5WN.js +1 -0
- package/core/built/admin/assets/use-filter-params-ChoV72Hw.js +1 -0
- package/core/built/admin/assets/use-filterable-api-iMpDZBxD.js +1 -0
- package/core/built/admin/assets/use-form-vRjI0Kwk.js +1 -0
- package/core/built/admin/assets/{use-growth-stats-aNXJOmQr.js → use-growth-stats-Dom8xVzo.js} +1 -1
- package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
- package/core/built/admin/assets/use-limiter-Dwg8ivgQ.js +2 -0
- package/core/built/admin/assets/use-pintura-config-Bj4QmiVk.js +1 -0
- package/core/built/admin/assets/use-pintura-editor-DvrslmPE.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-CG0SZioj.js → use-post-referrers-pxIHTXFs.js} +1 -1
- package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
- package/core/built/admin/assets/{use-reply-chain-data-RLdPKRmS.js → use-reply-chain-data-CmFe0N6F.js} +1 -1
- package/core/built/admin/assets/use-setting-group-Cmp_sy-d.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-BmROPrls.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-DgcF69KI.js → use-simple-pagination-D5rL3YwO.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-C1R_Kbmy.js → use-tinybird-query-DP2dMaq7.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-C9fPjVM_.js → use-tinybird-token-DrK_skPQ.js} +1 -1
- package/core/built/admin/assets/use-url-input-ChrUZBFf.js +5 -0
- package/core/built/admin/assets/user-detail-modal-XODpOqNC.js +1 -0
- package/core/built/admin/assets/{users-Dkv8NQZe.js → users-B6hIds6n.js} +1 -1
- package/core/built/admin/assets/validator-Cr0sMKuW.js +7 -0
- package/core/built/admin/assets/{virtual-list-BmTUzeoF.js → virtual-list-Dnoafqbk.js} +1 -1
- package/core/built/admin/assets/web-D71mjB2r.js +1 -0
- package/core/built/admin/assets/web-z71lA1Y3.js +2 -0
- package/core/built/admin/assets/zapier-modal-Bp507Um7.js +1 -0
- package/core/built/admin/index.html +16 -14
- package/core/frontend/helpers/authors.js +1 -1
- package/core/frontend/helpers/link.js +3 -8
- package/core/frontend/helpers/tags.js +1 -1
- package/core/frontend/meta/author-url.js +2 -2
- package/core/frontend/meta/url.js +4 -4
- package/core/frontend/services/proxy.js +1 -1
- package/core/frontend/services/routing/events.js +20 -6
- package/core/frontend/services/routing/permalink-adapter.js +0 -13
- package/core/frontend/services/routing/permalink-adapter.ts +0 -13
- package/core/frontend/services/routing/router-manager.js +15 -9
- package/core/frontend/services/sitemap/base-site-map-generator.js +1 -28
- package/core/frontend/services/sitemap/site-map-manager.js +22 -57
- package/core/frontend/web/middleware/admin-toolbar.js +1 -1
- package/core/frontend/web/middleware/serve-indexnow-key.js +1 -7
- package/core/server/adapters/route-settings/utils.js +10 -2
- package/core/server/adapters/route-settings/utils.ts +12 -2
- package/core/server/api/endpoints/email-post.js +1 -1
- package/core/server/api/endpoints/index.js +2 -2
- package/core/server/api/endpoints/previews.js +1 -1
- package/core/server/api/endpoints/search-index-public.js +3 -3
- package/core/server/api/endpoints/search-index.js +4 -4
- package/core/server/api/endpoints/themes.js +6 -1
- package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/comments.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/pages.js +3 -3
- package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
- package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/users.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/utils/url.js +31 -13
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +2 -2
- package/core/server/api/endpoints/utils/serializers/output/mappers/tags.js +2 -2
- package/core/server/api/endpoints/utils/serializers/output/mappers/users.js +8 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/url.js +18 -11
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +74 -0
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-54-14-reset-automation-email-analytics.js +25 -0
- package/core/server/data/schema/commands.js +21 -5
- package/core/server/data/schema/schema.js +24 -5
- package/core/server/lib/common/to-plain.ts +1 -1
- package/core/server/models/base/plugins/overrides.js +7 -5
- package/core/server/models/post.js +2 -2
- package/core/server/services/adapter-manager/adapter-manager.js +1 -45
- package/core/server/services/adapter-manager/adapter-manager.ts +2 -48
- package/core/server/services/adapter-manager/adapter-paths.js +28 -0
- package/core/server/services/adapter-manager/adapter-paths.ts +26 -0
- package/core/server/services/adapter-manager/base-classes.js +22 -0
- package/core/server/services/adapter-manager/base-classes.ts +22 -0
- package/core/server/services/adapter-manager/index.js +4 -27
- package/core/server/services/adapter-manager/index.ts +4 -31
- package/core/server/services/adapter-manager/utils.js +50 -0
- package/core/server/services/adapter-manager/utils.ts +57 -0
- package/core/server/services/audience-feedback/audience-feedback-controller.js +11 -2
- package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
- package/core/server/services/audience-feedback/feedback-repository.js +4 -1
- package/core/server/services/auth/session/session-service.js +18 -3
- package/core/server/services/automations/database-automations-repository.js +5 -5
- package/core/server/services/automations/database-automations-repository.ts +12 -12
- package/core/server/services/automations/database-date.js +52 -3
- package/core/server/services/automations/database-date.ts +23 -2
- package/core/server/services/automations/service.js +2 -2
- package/core/server/services/automations/service.ts +2 -2
- package/core/server/services/comments/comments-service-emails.js +1 -1
- package/core/server/services/comments/comments-service.js +1 -1
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +12 -3
- package/core/server/services/email-analytics/email-analytics-service.js +4 -3
- package/core/server/services/email-analytics/index.js +2 -3
- package/core/server/services/email-analytics/index.ts +1 -2
- package/core/server/services/email-analytics/lib/queries.js +91 -124
- package/core/server/services/email-analytics/lib/queries.ts +341 -0
- package/core/server/services/email-service/email-service-wrapper.js +2 -1
- package/core/server/services/email-service/mailgun-email-provider.js +15 -0
- package/core/server/services/indexnow-ping/index.js +0 -2
- package/core/server/services/indexnow-ping/index.ts +0 -2
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +6 -8
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +9 -17
- package/core/server/services/lib/mailgun-client.js +3 -3
- package/core/server/services/media-inliner/external-media-inliner.js +12 -8
- package/core/server/services/member-attribution/url-translator.js +5 -7
- package/core/server/services/member-welcome-emails/service.js +72 -12
- package/core/server/services/members/api.js +1 -1
- package/core/server/services/members/import-export/import/completion-email.js +8 -4
- package/core/server/services/members/import-export/import/completion-email.ts +9 -4
- package/core/server/services/members/import-export/import/importer.js +25 -4
- package/core/server/services/members/import-export/import/importer.ts +26 -6
- package/core/server/services/members/import-export/import/row.ts +5 -1
- package/core/server/services/members/import-export/index.js +1 -5
- package/core/server/services/members/import-export/index.ts +2 -6
- package/core/server/services/members/members-api/repositories/member-repository.js +51 -0
- package/core/server/services/members-custom-fields/queries.js +4 -13
- package/core/server/services/members-custom-fields/queries.ts +4 -13
- package/core/server/services/members-custom-fields/schema.js +18 -27
- package/core/server/services/members-custom-fields/schema.ts +17 -27
- package/core/server/services/members-custom-fields/storage.js +94 -27
- package/core/server/services/members-custom-fields/storage.ts +114 -39
- package/core/server/services/members-custom-fields/values-service.js +98 -219
- package/core/server/services/members-custom-fields/values-service.ts +110 -238
- package/core/server/services/mentions/resource-service.js +1 -1
- package/core/server/services/mentions/service.js +2 -2
- package/core/server/services/route-settings/dynamic-routing-service.js +9 -59
- package/core/server/services/slack-notifications/slack-notifications.js +2 -2
- package/core/server/services/slack-ping/index.js +1 -1
- package/core/server/services/slack-ping/index.ts +1 -1
- package/core/server/services/slack-ping/slack-ping-service.js +3 -2
- package/core/server/services/slack-ping/slack-ping-service.ts +4 -5
- package/core/server/services/stats/content-stats-service.js +3 -3
- package/core/server/services/stats/members-stats-service.js +0 -7
- package/core/server/services/stats/posts-stats-service.js +2 -2
- package/core/server/services/themes/storage.js +19 -2
- package/core/server/services/url/config.js +90 -135
- package/core/server/services/url/index.js +6 -49
- package/core/server/services/url/lazy-find-resource.js +14 -11
- package/core/server/services/url/lazy-find-resource.ts +14 -11
- package/core/server/services/url/lazy-url-service.js +174 -94
- package/core/server/services/url/lazy-url-service.ts +233 -97
- package/core/server/services/url/routable-resources.js +62 -71
- package/core/server/services/url/router-filter.js +12 -4
- package/core/server/services/url/router-filter.ts +12 -4
- package/core/server/services/webhooks/index.js +1 -1
- package/core/server/services/webhooks/serialize.js +12 -2
- package/core/server/web/api/endpoints/admin/routes.js +2 -2
- package/core/shared/config/env/config.testing-mysql.json +1 -2
- package/core/shared/config/env/config.testing.json +1 -2
- package/core/shared/custom-theme-settings-cache/custom-theme-settings-bread-service.js +4 -0
- package/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js +39 -0
- package/core/shared/events/index.js +1 -2
- package/core/shared/labs.js +4 -4
- package/package.json +10 -11
- package/pnpm-lock.yaml +44 -47
- package/pnpm-workspace.yaml +19 -19
- package/tsconfig.json +1 -0
- package/components/tryghost-adapter-base-scheduling-0.2.0.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.3.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
- package/core/built/admin/assets/Combination-BCm9FL4o.js +0 -41
- package/core/built/admin/assets/activitypub/layout-CTgrezN7.js +0 -1
- package/core/built/admin/assets/automated-emails-6t2bIRWf.js +0 -7
- package/core/built/admin/assets/checkbox-BIl077a0.js +0 -1
- package/core/built/admin/assets/chunk.524.87b52f4fada529df2c4c.js +0 -25
- package/core/built/admin/assets/chunk.582.340bcca873e806cbd4bb.js +0 -27
- package/core/built/admin/assets/code-editor-view-C0aWNwdl.js +0 -1
- package/core/built/admin/assets/comments-qwhIjH3c.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-DYAXW-6V.js +0 -1
- package/core/built/admin/assets/dist-BUKmD51u.js +0 -1
- package/core/built/admin/assets/dist-CNlMj7ML.js +0 -1
- package/core/built/admin/assets/dist-DDY6GRVz.js +0 -1
- package/core/built/admin/assets/dist-DG78yroX.js +0 -23
- package/core/built/admin/assets/editor-DDYSOXGN.js +0 -8
- package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
- package/core/built/admin/assets/filters-CLehPo_D.js +0 -1
- package/core/built/admin/assets/index-08n05-rc.js +0 -67
- package/core/built/admin/assets/index-DgUzgFvS.css +0 -1
- package/core/built/admin/assets/label-picker-DVnpu_IF.js +0 -1
- package/core/built/admin/assets/layout-B0YLh7R9.js +0 -1
- package/core/built/admin/assets/member-custom-fields-C8NpZYza.js +0 -1
- package/core/built/admin/assets/member-detail-CIVAdKXf.js +0 -1
- package/core/built/admin/assets/members-oLPiSkXD.js +0 -10
- package/core/built/admin/assets/modals-CB7v-k3T.js +0 -31
- package/core/built/admin/assets/newsletter-rbeDX9Wk.js +0 -1
- package/core/built/admin/assets/overview--4V-kx9c.js +0 -1
- package/core/built/admin/assets/overview-DSLv30oz.js +0 -1
- package/core/built/admin/assets/post-analytics-header-pU2m9ZuV.js +0 -1
- package/core/built/admin/assets/preferences-Ru4CRPHC.js +0 -5
- package/core/built/admin/assets/profile-BNb1oj7D.js +0 -1
- package/core/built/admin/assets/select-B0HteHrp.js +0 -1
- package/core/built/admin/assets/settings-B1C9Q3wo.js +0 -4
- package/core/built/admin/assets/social-urls-DA2lXrhW.js +0 -8
- package/core/built/admin/assets/switch-CyQcwMAv.js +0 -1
- package/core/built/admin/assets/tabs-D4y1gqUd.js +0 -1
- package/core/built/admin/assets/tags-Dmft-Y4O.js +0 -1
- package/core/built/admin/assets/toggle-group-R2QMFvXW.js +0 -1
- package/core/built/admin/assets/use-filter-params-WrdhAISv.js +0 -1
- package/core/built/admin/assets/web-BqW0b0_C.js +0 -1
- package/core/built/admin/assets/web-Cy5GKMWM.js +0 -2
- package/core/server/services/email-analytics/jobs/update-member-email-analytics/index.js +0 -14
- package/core/server/services/url/local-file-cache.js +0 -75
- package/core/server/services/url/queue.js +0 -260
- package/core/server/services/url/resource.js +0 -102
- package/core/server/services/url/resources.js +0 -480
- package/core/server/services/url/url-generator.js +0 -276
- package/core/server/services/url/url-service-facade.js +0 -366
- package/core/server/services/url/url-service-facade.ts +0 -519
- package/core/server/services/url/url-service.js +0 -387
- package/core/server/services/url/urls.js +0 -140
- package/core/shared/events/url-resource-updated-event.js +0 -33
- /package/core/built/admin/assets/{chunk.211.6f7328d63c41536451b3.js.LICENSE.txt → chunk.571.40f093ed6d883993714e.js.LICENSE.txt} +0 -0
- /package/core/built/admin/assets/{dist-CqO2lWS0.js → dist-CqO2lWS02.js} +0 -0
- /package/core/server/api/endpoints/{gift-reminders.js → gifts.js} +0 -0
|
@@ -9,6 +9,8 @@ const emailAddressService = require('../email-address');
|
|
|
9
9
|
const settingsHelpers = require('../settings-helpers');
|
|
10
10
|
const EmailAddressParser = require('../email-address/email-address-parser');
|
|
11
11
|
const mail = require('../mail');
|
|
12
|
+
const MailgunClient = require('../lib/mailgun-client');
|
|
13
|
+
const config = require('../../../shared/config');
|
|
12
14
|
const labs = require('../../../shared/labs');
|
|
13
15
|
const {Automation, EmailDesignSetting, Newsletter} = require('../../models');
|
|
14
16
|
const MemberWelcomeEmailRenderer = require('./member-welcome-email-renderer');
|
|
@@ -46,7 +48,8 @@ const getSenderDetails = (designSettingsJson) => {
|
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
class MemberWelcomeEmailService {
|
|
49
|
-
#
|
|
51
|
+
#transactionalMailer;
|
|
52
|
+
#bulkMailer;
|
|
50
53
|
#renderer;
|
|
51
54
|
#magicLinkService;
|
|
52
55
|
#memberWelcomeEmails = {free: null, paid: null};
|
|
@@ -54,7 +57,8 @@ class MemberWelcomeEmailService {
|
|
|
54
57
|
|
|
55
58
|
constructor({t, dir, singleUseTokenProvider}) {
|
|
56
59
|
emailAddressService.init();
|
|
57
|
-
this.#
|
|
60
|
+
this.#transactionalMailer = new mail.GhostMailer();
|
|
61
|
+
this.#bulkMailer = new MailgunClient({config, settings: settingsCache});
|
|
58
62
|
this.#renderer = new MemberWelcomeEmailRenderer({t, dir});
|
|
59
63
|
|
|
60
64
|
const getSigninURL = (token) => {
|
|
@@ -306,7 +310,7 @@ class MemberWelcomeEmailService {
|
|
|
306
310
|
logging.warn(message.text);
|
|
307
311
|
}
|
|
308
312
|
|
|
309
|
-
return this.#
|
|
313
|
+
return this.#transactionalMailer.send({
|
|
310
314
|
from: fromEmail,
|
|
311
315
|
subject: 'Verify email address',
|
|
312
316
|
forceTextContent: true,
|
|
@@ -409,18 +413,16 @@ class MemberWelcomeEmailService {
|
|
|
409
413
|
getSenderDetails(email.designSettings)
|
|
410
414
|
);
|
|
411
415
|
|
|
412
|
-
const headers = unsubscribe?.oneClickUrl ? {
|
|
413
|
-
'List-Unsubscribe': `<${unsubscribe.oneClickUrl}>`,
|
|
414
|
-
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
|
|
415
|
-
} : undefined;
|
|
416
|
-
|
|
417
416
|
/** @type {string[]} */ let tags;
|
|
417
|
+
let sendEmail;
|
|
418
418
|
switch (emailType) {
|
|
419
419
|
case 'welcome':
|
|
420
420
|
tags = [MEMBER_WELCOME_EMAIL_TAG];
|
|
421
|
+
sendEmail = this.#sendTransactionalEmail.bind(this);
|
|
421
422
|
break;
|
|
422
423
|
case 'automation':
|
|
423
424
|
tags = [AUTOMATION_EMAIL_TAG];
|
|
425
|
+
sendEmail = this.#sendBulkEmail.bind(this);
|
|
424
426
|
break;
|
|
425
427
|
default: {
|
|
426
428
|
/** @type {never} */ const _exhaustive = emailType;
|
|
@@ -430,19 +432,78 @@ class MemberWelcomeEmailService {
|
|
|
430
432
|
}
|
|
431
433
|
}
|
|
432
434
|
|
|
433
|
-
return await
|
|
435
|
+
return await sendEmail({
|
|
434
436
|
to: member.email,
|
|
435
437
|
subject,
|
|
436
438
|
html,
|
|
437
439
|
text,
|
|
440
|
+
tags,
|
|
441
|
+
trackOpens,
|
|
442
|
+
listUnsubscribe: unsubscribe?.oneClickUrl,
|
|
443
|
+
...senderOptions
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* @param {object} options
|
|
449
|
+
* @param {string} options.to
|
|
450
|
+
* @param {string} options.subject
|
|
451
|
+
* @param {string} options.html
|
|
452
|
+
* @param {string} options.text
|
|
453
|
+
* @param {string} options.from
|
|
454
|
+
* @param {string} [options.replyTo]
|
|
455
|
+
* @param {string[]} [options.tags]
|
|
456
|
+
* @param {boolean} [options.trackOpens]
|
|
457
|
+
* @param {string} [options.listUnsubscribe]
|
|
458
|
+
* @returns {Promise<unknown>}
|
|
459
|
+
*/
|
|
460
|
+
async #sendTransactionalEmail({to, subject, html, text, from, replyTo, tags, trackOpens, listUnsubscribe}) {
|
|
461
|
+
const headers = listUnsubscribe ? {
|
|
462
|
+
'List-Unsubscribe': `<${listUnsubscribe}>`,
|
|
463
|
+
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
|
|
464
|
+
} : undefined;
|
|
465
|
+
|
|
466
|
+
return await this.#transactionalMailer.send({
|
|
467
|
+
to,
|
|
468
|
+
subject,
|
|
469
|
+
html,
|
|
470
|
+
text,
|
|
438
471
|
forceTextContent: true,
|
|
439
472
|
tags,
|
|
440
473
|
...(headers ? {headers} : {}),
|
|
441
474
|
...(typeof trackOpens === 'boolean' ? {trackOpens} : {}),
|
|
442
|
-
|
|
475
|
+
from,
|
|
476
|
+
replyTo
|
|
443
477
|
});
|
|
444
478
|
}
|
|
445
479
|
|
|
480
|
+
/**
|
|
481
|
+
* @param {object} options
|
|
482
|
+
* @param {string} options.to
|
|
483
|
+
* @param {string} options.subject
|
|
484
|
+
* @param {string} options.html
|
|
485
|
+
* @param {string} options.text
|
|
486
|
+
* @param {string} options.from
|
|
487
|
+
* @param {string} [options.replyTo]
|
|
488
|
+
* @param {string[]} [options.tags]
|
|
489
|
+
* @param {boolean} [options.trackOpens]
|
|
490
|
+
* @param {string} [options.listUnsubscribe]
|
|
491
|
+
* @returns {Promise<unknown>}
|
|
492
|
+
*/
|
|
493
|
+
async #sendBulkEmail({to, subject, html, text, from, replyTo, tags, trackOpens, listUnsubscribe}) {
|
|
494
|
+
return await this.#bulkMailer.send({
|
|
495
|
+
subject,
|
|
496
|
+
html,
|
|
497
|
+
plaintext: text,
|
|
498
|
+
from,
|
|
499
|
+
replyTo,
|
|
500
|
+
tags,
|
|
501
|
+
...(typeof trackOpens === 'boolean' ? {track_opens: trackOpens} : {})
|
|
502
|
+
}, {
|
|
503
|
+
[to]: listUnsubscribe ? {list_unsubscribe: listUnsubscribe} : {}
|
|
504
|
+
}, []);
|
|
505
|
+
}
|
|
506
|
+
|
|
446
507
|
async send({member, memberStatus}) {
|
|
447
508
|
const email = this.#memberWelcomeEmails[memberStatus];
|
|
448
509
|
|
|
@@ -651,12 +712,11 @@ class MemberWelcomeEmailService {
|
|
|
651
712
|
getSenderDetails(designSettingsJson)
|
|
652
713
|
);
|
|
653
714
|
|
|
654
|
-
await this.#
|
|
715
|
+
await this.#sendTransactionalEmail({
|
|
655
716
|
to: email,
|
|
656
717
|
subject: `[Test] ${renderedSubject}`,
|
|
657
718
|
html,
|
|
658
719
|
text,
|
|
659
|
-
forceTextContent: true,
|
|
660
720
|
...senderOptions
|
|
661
721
|
});
|
|
662
722
|
}
|
|
@@ -54,7 +54,7 @@ function trimLeadingWhitespace(strings, ...values) {
|
|
|
54
54
|
|
|
55
55
|
function createApiInstance(config) {
|
|
56
56
|
const membersApiInstance = MembersApi({
|
|
57
|
-
urlService
|
|
57
|
+
urlService,
|
|
58
58
|
tokenConfig: config.getTokenConfig(),
|
|
59
59
|
auth: {
|
|
60
60
|
getSigninURL: config.getSigninURL.bind(config),
|
|
@@ -6,14 +6,18 @@ const csv_2 = require("@tryghost/custom-field-types/csv");
|
|
|
6
6
|
const emailTemplate = require('./email-template');
|
|
7
7
|
// Turn an ORM validation error into copy a member manager can act on. Presentation
|
|
8
8
|
// only: the API response keeps the raw messages, and just the emailed report is
|
|
9
|
-
// rewritten.
|
|
10
|
-
|
|
9
|
+
// rewritten. Takes one reason at a time, so a rewrite matching to the end of its input
|
|
10
|
+
// cannot consume the reason after it.
|
|
11
|
+
function humaniseReason(message) {
|
|
11
12
|
return message
|
|
12
13
|
.replace('Value in [members.email] cannot be blank.', 'Missing email address')
|
|
13
14
|
.replace('Value in [members.note] exceeds maximum length of 2000 characters.', '"Note" exceeds maximum length of 2000 characters')
|
|
14
15
|
.replace('Value in [members.subscribed] must be one of true, false, 0 or 1.', 'Value in "Subscribed to emails" must be "true" or "false"')
|
|
15
16
|
.replace('Validation (isEmail) failed for email', 'Invalid email address')
|
|
16
|
-
.replace(/No such customer:[
|
|
17
|
+
.replace(/No such customer:[\s\S]*/, 'Could not find Stripe customer');
|
|
18
|
+
}
|
|
19
|
+
function humaniseError(row) {
|
|
20
|
+
return row.errors.map(humaniseReason).join('\n');
|
|
17
21
|
}
|
|
18
22
|
function stringifyLabels(labels) {
|
|
19
23
|
return labels.map(label => (typeof label === 'string' ? label : label.name)).join(',');
|
|
@@ -40,7 +44,7 @@ function toErrorReportRow(row) {
|
|
|
40
44
|
labels: stringifyLabels(row.labels),
|
|
41
45
|
tiers: '',
|
|
42
46
|
gift_id: row.gift_id || null,
|
|
43
|
-
error: humaniseError(row
|
|
47
|
+
error: humaniseError(row)
|
|
44
48
|
};
|
|
45
49
|
}
|
|
46
50
|
// The error report attached to the completion email: the failed rows as CSV. It shares
|
|
@@ -34,14 +34,19 @@ interface EmailPayload {
|
|
|
34
34
|
|
|
35
35
|
// Turn an ORM validation error into copy a member manager can act on. Presentation
|
|
36
36
|
// only: the API response keeps the raw messages, and just the emailed report is
|
|
37
|
-
// rewritten.
|
|
38
|
-
|
|
37
|
+
// rewritten. Takes one reason at a time, so a rewrite matching to the end of its input
|
|
38
|
+
// cannot consume the reason after it.
|
|
39
|
+
function humaniseReason(message: string): string {
|
|
39
40
|
return message
|
|
40
41
|
.replace('Value in [members.email] cannot be blank.', 'Missing email address')
|
|
41
42
|
.replace('Value in [members.note] exceeds maximum length of 2000 characters.', '"Note" exceeds maximum length of 2000 characters')
|
|
42
43
|
.replace('Value in [members.subscribed] must be one of true, false, 0 or 1.', 'Value in "Subscribed to emails" must be "true" or "false"')
|
|
43
44
|
.replace('Validation (isEmail) failed for email', 'Invalid email address')
|
|
44
|
-
.replace(/No such customer:[
|
|
45
|
+
.replace(/No such customer:[\s\S]*/, 'Could not find Stripe customer');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function humaniseError(row: ImportErrorRow): string {
|
|
49
|
+
return row.errors.map(humaniseReason).join('\n');
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
// One row of the fixed, member-vocabulary part of the error report, in emit order.
|
|
@@ -93,7 +98,7 @@ function toErrorReportRow(row: ImportErrorRow): ErrorReportRow {
|
|
|
93
98
|
labels: stringifyLabels(row.labels),
|
|
94
99
|
tiers: '',
|
|
95
100
|
gift_id: row.gift_id || null,
|
|
96
|
-
error: humaniseError(row
|
|
101
|
+
error: humaniseError(row)
|
|
97
102
|
};
|
|
98
103
|
}
|
|
99
104
|
|
|
@@ -165,7 +165,7 @@ class MembersCSVImporter {
|
|
|
165
165
|
// before any member write -- so there is no reason to hold a transaction
|
|
166
166
|
// across it, and doing so would deadlock the single-connection SQLite pool.
|
|
167
167
|
const customFieldPlan = activeCustomFields.length > 0
|
|
168
|
-
? await this._customFields.planWrite((0, csv_2.fieldValuesFromCsvRow)(activeCustomFields, row, csv_1.stripFormulaGuard))
|
|
168
|
+
? await namingTheColumn(() => this._customFields.planWrite((0, csv_2.fieldValuesFromCsvRow)(activeCustomFields, row, csv_1.stripFormulaGuard)))
|
|
169
169
|
: [];
|
|
170
170
|
trx = await this._knex.transaction(undefined, { doNotRejectOnRollback: false });
|
|
171
171
|
const options = { transacting: trx, context: IMPORT_CONTEXT };
|
|
@@ -265,15 +265,16 @@ class MembersCSVImporter {
|
|
|
265
265
|
}
|
|
266
266
|
catch (error) {
|
|
267
267
|
const errorList = Array.isArray(error) ? error : [error];
|
|
268
|
-
const
|
|
268
|
+
const reasons = errorList
|
|
269
269
|
.map(e => (typeof e === 'object' && e !== null && 'message' in e ? e.message : undefined))
|
|
270
|
-
.
|
|
270
|
+
.filter((message) => typeof message === 'string');
|
|
271
|
+
const errorMessage = reasons.join('\n');
|
|
271
272
|
// trx is unset if the row failed before the transaction opened (a bad
|
|
272
273
|
// custom field value or gift combination).
|
|
273
274
|
if (trx) {
|
|
274
275
|
await trx.rollback();
|
|
275
276
|
}
|
|
276
|
-
importErrors.push({ ...row, error: errorMessage });
|
|
277
|
+
importErrors.push({ ...row, error: errorMessage, errors: reasons });
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
await Promise.all(archivableStripePriceIds.map(id => this._stripe.archivePrice(id)));
|
|
@@ -287,6 +288,26 @@ class MembersCSVImporter {
|
|
|
287
288
|
return { imported: 0, errors: importErrors };
|
|
288
289
|
}
|
|
289
290
|
}
|
|
291
|
+
// A row error is read next to a spreadsheet, and a value rejection states only what the
|
|
292
|
+
// value should be — the same sentence Admin shows under a single input, where the field is
|
|
293
|
+
// already on screen. `property` is the dotted path a default export writes as its column
|
|
294
|
+
// header, so prefixing with it names the column a publisher has to go and fix.
|
|
295
|
+
//
|
|
296
|
+
// A rejection about the whole request carries the bare namespace and names no field after
|
|
297
|
+
// it, so it is left alone rather than made to point at a column that does not exist.
|
|
298
|
+
async function namingTheColumn(plan) {
|
|
299
|
+
try {
|
|
300
|
+
return await plan();
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
const { property, message } = (error ?? {});
|
|
304
|
+
const namesAField = typeof property === 'string' && property.includes('.');
|
|
305
|
+
if (!namesAField || typeof message !== 'string') {
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
throw new errors.DataImportError({ message: `${property}: ${message}` });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
290
311
|
function wrapGiftError(error) {
|
|
291
312
|
const err = error;
|
|
292
313
|
const message = (err && typeof err.message === 'string' && err.message) || tpl(messages.giftReassignFailed);
|
|
@@ -99,8 +99,7 @@ type CustomFieldPlan = unknown;
|
|
|
99
99
|
// The custom fields collaborator as the import needs it. activeFields is the field set a
|
|
100
100
|
// custom_fields.* column is read against, empty when the feature is off; planWrite
|
|
101
101
|
// validates a row's values (throwing so the row fails whole) and applyWrite persists
|
|
102
|
-
// them,
|
|
103
|
-
// row's transaction.
|
|
102
|
+
// them, touching only the parts the row named, both on the row's transaction.
|
|
104
103
|
export interface CustomFieldsImport {
|
|
105
104
|
activeFields(): Promise<CsvField[]>;
|
|
106
105
|
planWrite(values: Record<string, unknown>): Promise<CustomFieldPlan[]>;
|
|
@@ -333,7 +332,7 @@ class MembersCSVImporter {
|
|
|
333
332
|
// before any member write -- so there is no reason to hold a transaction
|
|
334
333
|
// across it, and doing so would deadlock the single-connection SQLite pool.
|
|
335
334
|
const customFieldPlan = activeCustomFields.length > 0
|
|
336
|
-
? await this._customFields.planWrite(fieldValuesFromCsvRow(activeCustomFields, row, stripFormulaGuard))
|
|
335
|
+
? await namingTheColumn(() => this._customFields.planWrite(fieldValuesFromCsvRow(activeCustomFields, row, stripFormulaGuard)))
|
|
337
336
|
: [];
|
|
338
337
|
|
|
339
338
|
trx = await this._knex.transaction(undefined, {doNotRejectOnRollback: false});
|
|
@@ -433,15 +432,16 @@ class MembersCSVImporter {
|
|
|
433
432
|
imported += 1;
|
|
434
433
|
} catch (error) {
|
|
435
434
|
const errorList: unknown[] = Array.isArray(error) ? error : [error];
|
|
436
|
-
const
|
|
435
|
+
const reasons = errorList
|
|
437
436
|
.map(e => (typeof e === 'object' && e !== null && 'message' in e ? e.message : undefined))
|
|
438
|
-
.
|
|
437
|
+
.filter((message): message is string => typeof message === 'string');
|
|
438
|
+
const errorMessage = reasons.join('\n');
|
|
439
439
|
// trx is unset if the row failed before the transaction opened (a bad
|
|
440
440
|
// custom field value or gift combination).
|
|
441
441
|
if (trx) {
|
|
442
442
|
await trx.rollback();
|
|
443
443
|
}
|
|
444
|
-
importErrors.push({...row, error: errorMessage});
|
|
444
|
+
importErrors.push({...row, error: errorMessage, errors: reasons});
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
|
|
@@ -458,6 +458,26 @@ class MembersCSVImporter {
|
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
+
// A row error is read next to a spreadsheet, and a value rejection states only what the
|
|
462
|
+
// value should be — the same sentence Admin shows under a single input, where the field is
|
|
463
|
+
// already on screen. `property` is the dotted path a default export writes as its column
|
|
464
|
+
// header, so prefixing with it names the column a publisher has to go and fix.
|
|
465
|
+
//
|
|
466
|
+
// A rejection about the whole request carries the bare namespace and names no field after
|
|
467
|
+
// it, so it is left alone rather than made to point at a column that does not exist.
|
|
468
|
+
async function namingTheColumn<T>(plan: () => Promise<T>): Promise<T> {
|
|
469
|
+
try {
|
|
470
|
+
return await plan();
|
|
471
|
+
} catch (error) {
|
|
472
|
+
const {property, message} = (error ?? {}) as {property?: unknown; message?: unknown};
|
|
473
|
+
const namesAField = typeof property === 'string' && property.includes('.');
|
|
474
|
+
if (!namesAField || typeof message !== 'string') {
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
throw new errors.DataImportError({message: `${property}: ${message}`});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
461
481
|
function wrapGiftError(error: unknown) {
|
|
462
482
|
const err = error as {message?: unknown};
|
|
463
483
|
const message = (err && typeof err.message === 'string' && err.message) || tpl(messages.giftReassignFailed);
|
|
@@ -42,7 +42,11 @@ export type MemberImportRow = z.infer<typeof memberImportRowSchema>;
|
|
|
42
42
|
|
|
43
43
|
// A row that failed to import, carrying the raw message that stopped it. The message is
|
|
44
44
|
// left raw here; turning it into human copy is a presentation concern.
|
|
45
|
-
|
|
45
|
+
// A row can fail for more than one reason. `errors` is the list; `error` is that list
|
|
46
|
+
// written out for the error report's single cell. Two fields because a reason may quote a
|
|
47
|
+
// cell the publisher wrote, and a CSV cell legally holds commas and newlines, so no
|
|
48
|
+
// separator survives being split back apart.
|
|
49
|
+
export type ImportErrorRow = MemberImportRow & {error: string; errors: string[]};
|
|
46
50
|
|
|
47
51
|
// The persisted import label as plain data -- taken off the Bookshelf model before it
|
|
48
52
|
// leaves the kernel, so the result and the email carry only what they show.
|
|
@@ -52,11 +52,7 @@ function makeImporter(deps) {
|
|
|
52
52
|
const customFields = {
|
|
53
53
|
activeFields: async () => (labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : []),
|
|
54
54
|
planWrite: values => deps.customFields.values.planWrite(values),
|
|
55
|
-
|
|
56
|
-
// and leaves the rest alone, the same rule a blank `name` column follows. Without
|
|
57
|
-
// it a file naming one address column would replace the whole address, clearing
|
|
58
|
-
// five sub-fields it never mentioned.
|
|
59
|
-
applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, executor, { mergeComposites: true })
|
|
55
|
+
applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, { executor })
|
|
60
56
|
};
|
|
61
57
|
return new importer_1.default({
|
|
62
58
|
knex: deps.knex,
|
|
@@ -33,7 +33,7 @@ interface ImporterServices {
|
|
|
33
33
|
definitions: {browse(): Promise<CsvField[]>};
|
|
34
34
|
values: {
|
|
35
35
|
planWrite(values: Record<string, unknown>): Promise<unknown[]>;
|
|
36
|
-
applyWrite(memberId: string, plan: unknown[],
|
|
36
|
+
applyWrite(memberId: string, plan: unknown[], options?: {executor?: Knex}): Promise<void>;
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
39
|
}
|
|
@@ -87,11 +87,7 @@ export function makeImporter(deps: ImporterServices) {
|
|
|
87
87
|
const customFields: CustomFieldsImport = {
|
|
88
88
|
activeFields: async () => (labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : []),
|
|
89
89
|
planWrite: values => deps.customFields.values.planWrite(values),
|
|
90
|
-
|
|
91
|
-
// and leaves the rest alone, the same rule a blank `name` column follows. Without
|
|
92
|
-
// it a file naming one address column would replace the whole address, clearing
|
|
93
|
-
// five sub-fields it never mentioned.
|
|
94
|
-
applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, executor, {mergeComposites: true})
|
|
90
|
+
applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, {executor})
|
|
95
91
|
};
|
|
96
92
|
|
|
97
93
|
return new MembersCSVImporter({
|
|
@@ -1256,6 +1256,11 @@ module.exports = class MemberRepository {
|
|
|
1256
1256
|
};
|
|
1257
1257
|
let eventData = {};
|
|
1258
1258
|
|
|
1259
|
+
// A cancellation (or reactivation) changes no `members` column, so the member
|
|
1260
|
+
// model event would be suppressed by `wasChanged()`. Remember the pre-update
|
|
1261
|
+
// subscription so the event can be marked and carry its prior state.
|
|
1262
|
+
let subscriptionBeforeCancelFlagChange = null;
|
|
1263
|
+
|
|
1259
1264
|
const stripeCustomerSubscriptionModelShouldBeDeleted = stripeSubscriptionData.metadata && !!stripeSubscriptionData.metadata.ghost_migrated_to && stripeSubscriptionData.status === 'canceled';
|
|
1260
1265
|
if (stripeCustomerSubscriptionModelShouldBeDeleted) {
|
|
1261
1266
|
logging.warn(`Subscription ${subscriptionData.subscription_id} is marked for deletion, skipping linking.`);
|
|
@@ -1300,6 +1305,10 @@ module.exports = class MemberRepository {
|
|
|
1300
1305
|
this.dispatchEvent(offerRedemptionEvent, options);
|
|
1301
1306
|
}
|
|
1302
1307
|
|
|
1308
|
+
if (stripeCustomerSubscriptionModel.get('cancel_at_period_end') !== updatedStripeCustomerSubscriptionModel.get('cancel_at_period_end')) {
|
|
1309
|
+
subscriptionBeforeCancelFlagChange = stripeCustomerSubscriptionModel;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1303
1312
|
if (stripeCustomerSubscriptionModel.get('mrr') !== updatedStripeCustomerSubscriptionModel.get('mrr') || stripeCustomerSubscriptionModel.get('plan_id') !== updatedStripeCustomerSubscriptionModel.get('plan_id') || stripeCustomerSubscriptionModel.get('status') !== updatedStripeCustomerSubscriptionModel.get('status') || stripeCustomerSubscriptionModel.get('cancel_at_period_end') !== updatedStripeCustomerSubscriptionModel.get('cancel_at_period_end')) {
|
|
1304
1313
|
const originalMrrDelta = stripeCustomerSubscriptionModel.get('mrr');
|
|
1305
1314
|
const updatedMrrDelta = updatedStripeCustomerSubscriptionModel.get('mrr');
|
|
@@ -1550,6 +1559,48 @@ module.exports = class MemberRepository {
|
|
|
1550
1559
|
updatedMember = await this._Member.edit({status: status}, {...options, id: data.id});
|
|
1551
1560
|
}
|
|
1552
1561
|
|
|
1562
|
+
// Cancelling (or reactivating) touches no `members` column, so mark the
|
|
1563
|
+
// subscription relation as the change and carry its prior state. This lets the
|
|
1564
|
+
// webhook payload express before/after — see `services/webhooks/serialize.js`.
|
|
1565
|
+
//
|
|
1566
|
+
// No explicit `emitChange` is needed, and adding one would emit twice: inside a
|
|
1567
|
+
// transaction `emitChange` queues onto the `committed` handler and defers its
|
|
1568
|
+
// `wasChanged()` check to commit time (see `models/base/plugins/events.js`), so
|
|
1569
|
+
// the event already queued by `_Member.edit` above is still pending and setting
|
|
1570
|
+
// `_changed` here is what lets it through. The e2e test asserting exactly one
|
|
1571
|
+
// `member.edited` per cancellation guards this.
|
|
1572
|
+
if (subscriptionBeforeCancelFlagChange && updatedMember) {
|
|
1573
|
+
// Price/tier relations are needed for the serialized subscription shape
|
|
1574
|
+
// to match the Admin API member resource (price, tier, plan)
|
|
1575
|
+
await updatedMember.load([
|
|
1576
|
+
'stripeSubscriptions',
|
|
1577
|
+
'stripeSubscriptions.stripePrice',
|
|
1578
|
+
'stripeSubscriptions.stripePrice.stripeProduct',
|
|
1579
|
+
'stripeSubscriptions.stripePrice.stripeProduct.product'
|
|
1580
|
+
], options);
|
|
1581
|
+
await subscriptionBeforeCancelFlagChange.load([
|
|
1582
|
+
'stripePrice',
|
|
1583
|
+
'stripePrice.stripeProduct',
|
|
1584
|
+
'stripePrice.stripeProduct.product'
|
|
1585
|
+
], options);
|
|
1586
|
+
updatedMember._changed = {
|
|
1587
|
+
...updatedMember._changed,
|
|
1588
|
+
stripeSubscriptions: {
|
|
1589
|
+
cancel_at_period_end: subscriptionBeforeCancelFlagChange.get('cancel_at_period_end')
|
|
1590
|
+
}
|
|
1591
|
+
};
|
|
1592
|
+
// `previous` must be the full collection — a member can have multiple
|
|
1593
|
+
// subscriptions — with only the changed one swapped for its prior state
|
|
1594
|
+
updatedMember._previousRelations = {
|
|
1595
|
+
...updatedMember._previousRelations,
|
|
1596
|
+
stripeSubscriptions: {
|
|
1597
|
+
models: updatedMember.related('stripeSubscriptions').models.map((subscription) => {
|
|
1598
|
+
return subscription.id === subscriptionBeforeCancelFlagChange.id ? subscriptionBeforeCancelFlagChange : subscription;
|
|
1599
|
+
})
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1553
1604
|
const newMemberProductIds = memberProducts.map(product => product.id);
|
|
1554
1605
|
const oldMemberProductIds = oldMemberProducts.map(product => product.id);
|
|
1555
1606
|
|
|
@@ -3,19 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.activeFields = activeFields;
|
|
4
4
|
const schema_1 = require("./schema");
|
|
5
5
|
const FIELDS_TABLE = 'members_custom_fields';
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
// fields must stay out of every read and every write, and that's enforced by a
|
|
11
|
-
// filter, not a database constraint — nothing stops a value row referencing an
|
|
12
|
-
// archived field, so a query that forgets the filter is a silent bug. Keeping the
|
|
13
|
-
// filter in one builder means there's one place to get it right.
|
|
14
|
-
/**
|
|
15
|
-
* The active field definitions. Takes the executor — a knex instance or a
|
|
16
|
-
* transaction — so the same query runs standalone or inside a write's
|
|
17
|
-
* transaction; callers chain their own `select`/`orderBy`/`whereIn` onto it.
|
|
18
|
-
*/
|
|
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
|
+
/** Takes the executor so the same query runs standalone or inside a write's transaction. */
|
|
19
10
|
function activeFields(db) {
|
|
20
11
|
return db(FIELDS_TABLE).where('status', schema_1.FIELD_STATUS.active);
|
|
21
12
|
}
|
|
@@ -3,20 +3,11 @@ import {FIELD_STATUS} from './schema';
|
|
|
3
3
|
|
|
4
4
|
const FIELDS_TABLE = 'members_custom_fields';
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// The `status = 'active'` filter is the one invariant worth centralising: archived
|
|
10
|
-
// fields must stay out of every read and every write, and that's enforced by a
|
|
11
|
-
// filter, not a database constraint — nothing stops a value row referencing an
|
|
12
|
-
// archived field, so a query that forgets the filter is a silent bug. Keeping the
|
|
13
|
-
// filter in one builder means there's one place to get it right.
|
|
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.
|
|
14
9
|
|
|
15
|
-
/**
|
|
16
|
-
* The active field definitions. Takes the executor — a knex instance or a
|
|
17
|
-
* transaction — so the same query runs standalone or inside a write's
|
|
18
|
-
* transaction; callers chain their own `select`/`orderBy`/`whereIn` onto it.
|
|
19
|
-
*/
|
|
10
|
+
/** Takes the executor so the same query runs standalone or inside a write's transaction. */
|
|
20
11
|
export function activeFields(db: Knex) {
|
|
21
12
|
return db(FIELDS_TABLE).where('status', FIELD_STATUS.active);
|
|
22
13
|
}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.DbCustomFieldLeaf = exports.DbCustomFieldValue = exports.DbCustomField = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const custom_field_types_1 = require("@tryghost/custom-field-types");
|
|
6
6
|
const db_date_1 = require("../../lib/db-date");
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// schema.js's `isIn` constraint on the column — which is static config and can't
|
|
11
|
-
// import this, so that one stays literal with a pointer back here.
|
|
7
|
+
// `archived` is soft: the field drops out of the values path but stays in the definition
|
|
8
|
+
// list so it can be renamed, restored or deleted. Mirrors schema.js's `isIn` on the
|
|
9
|
+
// column, which is static config and cannot import this.
|
|
12
10
|
exports.FIELD_STATUS = { active: 'active', archived: 'archived' };
|
|
13
11
|
exports.FieldStatusSchema = zod_1.z.enum([exports.FIELD_STATUS.active, exports.FIELD_STATUS.archived]);
|
|
14
|
-
// The
|
|
15
|
-
//
|
|
16
|
-
// only stores registered types), so the row already carries the narrow type and
|
|
17
|
-
// the definition codec needs no cast. `status` travels with the row: it's part of
|
|
18
|
-
// the read projection so the definition list can group active vs archived.
|
|
12
|
+
// The single source for the read projection and the knex table type below. `type` parses
|
|
13
|
+
// as the field-type enum, so the row carries the narrow type and no codec needs a cast.
|
|
19
14
|
exports.DbCustomField = zod_1.z.object({
|
|
20
15
|
id: zod_1.z.string(),
|
|
21
16
|
key: zod_1.z.string(),
|
|
@@ -25,32 +20,28 @@ exports.DbCustomField = zod_1.z.object({
|
|
|
25
20
|
created_at: db_date_1.DbDate,
|
|
26
21
|
updated_at: db_date_1.DbDate.nullable()
|
|
27
22
|
});
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// codec's business (see storage.ts), so they're plain nullable strings here.
|
|
23
|
+
// One part of a member's value. What a `path` means is storage.ts's business, so the row
|
|
24
|
+
// carries it as a plain string.
|
|
31
25
|
exports.DbCustomFieldValue = zod_1.z.object({
|
|
32
26
|
id: zod_1.z.string(),
|
|
33
27
|
custom_field_id: zod_1.z.string(),
|
|
34
28
|
member_id: zod_1.z.string(),
|
|
29
|
+
path: zod_1.z.string(),
|
|
30
|
+
// Nullable like the column, though nothing here writes a null: a part with no value
|
|
31
|
+
// has no row.
|
|
35
32
|
value_text: zod_1.z.string().nullable(),
|
|
36
|
-
value_json: zod_1.z.string().nullable(),
|
|
37
33
|
created_at: db_date_1.DbDate,
|
|
38
34
|
updated_at: db_date_1.DbDate.nullable()
|
|
39
35
|
});
|
|
40
|
-
//
|
|
41
|
-
// sub-fields a composite has is its field type's business, not the row's.
|
|
36
|
+
// The field's key travels with the row so a value assembles without a second lookup.
|
|
42
37
|
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
exports.
|
|
47
|
-
// The value join a read needs: the field's identity and type travel with the
|
|
48
|
-
// stored columns, so a row can be decoded without a second lookup. `type` is
|
|
49
|
-
// parsed as the field-type enum, which narrows it with no cast.
|
|
50
|
-
exports.DbCustomFieldValueWithField = zod_1.z.object({
|
|
38
|
+
// `type` takes no part in the assembly and is here as a gate: a value whose type has left
|
|
39
|
+
// the catalog is one the definitions list no longer returns either, so failing to parse
|
|
40
|
+
// is what drops it.
|
|
41
|
+
exports.DbCustomFieldLeaf = zod_1.z.object({
|
|
51
42
|
member_id: zod_1.z.string(),
|
|
52
43
|
key: zod_1.z.string(),
|
|
53
44
|
type: custom_field_types_1.FieldTypeSchema,
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
path: zod_1.z.string(),
|
|
46
|
+
value_text: zod_1.z.string()
|
|
56
47
|
});
|