ghost 6.55.0 → 6.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/validate-adapters.js +74 -0
- package/bin/validate-adapters.ts +87 -0
- package/bin/warm-compile-cache.sh +60 -0
- package/components/tryghost-custom-field-types-0.0.0.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/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-nql-string-0.0.0.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/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
- package/core/built/admin/assets/about-C7O-AgRZ.js +1 -0
- package/core/built/admin/assets/{account-migration-sjHLx5dy.js → account-migration-Cv_ol1ko.js} +1 -1
- package/core/built/admin/assets/action-list-lGzZLHc8.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-DoYXtnZ8.js → account-migration-DHFbePl6.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +6 -6
- package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-CT4tS2_S.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-ByYWKipo.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-Bxeh6MKW.js → bluesky-sharing-srVnSHDz.js} +16 -16
- package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-C2sos5Ht.js} +3 -3
- package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-B1UBTOoZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-D8sFfdpC.js → dist-MPsb2WZ1.js} +12 -12
- package/core/built/admin/assets/activitypub/{domain-CWueFW6q.js → domain-DzcM9Dzo.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-BGxPU7N5.js → edit-profile-CsQ_4xAk.js} +945 -945
- package/core/built/admin/assets/activitypub/{explore-DBK-u0Ch.js → explore-CJe0DZBK.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-Bcir5RQZ.js → feed-CKV3qWON.js} +33 -33
- package/core/built/admin/assets/activitypub/{field-BV-CIT3d.js → field-DckHfDJf.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-Dlp8RM55.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-CsPy4R0M.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-yC0nfvzl.js → inbox-DAsjzUUe.js} +85 -85
- package/core/built/admin/assets/activitypub/{label-DTRS9TDk.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-Dwl9t0pb.js → moderation-B0Inc03M.js} +11 -11
- package/core/built/admin/assets/activitypub/{note-B-zKsZ7r.js → note-CSE54lER.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-DjJZD54t.js → notifications-RSwbKqdh.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-CoU8vs6d.js → preferences-CE231n2_.js} +20 -20
- package/core/built/admin/assets/activitypub/{profile-BAsaJekx.js → profile-aaQ1j1Eo.js} +237 -237
- package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cjb0Vs3m.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-CLH3OVTP.js → routes-Djb0mc9k.js} +1139 -1150
- package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-D8pROI5n.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-DA6pNJ-Q.js → settings-DMc32IQl.js} +13 -13
- package/core/built/admin/assets/activitypub/{site-CQD42Pw5.js → site-DVIcsohX.js} +81 -81
- package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-9Yey9usF.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DdYmKRXV.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-hdSkcjZU.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-Cg0saow1.js → tabs-CUqseYzQ.js} +317 -315
- package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-Coni6OXM.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-MeHTPMQv.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-B046AM38.js} +115 -96
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-Br4aOWOh.js → use-reply-chain-data-1AO-Xob6.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-BRfRYd16.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-m9Dv6f77.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-CPSVZL2H.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-Ci9Z5yat.js +1 -0
- package/core/built/admin/assets/analytics-DqSCVV8U.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-DU3avV7d.js +1 -0
- package/core/built/admin/assets/api-keys-BK0LylyK.js +1 -0
- package/core/built/admin/assets/api-keys-_iCT4dxr.js +1 -0
- package/core/built/admin/assets/{audience-DjEWNhp3.js → audience-DTJtXXWY.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-Bx3VcFIL.js → automations-dtQ_MRZ7.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-ClMd0-Ft.js → bluesky-sharing-B8Es3AuV.js} +1 -1
- package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
- package/core/built/admin/assets/brand-icon-09YqFZjV.js +1 -0
- package/core/built/admin/assets/{breadcrumb-BD0U2ygg.js → breadcrumb-tg5zL--z.js} +1 -1
- package/core/built/admin/assets/build-CsiH26uS.js +1 -0
- package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
- package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-fjQKRVyY.js → chart-helpers-CZElTGYL.js} +1 -1
- package/core/built/admin/assets/checkbox-Cyo-x7mC.js +1 -0
- package/core/built/admin/assets/chunk.524.cd5498dd4e195f679266.js +25 -0
- package/core/built/admin/assets/chunk.582.6f92ec1f03734bf2ea3f.js +27 -0
- package/core/built/admin/assets/{chunk.481.6c7395a49484bb4583aa.js → chunk.873.b8fc05a8ab877fb819ed.js} +62 -60
- package/core/built/admin/assets/code-editor-view-Dq1rQ4tS.js +1 -0
- package/core/built/admin/assets/color-picker-field-BonBgxhU.js +1 -0
- package/core/built/admin/assets/color-utils-BsrwXuDz.js +1 -0
- package/core/built/admin/assets/combobox-uX5r_paI.js +1 -0
- package/core/built/admin/assets/command-DxwBO2pF.js +1 -0
- package/core/built/admin/assets/comments-Dn-RO-nU.js +1 -0
- package/core/built/admin/assets/components-CwL6a6As-B0E0-NBJ.js +1 -0
- package/core/built/admin/assets/confirmation-modal-B6D6mk6R.js +1 -0
- package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
- package/core/built/admin/assets/content-api-modal-BxQ-7T6u.js +1 -0
- package/core/built/admin/assets/copy-field-BAxL4Zqn.js +1 -0
- package/core/built/admin/assets/currency-Dy4Tc2R2.js +1 -0
- package/core/built/admin/assets/custom-integration-modal---d_l7lc.js +1 -0
- package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
- package/core/built/admin/assets/{date-range-select-BFp58EMk.js → date-range-select-PB2n1HZf.js} +1 -1
- package/core/built/admin/assets/debounce-Dg2fW5Rg.js +3 -0
- package/core/built/admin/assets/design-and-theme-modal-DYRT207a.js +23 -0
- package/core/built/admin/assets/dirty-confirm-dialog-BnDJ39ao.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-iMHrkyV3.js +1 -0
- package/core/built/admin/assets/dist-B2PvRwLn.js +1 -0
- package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
- package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS02.js} +1 -1
- package/core/built/admin/assets/{dist-pHiOxyxa.js → dist-D5vhMdXf.js} +1 -1
- package/core/built/admin/assets/dist-DlCyLrOC.js +1 -0
- package/core/built/admin/assets/dist-WeOIvSYM.js +23 -0
- package/core/built/admin/assets/{dist-DyViJwIy.js → dist-cFzKLG7y.js} +3 -3
- 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-BR8FIdjD.js +1 -0
- package/core/built/admin/assets/{dropzone-DlK-K2DQ.js → dropzone-hNxmUvwD.js} +1 -1
- package/core/built/admin/assets/{edit-profile-BKT-REJw.js → edit-profile-CENrkY0l.js} +1 -1
- package/core/built/admin/assets/editor-B18D0lm8.js +8 -0
- package/core/built/admin/assets/embed-signup-form-modal-BsCYUMvQ.js +9 -0
- package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
- package/core/built/admin/assets/{explore-ClUpJZOp.js → explore-BmrnljWw.js} +1 -1
- package/core/built/admin/assets/{feed-DF92dUMP.js → feed-Dy4li6i8.js} +1 -1
- package/core/built/admin/assets/{field-Do7lieBA.js → field-Dh_lwk13.js} +1 -1
- package/core/built/admin/assets/filter-sources-D2zDuJH5.js +1 -0
- package/core/built/admin/assets/filters-3JBQcNtJ.js +1 -0
- package/core/built/admin/assets/filters-CFt_BDe4.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-D6Rjxugw.js +1 -0
- package/core/built/admin/assets/format-url-C-t8G6Qd.js +1 -0
- package/core/built/admin/assets/{ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
- package/core/built/admin/assets/{ghost-c4057474943effc568c5659ccc4909d7.js → ghost-d683fbeb945b13155ea669caa5d8db41.js} +420 -398
- package/core/built/admin/assets/{ghost-dark-db36d795d21c61426837df40af270966.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-f9VIMNDv.js → gift-link-modal-zMtlJhLQ.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-B949LDXJ.js +1 -0
- package/core/built/admin/assets/grid-CUciBfjf.js +1 -0
- package/core/built/admin/assets/{growth-CVw1My4S.js → growth-C38Xtp8D.js} +2 -2
- package/core/built/admin/assets/{growth-B3I6gNLm.js → growth-CFws67_i.js} +1 -1
- package/core/built/admin/assets/history-modal-ILAOkbxH.js +1 -0
- package/core/built/admin/assets/{hooks-CixO01lL.js → hooks-D6EE7BIt.js} +2 -2
- package/core/built/admin/assets/html-field-9a9Id_2q.js +1 -0
- package/core/built/admin/assets/iframe-buffering-e8vH4-Qn.js +1 -0
- package/core/built/admin/assets/images-CrQKVc9Q.js +1 -0
- package/core/built/admin/assets/{inbox-CFBSu4Ed.js → inbox-BrSRzyvz.js} +6 -6
- package/core/built/admin/assets/index-BHy2DZX-.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/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
- package/core/built/admin/assets/input-group-6vEPvX9S.js +1 -0
- package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.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-5xDCEw90.js +1 -0
- package/core/built/admin/assets/isEmail-BOfC647y.js +1 -0
- package/core/built/admin/assets/koenig-editor-base-BWLEB9Vf.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9535 -9527
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
- package/core/built/admin/assets/{koenig-lexical-f8vHRYvz.js → koenig-lexical-Cj62r_y9.js} +86 -86
- package/core/built/admin/assets/{kpi-card-DSap9rAy.js → kpi-card-DOUnkZcN.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DU0rn9Cj.js → kpi-card-IHDo8Q56.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-Dz33AV6h.js → kpi-helpers-CjUdECAK.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BtTTrKvJ.js → kpi-tabs-X3DkOYbI.js} +1 -1
- package/core/built/admin/assets/{label-DS8Mi6G5.js → label-Dq6QaOuS.js} +1 -1
- package/core/built/admin/assets/label-picker-8YlhS2U3.js +1 -0
- package/core/built/admin/assets/labels-DAPjKPy2.js +1 -0
- package/core/built/admin/assets/layout-TMj4P-vv.js +1 -0
- package/core/built/admin/assets/limit-modal-BRPsit_B.js +1 -0
- package/core/built/admin/assets/link-helpers-Bzkk68GD.js +1 -0
- package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-CmvsuuV_.js +1 -0
- package/core/built/admin/assets/member-detail-DCvRrkmJ.js +1 -0
- package/core/built/admin/assets/members-DxbSKngD.js +11 -0
- package/core/built/admin/assets/modal-page-BMTzFHVO.js +1 -0
- package/core/built/admin/assets/{moderation-DySbgyUS.js → moderation-Da5lhhWh.js} +1 -1
- package/core/built/admin/assets/multi-select-combobox-CYdkH7KQ.js +1 -0
- package/core/built/admin/assets/navigation-modal-BIoTwtMy.js +1 -0
- package/core/built/admin/assets/newsletter-BptlVoJm.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-BIvxqE0N.js +1 -0
- package/core/built/admin/assets/newsletter-emails-Bf8IZLsP.js +1 -0
- package/core/built/admin/assets/newsletters-BgrIZk0V.js +1 -0
- package/core/built/admin/assets/{newsletters-BXJbJgA-.js → newsletters-Du0lzskS.js} +1 -1
- package/core/built/admin/assets/{note-C8kxrz6w.js → note-DYcLOmDb.js} +1 -1
- package/core/built/admin/assets/{notifications-CgtSfX7t.js → notifications-B2SPFIV9.js} +1 -1
- package/core/built/admin/assets/{offers-DEniVRD5.js → offers-Um6ab71O.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BZnZITMw.js +1 -0
- package/core/built/admin/assets/{onboarding-route-4qtziA9d.js → onboarding-route-DgdfxkQh.js} +1 -1
- package/core/built/admin/assets/overview-BYQJXSog.js +1 -0
- package/core/built/admin/assets/overview-Dt2_mR6U.js +1 -0
- package/core/built/admin/assets/{page-header-B3Wb9Jqv.js → page-header-CpmxJj5B.js} +1 -1
- package/core/built/admin/assets/{pages-B4goRPUn.js → pages-BVMnpI2O.js} +1 -1
- package/core/built/admin/assets/pintura-modal-B4MuBuuO.js +1 -0
- package/core/built/admin/assets/portal-frame-BF0NBIji.js +1 -0
- package/core/built/admin/assets/portal-modal-DMVbm0oy.js +1 -0
- package/core/built/admin/assets/{post-analytics-We-ko5Cb.js → post-analytics-CXWBOGyP.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-ChzjXVPt.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-B6UynTGb.js → post-analytics-provider-Cgw4U_fw.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DFfO93_0.js → post-share-modal-B9Vmk_XM.js} +1 -1
- package/core/built/admin/assets/{posts-DhXE7b6r.js → posts-CxG7v0-M.js} +1 -1
- package/core/built/admin/assets/preferences-BfzJmnsd.js +5 -0
- package/core/built/admin/assets/preview-chrome-BOVO6_PE.js +1 -0
- package/core/built/admin/assets/preview-modal-Dw_HtD6S.js +1 -0
- package/core/built/admin/assets/profile-BT5vtv9b.js +1 -0
- package/core/built/admin/assets/radio-group-I7ou7PoJ.js +1 -0
- package/core/built/admin/assets/{referrers-pmJT4k3-.js → referrers-B9QDAfYm.js} +1 -1
- package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-DSQfprxH.js} +1 -1
- package/core/built/admin/assets/select-D9oU1IoM.js +1 -0
- package/core/built/admin/assets/settings-CfZFbyu_.js +4 -0
- package/core/built/admin/assets/{settings-WXNeWyU1.js → settings-CpvHWix7.js} +1 -1
- package/core/built/admin/assets/settings-breadcrumbs-K0USP2nw.js +1 -0
- package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
- package/core/built/admin/assets/{share-modal-DsgW3wbg.js → share-modal-ClIyPzc5.js} +1 -1
- package/core/built/admin/assets/slack-modal-1XXqOfiw.js +1 -0
- package/core/built/admin/assets/social-urls-CHQ9cTAK.js +1 -0
- package/core/built/admin/assets/{sort-button-Bb_FCl7e.js → sort-button-CdBsm40v.js} +1 -1
- package/core/built/admin/assets/{source-icon-BeaRxUn9.js → source-icon-CEYmVLXR.js} +1 -1
- package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
- package/core/built/admin/assets/{stats-Bjj_kmJ1.js → stats-D8nZCTy5.js} +1 -1
- package/core/built/admin/assets/{stats-view-BxPbW6K1.js → stats-view-CugFVf0g.js} +1 -1
- package/core/built/admin/assets/{step-1-B1e3-1F8.js → step-1-DOYjrORn.js} +1 -1
- package/core/built/admin/assets/{step-2-C5WaeBJo.js → step-2-vA6RsarH.js} +1 -1
- package/core/built/admin/assets/{step-3-B46iVPwu.js → step-3-BtIBxMId.js} +2 -2
- package/core/built/admin/assets/stripe-button-UFP4QRXd.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-C_L3uM6n.js +1 -0
- package/core/built/admin/assets/switch-CE2NuMn-.js +1 -0
- package/core/built/admin/assets/{table-CB-4_HBJ.js → table-TMCgCQK7.js} +1 -1
- package/core/built/admin/assets/tabs-Dd6oOKbv.js +1 -0
- package/core/built/admin/assets/tag-detail-D3l-UJPR.js +1 -0
- package/core/built/admin/assets/tags-BwCgmr5v.js +1 -0
- package/core/built/admin/assets/tags-WdKQGWR8.js +1 -0
- package/core/built/admin/assets/testimonials-modal-CqWp-xXJ.js +1 -0
- package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
- package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-BkcR2u0A.js +1 -0
- package/core/built/admin/assets/{tiers-DYsa4DCo.js → tiers-Da6kxHQP.js} +1 -1
- package/core/built/admin/assets/toggle-group-Guorw93z.js +1 -0
- package/core/built/admin/assets/{topic-filter-DAFT87tQ.js → topic-filter-CbgoWIHn.js} +1 -1
- package/core/built/admin/assets/transistor-modal-DuNnnpal.js +1 -0
- package/core/built/admin/assets/unsplash-modal-y2bVwTHH.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-S0FTd43s.js → use-analytics-data-B-b4wNSC.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-m09J38Mq.js +2 -0
- package/core/built/admin/assets/use-feature-flag-ud0Qz8vt.js +1 -0
- package/core/built/admin/assets/{use-filter-params-Gpoq2E1L.js → use-filter-params-BnbBDc7T.js} +1 -1
- 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-CIaiHOVb.js → use-growth-stats-DPU5rt3W.js} +1 -1
- package/core/built/admin/assets/use-hash-link-navigation-guard-B9j6HIrV.js +1 -0
- package/core/built/admin/assets/use-limiter-BkN3E4gw.js +2 -0
- package/core/built/admin/assets/use-pintura-config-C_z8HTG2.js +1 -0
- package/core/built/admin/assets/use-pintura-editor-DNgbkhGp.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-Cq2vBPrA.js → use-post-referrers-C2mXyrp-.js} +1 -1
- package/core/built/admin/assets/use-query-params-DHhhu8wi.js +1 -0
- package/core/built/admin/assets/{use-reply-chain-data-DBDRtGu-.js → use-reply-chain-data-DaK8q2qQ.js} +1 -1
- package/core/built/admin/assets/use-setting-group-YlYcoNyP.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-Bf1g1rEh.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-CjYNxdwe.js → use-simple-pagination-D9ckKo5-.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-falHAtuK.js → use-tinybird-query-GSa32hVw.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-D7-8ND1y.js → use-tinybird-token-CKLOZFsw.js} +1 -1
- package/core/built/admin/assets/use-url-input-z-p0Kewm.js +5 -0
- package/core/built/admin/assets/user-detail-modal-BMxOA4Sv.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-CvnC9zF2.js → virtual-list-RHeEL05L.js} +1 -1
- package/core/built/admin/assets/{web-jFYWCKDn.js → web-Bw2QMO5u.js} +2 -2
- package/core/built/admin/assets/web-xJtcwMy4.js +1 -0
- package/core/built/admin/assets/zapier-modal-BP2f4-vY.js +1 -0
- package/core/built/admin/index.html +20 -18
- 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/data/fetch-data.js +10 -5
- package/core/frontend/services/proxy.js +1 -1
- package/core/frontend/services/routing/api-adapter.js +115 -0
- package/core/frontend/services/routing/api-adapter.ts +157 -0
- package/core/frontend/services/routing/collection-router.js +5 -3
- package/core/frontend/services/routing/controllers/index.js +1 -1
- package/core/frontend/services/routing/controllers/rss.js +26 -31
- package/core/frontend/services/routing/controllers/rss.ts +66 -0
- package/core/frontend/services/routing/controllers/static.js +8 -7
- package/core/frontend/services/routing/events.js +20 -6
- package/core/frontend/services/routing/parent-router.js +17 -4
- package/core/frontend/services/routing/permalink-adapter.js +13 -0
- package/core/frontend/services/routing/permalink-adapter.ts +10 -0
- package/core/frontend/services/routing/registry.js +59 -33
- package/core/frontend/services/routing/router-manager.js +24 -22
- package/core/frontend/services/routing/static-routes-router.js +4 -4
- package/core/frontend/services/routing/taxonomy-router.js +6 -2
- 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/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/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/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.56/2026-08-03-19-27-51-add-automation-run-step-id-to-automated-email-recipients.js +8 -0
- package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
- 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/fixtures/fixtures.json +2 -2
- package/core/server/data/schema/schema.js +25 -5
- package/core/server/lib/common/to-plain.ts +1 -1
- package/core/server/models/post.js +12 -3
- 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 +24 -0
- package/core/server/services/adapter-manager/adapter-paths.ts +21 -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 +1 -1
- package/core/server/services/audience-feedback/audience-feedback-service.js +2 -2
- package/core/server/services/automations/automations-repository.ts +3 -1
- package/core/server/services/automations/database-automations-repository.js +33 -14
- package/core/server/services/automations/database-automations-repository.ts +41 -15
- package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
- package/core/server/services/automations/poll.js +3 -1
- package/core/server/services/automations/poll.ts +4 -1
- package/core/server/services/automations/service.js +7 -3
- package/core/server/services/automations/service.ts +7 -3
- 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 +11 -2
- package/core/server/services/email-analytics/email-analytics-service.js +1 -1
- 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/gift-links/CONTEXT.md +9 -0
- package/core/server/services/gifts/CONTEXT.md +55 -0
- package/core/server/services/gifts/README.md +28 -0
- package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
- package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
- package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
- package/core/server/services/gifts/gift-checkout-offer.js +74 -0
- package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
- package/core/server/services/gifts/gift-codec.js +63 -0
- package/core/server/services/gifts/gift-codec.ts +62 -0
- package/core/server/services/gifts/gift-controller.js +6 -31
- package/core/server/services/gifts/gift-controller.ts +6 -69
- package/core/server/services/gifts/gift-email-service.ts +3 -2
- package/core/server/services/gifts/gift-schema.js +34 -0
- package/core/server/services/gifts/gift-schema.ts +39 -0
- package/core/server/services/gifts/gift-service-wrapper.js +13 -11
- package/core/server/services/gifts/gift-service.js +265 -52
- package/core/server/services/gifts/gift-service.ts +431 -97
- package/core/server/services/gifts/gift.ts +16 -39
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +1 -1
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +2 -4
- package/core/server/services/lib/mailgun-client.js +3 -3
- package/core/server/services/link-tracking/link-click-repository.js +6 -1
- package/core/server/services/link-tracking/link-click-tracking-service.js +12 -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/member-welcome-email-renderer.js +4 -3
- package/core/server/services/member-welcome-emails/service.js +79 -15
- package/core/server/services/members/api.js +2 -3
- 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 +27 -6
- package/core/server/services/members/import-export/import/row.ts +5 -1
- package/core/server/services/members/import-export/index.js +6 -2
- package/core/server/services/members/import-export/index.ts +10 -4
- package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
- package/core/server/services/members/members-api/members-api.js +13 -7
- package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
- package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
- package/core/server/services/members/members-api/services/payments-service.js +6 -68
- 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 +19 -21
- package/core/server/services/members-custom-fields/schema.ts +18 -20
- 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 -103
- package/core/server/services/members-custom-fields/values-service.ts +116 -114
- package/core/server/services/mentions/resource-service.js +1 -1
- package/core/server/services/mentions/service.js +2 -2
- package/core/server/services/oembed/oembed-service.js +44 -3
- package/core/server/services/posts/posts-service.js +11 -0
- package/core/server/services/route-settings/dynamic-routing-service.js +10 -62
- package/core/server/services/route-settings/route-settings-parser.js +25 -25
- package/core/server/services/route-settings/route-settings-parser.ts +27 -27
- package/core/server/services/route-settings/validation-errors.js +68 -4
- package/core/server/services/route-settings/validation-errors.ts +79 -4
- package/core/server/services/slack-ping/slack-ping-service.js +2 -2
- package/core/server/services/slack-ping/slack-ping-service.ts +3 -5
- package/core/server/services/stats/content-stats-service.js +3 -3
- package/core/server/services/stats/posts-stats-service.js +29 -25
- package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
- package/core/server/services/stripe/stripe-api.js +10 -0
- 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 +2 -1
- package/core/server/web/gift-preview/controller.js +10 -25
- package/core/shared/config/env/config.testing-mysql.json +1 -2
- package/core/shared/config/env/config.testing.json +1 -2
- package/core/shared/events/index.js +1 -2
- package/core/shared/labs.js +4 -4
- package/package.json +10 -10
- package/pnpm-lock.yaml +27 -20
- package/pnpm-workspace.yaml +3 -2
- package/tsconfig.json +1 -0
- package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
- package/core/built/admin/assets/Combination-CJG7Flp-.js +0 -41
- package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +0 -1
- package/core/built/admin/assets/automated-emails-DRy7qY-J.js +0 -7
- package/core/built/admin/assets/checkbox-CWa28lgO.js +0 -1
- package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +0 -25
- package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +0 -27
- package/core/built/admin/assets/code-editor-view-CCr0rBWS.js +0 -1
- package/core/built/admin/assets/comments-DyUSMUdj.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-CUxMfiPa.js +0 -1
- package/core/built/admin/assets/dist-2Tnz2Yfq.js +0 -1
- package/core/built/admin/assets/dist-CjY-f5Z1.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/domain-C0bV2UV8.js +0 -1
- package/core/built/admin/assets/editor-Dp865O-4.js +0 -8
- package/core/built/admin/assets/esm-CFX93H5M.js +0 -2
- package/core/built/admin/assets/filter-sources-BV2rnpfA.js +0 -1
- package/core/built/admin/assets/filters-CUXJ2Nm9.js +0 -1
- package/core/built/admin/assets/filters-DfbIWc_l.js +0 -1
- package/core/built/admin/assets/index-9Cw8z3rA.css +0 -1
- package/core/built/admin/assets/index-BGG7NckO.js +0 -67
- package/core/built/admin/assets/input-group-BSgG_lVw.js +0 -1
- package/core/built/admin/assets/label-picker-1DvSuLA1.js +0 -1
- package/core/built/admin/assets/labels-CDSUt4J_.js +0 -1
- package/core/built/admin/assets/layout-yQEi2qDM.js +0 -1
- package/core/built/admin/assets/link-helpers-7EYqvvHs.js +0 -1
- package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +0 -1
- package/core/built/admin/assets/member-detail-BAq7gDaa.js +0 -1
- package/core/built/admin/assets/members-CQpVFJu4.js +0 -10
- package/core/built/admin/assets/modals-ReORvWnu.js +0 -31
- package/core/built/admin/assets/newsletter-CJQvtHqN.js +0 -1
- package/core/built/admin/assets/newsletters-B3QQXfGT.js +0 -1
- package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
- package/core/built/admin/assets/overview-Bf2sbznq.js +0 -1
- package/core/built/admin/assets/overview-CBfePxcr.js +0 -1
- package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +0 -1
- package/core/built/admin/assets/preferences-DJPfHEGu.js +0 -5
- package/core/built/admin/assets/profile-DqBQp-gT.js +0 -1
- package/core/built/admin/assets/select-C5_xEt5b.js +0 -1
- package/core/built/admin/assets/settings-BwiVIXau.js +0 -4
- package/core/built/admin/assets/social-urls-BQPFEk1_.js +0 -8
- package/core/built/admin/assets/switch-z5Y6Uzt6.js +0 -1
- package/core/built/admin/assets/tabs-CCEgKYzq.js +0 -1
- package/core/built/admin/assets/tags-Cg2VJGmG.js +0 -1
- package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
- package/core/built/admin/assets/toggle-group-b3IlxvhV.js +0 -1
- package/core/built/admin/assets/web-B_5GZRvH.js +0 -1
- package/core/server/services/gifts/gift-repository.js +0 -2
- package/core/server/services/gifts/gift-repository.ts +0 -29
- package/core/server/services/route-settings/activation-bridge.js +0 -174
- package/core/server/services/route-settings/activation-bridge.ts +0 -250
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {GIFT_EXPIRY_DAYS} from './constants';
|
|
2
|
+
import type {GiftCadence, GiftData, GiftStatus} from './gift-schema';
|
|
2
3
|
|
|
3
|
-
export type GiftStatus
|
|
4
|
-
export type GiftCadence = 'month' | 'year';
|
|
4
|
+
export type {GiftCadence, GiftStatus} from './gift-schema';
|
|
5
5
|
|
|
6
6
|
export type RedeemableCheckFailureReason = 'redeemed' | 'consumed' | 'expired' | 'refunded' | 'paid-member';
|
|
7
7
|
export type RedeemableCheckResult =
|
|
@@ -13,43 +13,20 @@ export type ReassignableCheckResult =
|
|
|
13
13
|
| {reassignable: true}
|
|
14
14
|
| {reassignable: false; reason: ReassignableCheckFailureReason};
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
token
|
|
18
|
-
buyerEmail
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
status: GiftStatus;
|
|
31
|
-
purchasedAt: Date;
|
|
32
|
-
redeemedAt: Date | null;
|
|
33
|
-
consumedAt: Date | null;
|
|
34
|
-
expiredAt: Date | null;
|
|
35
|
-
refundedAt: Date | null;
|
|
36
|
-
consumesSoonReminderSentAt: Date | null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface GiftFromPurchaseData {
|
|
40
|
-
token: string;
|
|
41
|
-
buyerEmail: string;
|
|
42
|
-
buyerMemberId: string | null;
|
|
43
|
-
tierId: string;
|
|
44
|
-
cadence: GiftCadence;
|
|
45
|
-
duration: number;
|
|
46
|
-
currency: string;
|
|
47
|
-
amount: number;
|
|
48
|
-
stripeCheckoutSessionId: string;
|
|
49
|
-
stripePaymentIntentId: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export class Gift {
|
|
16
|
+
export type GiftFromPurchaseData = Pick<GiftData,
|
|
17
|
+
| 'token'
|
|
18
|
+
| 'buyerEmail'
|
|
19
|
+
| 'buyerMemberId'
|
|
20
|
+
| 'tierId'
|
|
21
|
+
| 'cadence'
|
|
22
|
+
| 'duration'
|
|
23
|
+
| 'currency'
|
|
24
|
+
| 'amount'
|
|
25
|
+
| 'stripeCheckoutSessionId'
|
|
26
|
+
| 'stripePaymentIntentId'
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
export class Gift implements GiftData {
|
|
53
30
|
token: string;
|
|
54
31
|
buyerEmail: string;
|
|
55
32
|
buyerMemberId: string | null;
|
|
@@ -94,7 +94,7 @@ class IndexNowPingService {
|
|
|
94
94
|
// publishing.
|
|
95
95
|
let url = null;
|
|
96
96
|
try {
|
|
97
|
-
url = this.urlService.
|
|
97
|
+
url = this.urlService.getUrlForResource({ ...post, type: 'posts' }, { absolute: true });
|
|
98
98
|
if (!url || url.endsWith('/404/')) {
|
|
99
99
|
this.logging.warn({
|
|
100
100
|
event: { name: 'indexnow.unresolved_url' },
|
|
@@ -44,9 +44,7 @@ type Labs = {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
type UrlService = {
|
|
47
|
-
|
|
48
|
-
getUrlForResource(resource: Record<string, unknown>, options: {absolute: boolean}): string | null;
|
|
49
|
-
};
|
|
47
|
+
getUrlForResource(resource: Record<string, unknown>, options: {absolute: boolean}): string | null;
|
|
50
48
|
};
|
|
51
49
|
|
|
52
50
|
type UrlUtils = {
|
|
@@ -170,7 +168,7 @@ export class IndexNowPingService {
|
|
|
170
168
|
// publishing.
|
|
171
169
|
let url: string | null = null;
|
|
172
170
|
try {
|
|
173
|
-
url = this.urlService.
|
|
171
|
+
url = this.urlService.getUrlForResource({...post, type: 'posts'}, {absolute: true});
|
|
174
172
|
|
|
175
173
|
if (!url || url.endsWith('/404/')) {
|
|
176
174
|
this.logging.warn({
|
|
@@ -85,9 +85,9 @@ module.exports = class MailgunClient {
|
|
|
85
85
|
messageData['v:email-id'] = message.id;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
const tags = ['
|
|
89
|
-
if (
|
|
90
|
-
tags.push(
|
|
88
|
+
const tags = ['ghost-email'];
|
|
89
|
+
if (Array.isArray(message.tags)) {
|
|
90
|
+
tags.push(...message.tags);
|
|
91
91
|
}
|
|
92
92
|
messageData['o:tag'] = tags;
|
|
93
93
|
|
|
@@ -32,7 +32,12 @@ module.exports = class LinkClickRepository {
|
|
|
32
32
|
|
|
33
33
|
// Memoize the findOne function
|
|
34
34
|
this.memoizedFindOne = _.memoize(async (uuid, options) => {
|
|
35
|
-
|
|
35
|
+
try {
|
|
36
|
+
return await this.#Member.findOne({uuid}, options);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
this.memoizedFindOne.cache.delete(uuid);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
36
41
|
});
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -4,6 +4,7 @@ const PostLink = require('./post-link');
|
|
|
4
4
|
const ObjectID = require('bson-objectid').default;
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
6
|
const nql = require('@tryghost/nql');
|
|
7
|
+
const {escapeNqlString} = require('@tryghost/nql-string');
|
|
7
8
|
const _ = require('lodash');
|
|
8
9
|
const tpl = require('@tryghost/tpl');
|
|
9
10
|
const moment = require('moment');
|
|
@@ -174,7 +175,11 @@ class LinkClickTrackingService {
|
|
|
174
175
|
|
|
175
176
|
// manages transformation of current url to relative for comparision
|
|
176
177
|
const transformedOldUrl = this.#urlUtils.absoluteToTransformReady(redirectUrl.href);
|
|
177
|
-
|
|
178
|
+
// the url is re-serialised into NQL here, so it needs escaping again -
|
|
179
|
+
// quotes are legal in a URL and would otherwise break the filter.
|
|
180
|
+
// postId is String()d because nql parses an unquoted `post_id:123` to a
|
|
181
|
+
// number, and escapeNqlString operates on strings
|
|
182
|
+
const filterQuery = `post_id:${escapeNqlString(String(postId))}+to:${escapeNqlString(transformedOldUrl)}`;
|
|
178
183
|
|
|
179
184
|
const updatedFilterOptions = {
|
|
180
185
|
...filterOptions,
|
|
@@ -245,13 +250,15 @@ class LinkClickTrackingService {
|
|
|
245
250
|
* Add first-party tracking to a URL in an automation email
|
|
246
251
|
* @param {Readonly<URL>} url
|
|
247
252
|
* @param {string} automationActionRevisionId
|
|
253
|
+
* @param {string} automationRunStepId
|
|
248
254
|
* @param {string} memberUuid
|
|
249
255
|
* @return {Promise<URL>}
|
|
250
256
|
*/
|
|
251
|
-
async addAutomationTrackingToUrl(url, automationActionRevisionId, memberUuid) {
|
|
257
|
+
async addAutomationTrackingToUrl(url, automationActionRevisionId, automationRunStepId, memberUuid) {
|
|
252
258
|
const redirect = await this.#linkRedirectService.getOrAddAutomationRedirect(automationActionRevisionId, url);
|
|
253
259
|
const trackedUrl = new URL(redirect.from.href);
|
|
254
260
|
trackedUrl.searchParams.set('m', memberUuid);
|
|
261
|
+
trackedUrl.searchParams.set('step', automationRunStepId);
|
|
255
262
|
return trackedUrl;
|
|
256
263
|
}
|
|
257
264
|
|
|
@@ -269,7 +276,8 @@ class LinkClickTrackingService {
|
|
|
269
276
|
});
|
|
270
277
|
|
|
271
278
|
const automationActionRevisionId = event.data.link.automationActionRevisionId;
|
|
272
|
-
|
|
279
|
+
const automationRunStepId = event.data.url.searchParams.get('step');
|
|
280
|
+
if (!automationActionRevisionId || !automationRunStepId) {
|
|
273
281
|
await this.#linkClickRepository.save(click);
|
|
274
282
|
return;
|
|
275
283
|
}
|
|
@@ -282,6 +290,7 @@ class LinkClickTrackingService {
|
|
|
282
290
|
|
|
283
291
|
await this.#automationsApi.trackEmailClicked({
|
|
284
292
|
automationActionRevisionId,
|
|
293
|
+
automationRunStepId,
|
|
285
294
|
memberId,
|
|
286
295
|
clickedAt: event.timestamp
|
|
287
296
|
}, {transacting});
|
|
@@ -5,7 +5,6 @@ const errors = require('@tryghost/errors');
|
|
|
5
5
|
const logging = require('@tryghost/logging');
|
|
6
6
|
const string = require('@tryghost/string');
|
|
7
7
|
const path = require('path');
|
|
8
|
-
const convert = require('heic-convert');
|
|
9
8
|
|
|
10
9
|
let fileTypeFromBuffer;
|
|
11
10
|
|
|
@@ -102,20 +101,25 @@ class ExternalMediaInliner {
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
// If the file is heic or heif, attempt to convert it to jpeg
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
if (extension === 'heic' || extension === 'heif') {
|
|
105
|
+
// Lazy: pulls in libheif-js, a WASM codec costing ~50ms to load at boot.
|
|
106
|
+
// Deliberately outside the try — a missing codec is not a conversion error,
|
|
107
|
+
// and swallowing it would store an unconvertible .heic in its place.
|
|
108
|
+
const convert = require('heic-convert');
|
|
109
|
+
|
|
110
|
+
try {
|
|
107
111
|
body = await convert({
|
|
108
112
|
buffer: body,
|
|
109
113
|
format: 'JPEG'
|
|
110
114
|
});
|
|
111
115
|
|
|
112
116
|
extension = 'jpg';
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logging.error(`Error converting file to JPEG: ${requestURL}`);
|
|
119
|
+
logging.error(new errors.DataImportError({
|
|
120
|
+
err: error
|
|
121
|
+
}));
|
|
113
122
|
}
|
|
114
|
-
} catch (error) {
|
|
115
|
-
logging.error(`Error converting file to JPEG: ${requestURL}`);
|
|
116
|
-
logging.error(new errors.DataImportError({
|
|
117
|
-
err: error
|
|
118
|
-
}));
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
const removeExtRegExp = new RegExp(`.${extension}`, '');
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {Object} UrlService
|
|
3
|
-
* @prop {
|
|
4
|
-
*
|
|
5
|
-
* resolveUrl: (path: string) => Promise<Object | null>
|
|
6
|
-
* }} facade
|
|
3
|
+
* @prop {(resource: Object, options: Object) => string} getUrlForResource
|
|
4
|
+
* @prop {(path: string) => Promise<Object | null>} resolveUrl
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
const {toPlain} = require('../../lib/common/to-plain');
|
|
@@ -106,7 +104,7 @@ class UrlTranslator {
|
|
|
106
104
|
// surface as an error.
|
|
107
105
|
let resource;
|
|
108
106
|
try {
|
|
109
|
-
resource = await this.urlService.
|
|
107
|
+
resource = await this.urlService.resolveUrl(path);
|
|
110
108
|
} catch (err) {
|
|
111
109
|
return;
|
|
112
110
|
}
|
|
@@ -155,10 +153,10 @@ class UrlTranslator {
|
|
|
155
153
|
}
|
|
156
154
|
// Lazy URL service evaluates permalink templates against resource fields
|
|
157
155
|
// (slug, published_at, primary_tag, ...). Caller already loaded the model,
|
|
158
|
-
// so spread its plain data so those fields reach the
|
|
156
|
+
// so spread its plain data so those fields reach the URL service.
|
|
159
157
|
const data = toPlain(model);
|
|
160
158
|
const resource = {...data, id, type: TYPE_TO_RESOURCE[type]};
|
|
161
|
-
return this.urlService.
|
|
159
|
+
return this.urlService.getUrlForResource(resource, {absolute});
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
async getResourceById(id, type) {
|
|
@@ -119,9 +119,10 @@ class MemberWelcomeEmailRenderer {
|
|
|
119
119
|
* @param {string} [options.unsubscribeUrl] - When set, the footer shows an "Unsubscribe from these emails" link instead of "Manage your preferences"
|
|
120
120
|
* @param {boolean} [options.trackClicks]
|
|
121
121
|
* @param {string | null} [options.automationActionRevisionId]
|
|
122
|
+
* @param {string | null} [options.automationRunStepId]
|
|
122
123
|
* @returns {Promise<{html: string, text: string, subject: string}>}
|
|
123
124
|
*/
|
|
124
|
-
async render({lexical, subject, designSettings, member, siteSettings, unsubscribeUrl, trackClicks = false, automationActionRevisionId = null}) {
|
|
125
|
+
async render({lexical, subject, designSettings, member, siteSettings, unsubscribeUrl, trackClicks = false, automationActionRevisionId = null, automationRunStepId = null}) {
|
|
125
126
|
designSettings = designSettings || {};
|
|
126
127
|
|
|
127
128
|
const design = emailDesign.getEmailDesign({
|
|
@@ -171,9 +172,9 @@ class MemberWelcomeEmailRenderer {
|
|
|
171
172
|
return originalPath;
|
|
172
173
|
}
|
|
173
174
|
const isTrackable = ['http:', 'https:'].includes(url.protocol);
|
|
174
|
-
if (trackClicks && automationActionRevisionId && member.uuid && isTrackable) {
|
|
175
|
+
if (trackClicks && automationActionRevisionId && automationRunStepId && member.uuid && isTrackable) {
|
|
175
176
|
await linkTracking.init();
|
|
176
|
-
return await linkTracking.service.addAutomationTrackingToUrl(url, automationActionRevisionId, member.uuid);
|
|
177
|
+
return await linkTracking.service.addAutomationTrackingToUrl(url, automationActionRevisionId, automationRunStepId, member.uuid);
|
|
177
178
|
}
|
|
178
179
|
return url;
|
|
179
180
|
}, {base: siteSettings.url});
|
|
@@ -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,
|
|
@@ -370,10 +374,11 @@ class MemberWelcomeEmailService {
|
|
|
370
374
|
* @param {boolean} [options.trackOpens]
|
|
371
375
|
* @param {boolean} [options.trackClicks]
|
|
372
376
|
* @param {string | null} [options.automationActionRevisionId]
|
|
377
|
+
* @param {string | null} [options.automationRunStepId]
|
|
373
378
|
* @param {null | {url: string, oneClickUrl: string}} [options.unsubscribe] - When set, the footer links to an unsubscribe URL and the email carries one-click List-Unsubscribe headers
|
|
374
379
|
* @returns {Promise<unknown>}
|
|
375
380
|
*/
|
|
376
|
-
async #sendEmail({member, memberStatus, email, emailType, trackOpens, trackClicks = false, automationActionRevisionId = null, unsubscribe = null}) {
|
|
381
|
+
async #sendEmail({member, memberStatus, email, emailType, trackOpens, trackClicks = false, automationActionRevisionId = null, automationRunStepId = null, unsubscribe = null}) {
|
|
377
382
|
if (!member.email) {
|
|
378
383
|
throw new errors.IncorrectUsageError({
|
|
379
384
|
message: MESSAGES.MISSING_RECIPIENT_EMAIL
|
|
@@ -400,25 +405,24 @@ class MemberWelcomeEmailService {
|
|
|
400
405
|
siteSettings: this.#getSiteSettings(),
|
|
401
406
|
unsubscribeUrl: unsubscribe?.url,
|
|
402
407
|
trackClicks,
|
|
403
|
-
automationActionRevisionId
|
|
408
|
+
automationActionRevisionId,
|
|
409
|
+
automationRunStepId
|
|
404
410
|
});
|
|
405
411
|
|
|
406
412
|
const senderOptions = await this.#getEffectiveSenderOptions(
|
|
407
413
|
getSenderDetails(email.designSettings)
|
|
408
414
|
);
|
|
409
415
|
|
|
410
|
-
const headers = unsubscribe?.oneClickUrl ? {
|
|
411
|
-
'List-Unsubscribe': `<${unsubscribe.oneClickUrl}>`,
|
|
412
|
-
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
|
|
413
|
-
} : undefined;
|
|
414
|
-
|
|
415
416
|
/** @type {string[]} */ let tags;
|
|
417
|
+
let sendEmail;
|
|
416
418
|
switch (emailType) {
|
|
417
419
|
case 'welcome':
|
|
418
420
|
tags = [MEMBER_WELCOME_EMAIL_TAG];
|
|
421
|
+
sendEmail = this.#sendTransactionalEmail.bind(this);
|
|
419
422
|
break;
|
|
420
423
|
case 'automation':
|
|
421
424
|
tags = [AUTOMATION_EMAIL_TAG];
|
|
425
|
+
sendEmail = this.#sendBulkEmail.bind(this);
|
|
422
426
|
break;
|
|
423
427
|
default: {
|
|
424
428
|
/** @type {never} */ const _exhaustive = emailType;
|
|
@@ -428,19 +432,78 @@ class MemberWelcomeEmailService {
|
|
|
428
432
|
}
|
|
429
433
|
}
|
|
430
434
|
|
|
431
|
-
return await
|
|
435
|
+
return await sendEmail({
|
|
432
436
|
to: member.email,
|
|
433
437
|
subject,
|
|
434
438
|
html,
|
|
435
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,
|
|
436
471
|
forceTextContent: true,
|
|
437
472
|
tags,
|
|
438
473
|
...(headers ? {headers} : {}),
|
|
439
474
|
...(typeof trackOpens === 'boolean' ? {trackOpens} : {}),
|
|
440
|
-
|
|
475
|
+
from,
|
|
476
|
+
replyTo
|
|
441
477
|
});
|
|
442
478
|
}
|
|
443
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
|
+
|
|
444
507
|
async send({member, memberStatus}) {
|
|
445
508
|
const email = this.#memberWelcomeEmails[memberStatus];
|
|
446
509
|
|
|
@@ -480,9 +543,10 @@ class MemberWelcomeEmailService {
|
|
|
480
543
|
* @param {boolean} options.trackOpens
|
|
481
544
|
* @param {boolean} options.trackClicks
|
|
482
545
|
* @param {string} options.automationActionRevisionId
|
|
546
|
+
* @param {string} options.automationRunStepId
|
|
483
547
|
* @returns {Promise<unknown>}
|
|
484
548
|
*/
|
|
485
|
-
async sendAutomationEmail({email, member, memberStatus, trackOpens, trackClicks, automationActionRevisionId}) {
|
|
549
|
+
async sendAutomationEmail({email, member, memberStatus, trackOpens, trackClicks, automationActionRevisionId, automationRunStepId}) {
|
|
486
550
|
const designSettings = email.designSettingId ?
|
|
487
551
|
await EmailDesignSetting.findOne({id: email.designSettingId}) :
|
|
488
552
|
null;
|
|
@@ -510,6 +574,7 @@ class MemberWelcomeEmailService {
|
|
|
510
574
|
trackOpens,
|
|
511
575
|
trackClicks,
|
|
512
576
|
automationActionRevisionId,
|
|
577
|
+
automationRunStepId,
|
|
513
578
|
unsubscribe
|
|
514
579
|
});
|
|
515
580
|
}
|
|
@@ -647,12 +712,11 @@ class MemberWelcomeEmailService {
|
|
|
647
712
|
getSenderDetails(designSettingsJson)
|
|
648
713
|
);
|
|
649
714
|
|
|
650
|
-
await this.#
|
|
715
|
+
await this.#sendTransactionalEmail({
|
|
651
716
|
to: email,
|
|
652
717
|
subject: `[Test] ${renderedSubject}`,
|
|
653
718
|
html,
|
|
654
719
|
text,
|
|
655
|
-
forceTextContent: true,
|
|
656
720
|
...senderOptions
|
|
657
721
|
});
|
|
658
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),
|
|
@@ -244,8 +244,7 @@ function createApiInstance(config) {
|
|
|
244
244
|
EmailSpamComplaintEvent: models.EmailSpamComplaintEvent,
|
|
245
245
|
Automation: models.Automation,
|
|
246
246
|
WelcomeEmailAutomationRun: models.WelcomeEmailAutomationRun,
|
|
247
|
-
AutomatedEmailRecipient: models.AutomatedEmailRecipient
|
|
248
|
-
Gift: models.Gift
|
|
247
|
+
AutomatedEmailRecipient: models.AutomatedEmailRecipient
|
|
249
248
|
},
|
|
250
249
|
stripeAPIService: stripeService.api,
|
|
251
250
|
tiersService: tiersService,
|
|
@@ -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);
|