ghost 6.60.0 → 6.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/core/boot.js +13 -4
- package/core/built/admin/assets/{Combination-gnJO3vIw.js → Combination-5fYEzgbs.js} +1 -1
- package/core/built/admin/assets/{PolarAngleAxis-C4qtHy1B.js → PolarAngleAxis-pkMyqz8g.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-Bh09Stsj.js → RadialBarChart-JAKoS4KT.js} +1 -1
- package/core/built/admin/assets/{about-BgpmhCuM.js → about-Bve7IJS2.js} +1 -1
- package/core/built/admin/assets/{account-migration-8qTEI1i1.js → account-migration-DRhAehfw.js} +1 -1
- package/core/built/admin/assets/{action-list-lGzZLHc8.js → action-list-C4v0-du8.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DMHuPnJn.js → account-migration-C-aej4x_.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +3 -4
- package/core/built/admin/assets/activitypub/{avatar-DhTo5D1B.js → avatar-DLPL3Abs.js} +59 -68
- package/core/built/admin/assets/activitypub/{avatar-flipboard-DnVgbEfa.js → avatar-flipboard-C9iDY-QV.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-BT4zAlW7.js → bluesky-sharing-BXkA5mqV.js} +8 -8
- package/core/built/admin/assets/activitypub/{content-formatters-BeDZ-DIa.js → content-formatters-CHcyMFk4.js} +17 -17
- package/core/built/admin/assets/activitypub/{copy-8sqxUuNd.js → copy-DIAOQoCZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-xQDDu1CS.js → domain-B3ihkPde.js} +8 -8
- package/core/built/admin/assets/activitypub/{edit-profile-BXdF_r2G.js → edit-profile-DsZM3rTX.js} +5 -5
- package/core/built/admin/assets/activitypub/{explore-CMt7gKlL.js → explore-BodNrZgR.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-Bwqdddp_.js → feed-PwH1iJ84.js} +9 -9
- package/core/built/admin/assets/activitypub/{field-4co_8FVg.js → field-D06-kmOJ.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-DwakMs4X.js → hash-Bm3WxYN4.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DQ5CwLma.js → inbox-BluD2ucT.js} +16 -16
- package/core/built/admin/assets/activitypub/{inbox-ckD4wo_r.js → inbox-d8oJRTf4.js} +2 -2
- package/core/built/admin/assets/activitypub/{label-B72sfvEU.js → label-FHSh6ZAw.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-KCCO7L2J.js → moderation-D1Yapsod.js} +9 -9
- package/core/built/admin/assets/activitypub/{note-BX_Bz5A0.js → note-C5zjqq5C.js} +8 -8
- package/core/built/admin/assets/activitypub/{notifications-BQOYGvRv.js → notifications-B9p8eCHD.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-6h43G9_q.js → preferences-BCqUdC3v.js} +9 -9
- package/core/built/admin/assets/activitypub/{profile-BMN9VVZ2.js → profile-BRCWoFem.js} +178 -178
- package/core/built/admin/assets/activitypub/{reply-ZNo04xsO.js → reply-DYK5vVqb.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-3B9mUQ7i.js → routes-BSXT992i.js} +81 -84
- package/core/built/admin/assets/activitypub/{separator-D9taVCCa.js → separator-BbQqTm-p.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-71gCm3SO.js → settings-DQDkr06X.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-1-BsGGnBuT.js → step-1-B-YfsVeO.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-teosIS0k.js → step-2-DtPQzlTH.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-BjD5TokC.js → step-3-D6BFMXL_.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-Cvwl1wXU.js → tabs-GpNecvfB.js} +6 -6
- package/core/built/admin/assets/activitypub/{topic-filter-C3LZJc2X.js → topic-filter-BPDeQQys.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-Dp9f0gov.js → use-activity-pub-queries-BoVOxXkV.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-C4RI0sa9.js → use-navigate-with-base-path-Bmk5h4lW.js} +220 -220
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-C2oNDMUH.js → use-reply-chain-data-wVBX7swM.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-Dxm6pV9X.js → x-DbQZOLiI.js} +2 -2
- package/core/built/admin/assets/{add-integration-modal--YpTq3c0.js → add-integration-modal-BC0jExDZ.js} +1 -1
- package/core/built/admin/assets/{add-newsletter-modal-C8Ix2hol.js → add-newsletter-modal-DchTkA7l.js} +1 -1
- package/core/built/admin/assets/{add-recommendation-modal-CScuNFz4.js → add-recommendation-modal-DMvaJZKp.js} +1 -1
- package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +1 -0
- package/core/built/admin/assets/{analytics-Cnji45H5.js → analytics-No1DDY3A.js} +1 -1
- package/core/built/admin/assets/{announcement-bar-modal-BTHiy1ZO.js → announcement-bar-modal-DM3cYOpo.js} +1 -1
- package/core/built/admin/assets/{api-keys-Boy5pLbD.js → api-keys-BLm2NDBh.js} +1 -1
- package/core/built/admin/assets/audience-9mwU5TW6.js +1 -0
- package/core/built/admin/assets/{automations-BWpRCZ_f.js → automations-DpUNUdcZ.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-DHOpQxff.js → bluesky-sharing-Cjrv_f62.js} +1 -1
- package/core/built/admin/assets/{box-By_EgGtm.js → box-CdfQAP_F.js} +1 -1
- package/core/built/admin/assets/{brand-icon-09YqFZjV.js → brand-icon-CrIGOA3Z.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-CcFr_ltt.js → breadcrumb-DLiqCyFc.js} +1 -1
- package/core/built/admin/assets/{button-BshWPHvn.js → button-B2jcu6bk.js} +1 -1
- package/core/built/admin/assets/{card-CiiE7QfM.js → card--VNbfgo3.js} +1 -1
- package/core/built/admin/assets/{chart-BnXyfH11.js → chart-BrhA41Zi.js} +1 -1
- package/core/built/admin/assets/chart-helpers-Fo3bUL3K.js +1 -0
- package/core/built/admin/assets/{checkbox-Dwwr_3Py.js → checkbox-BfKktMph.js} +1 -1
- package/core/built/admin/assets/{chunk.524.91ecb33e6afc3bd8949b.js → chunk.524.60ca4692d91c6022042f.js} +4 -4
- package/core/built/admin/assets/{chunk.582.512549909741883f960c.js → chunk.582.525c7617c2d9e94c940a.js} +6 -6
- package/core/built/admin/assets/{chunk.16.f76a6314082d813d67e0.js → chunk.896.435fe0637944d4a69810.js} +3 -3
- package/core/built/admin/assets/{code-editor-view-BwIdPy_k.js → code-editor-view-CYVu4EW6.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-CtkF82Fv.js → code-editor-view-DCQJxsfU.js} +1 -1
- package/core/built/admin/assets/{color-picker-Bmo1r9Fc.js → color-picker-BTY_NdEm.js} +1 -1
- package/core/built/admin/assets/{color-picker-field-DywowPhF.js → color-picker-field-CPlkzOH0.js} +1 -1
- package/core/built/admin/assets/{combobox-C4QCh6Yw.js → combobox-DrMtv_4P.js} +1 -1
- package/core/built/admin/assets/command-ZdgYJpoI.js +1 -0
- package/core/built/admin/assets/comments-kidj60-h.js +1 -0
- package/core/built/admin/assets/{confirmation-context-CLFHhyyH.js → confirmation-context-BhbmQQEl.js} +1 -1
- package/core/built/admin/assets/{container-LiZf3XdY.js → container-BuQG1wuP.js} +1 -1
- package/core/built/admin/assets/{content-api-modal-BtpNI92-.js → content-api-modal-CyQEC3fr.js} +1 -1
- package/core/built/admin/assets/{copy-field-CglgRZyG.js → copy-field-B3M1s78L.js} +1 -1
- package/core/built/admin/assets/{custom-field-type-option-D0PXoSnP.js → custom-field-type-option-CsGIZk1J.js} +1 -1
- package/core/built/admin/assets/{custom-integration-modal-DCIHKN33.js → custom-integration-modal-BY37O_9X.js} +1 -1
- package/core/built/admin/assets/{data-list-CupF7bjM.js → data-list-CA2jYGPq.js} +1 -1
- package/core/built/admin/assets/{date-range-select-JPwM0yUj.js → date-range-select-DD2RKkgB.js} +1 -1
- package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +22 -0
- package/core/built/admin/assets/{detail-page-D8lx2SdP.js → detail-page-Dnv-CdR1.js} +1 -1
- package/core/built/admin/assets/dialog-B44iRwlc.js +1 -0
- package/core/built/admin/assets/{dirty-confirm-dialog-CmARpZQS.js → dirty-confirm-dialog-BxgQgQ6H.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-BTJ2ClQu.js → disabled-sources-indicator-Cvy47bE2.js} +1 -1
- package/core/built/admin/assets/{dist-DzcfoDJ3.js → dist-8YJUlf3B.js} +1 -1
- package/core/built/admin/assets/{dist-Cb5RSHSg.js → dist-B3keLvjj.js} +1 -1
- package/core/built/admin/assets/{dist-QLZuGq7o2.js → dist-Cy8tfrp_.js} +1 -1
- package/core/built/admin/assets/{dist-BT_Z-eCn.js → dist-Dgt2cr6a.js} +1 -1
- package/core/built/admin/assets/{domain-D0SAtc-T.js → domain-Ca6KhoQt.js} +1 -1
- package/core/built/admin/assets/{dropzone-CaPOeDRZ.js → dropzone-Bb-5zQTk.js} +1 -1
- package/core/built/admin/assets/edit-profile-BdupLlus.js +1 -0
- package/core/built/admin/assets/{editor-COXn4Atu.js → editor-BP883Lpn.js} +3 -3
- package/core/built/admin/assets/{embed-signup-form-modal-BFTPf04i.js → embed-signup-form-modal-DeBMLxlL.js} +1 -1
- package/core/built/admin/assets/{empty-indicator-WB1WgT9h.js → empty-indicator-D9QdpuNX.js} +1 -1
- package/core/built/admin/assets/{explore-BsNYrubV.js → explore-FB-CimXw.js} +1 -1
- package/core/built/admin/assets/{feed-8RR1O1bK.js → feed-C1GxsmI2.js} +1 -1
- package/core/built/admin/assets/{field-LmNk7wZh.js → field-Cj8lmDOo.js} +1 -1
- package/core/built/admin/assets/{filter-sources-4C0kF1NU.js → filter-sources-D5n5bFA2.js} +1 -1
- package/core/built/admin/assets/{filters-CV5jCb9n.js → filters-DJEzkMUK.js} +1 -1
- package/core/built/admin/assets/{filters-Dmr0PoqU.js → filters-MZXvdH5D.js} +1 -1
- package/core/built/admin/assets/{first-promoter-modal-OCzSaL3B.js → first-promoter-modal-CGTGLCr5.js} +1 -1
- package/core/built/admin/assets/{ghost-403cb39badad3df411fc0ba27d220047.js → ghost-3894eedcc6968f658578c0abfe942aab.js} +2 -2
- package/core/built/admin/assets/{gift-link-modal-0mzjn5oF.js → gift-link-modal-B6v0tnRN.js} +1 -1
- package/core/built/admin/assets/{grid-CUciBfjf.js → grid-Dj29PY05.js} +1 -1
- package/core/built/admin/assets/growth-BGAJD1rs.js +3 -0
- package/core/built/admin/assets/growth-D6mhcx3q.js +1 -0
- package/core/built/admin/assets/{heading-CRZZ2FUe.js → heading-Dh5SFVjL.js} +1 -1
- package/core/built/admin/assets/history-modal-iKwUY3XR.js +1 -0
- package/core/built/admin/assets/html-field-C8qVGGC1.js +1 -0
- package/core/built/admin/assets/{images-C0XvUYMb.js → images-h8VZEQ9k.js} +1 -1
- package/core/built/admin/assets/{inbox-DZXDUt4s.js → inbox-BYd1M06Q.js} +13 -13
- package/core/built/admin/assets/index-DjQe-e5s.js +4 -0
- package/core/built/admin/assets/{index.es-BMUCj9TR.js → index.es-VihPwfeH.js} +1 -1
- package/core/built/admin/assets/{inline-Cw-gigDW.js → inline-BWSs_VzR.js} +1 -1
- package/core/built/admin/assets/{input-UHrKuvBT.js → input--W-rCTrX.js} +1 -1
- package/core/built/admin/assets/{input-group-BPAxETpQ.js → input-group-D8sCXRF5.js} +1 -1
- package/core/built/admin/assets/{input-surface-BdOe4KzO.js → input-surface-BsrIZeSF.js} +1 -1
- package/core/built/admin/assets/{integration-header-B4DyXV06.js → integration-header-Bm6IDb1A.js} +1 -1
- package/core/built/admin/assets/{invite-user-modal-BrAkxEO3.js → invite-user-modal-MdImLCaJ.js} +1 -1
- package/core/built/admin/assets/{koenig-lexical-60O_8aiO.js → koenig-lexical-_-IsVI7E.js} +1 -1
- package/core/built/admin/assets/koenig-loader-DYZpm6El.js +2 -0
- package/core/built/admin/assets/{kpi-card-fUHZlzBX.js → kpi-card-DW_2y3ug.js} +1 -1
- package/core/built/admin/assets/kpi-card-DZQk_0W8.js +1 -0
- package/core/built/admin/assets/kpi-helpers-PueTKuA6.js +1 -0
- package/core/built/admin/assets/kpi-tabs-CojDs-Qz.js +1 -0
- package/core/built/admin/assets/{label-VIG969IC.js → label-DjbT91GM.js} +1 -1
- package/core/built/admin/assets/{label-picker-BBWykUeA.js → label-picker-cB51xl1r.js} +1 -1
- package/core/built/admin/assets/layout-c_B_1hax.js +1 -0
- package/core/built/admin/assets/{link-helpers-Dtq9fxQg.js → link-helpers-MVDdnFCY.js} +1 -1
- package/core/built/admin/assets/{list-page-CdsnQR9N.js → list-page-BucXJ0hi.js} +1 -1
- package/core/built/admin/assets/{loading-indicator-SFTqyYVE.js → loading-indicator-BpFWTVgh.js} +1 -1
- package/core/built/admin/assets/{member-custom-fields-gOv6Hoh3.js → member-custom-fields-qs0dIkxM.js} +1 -1
- package/core/built/admin/assets/member-detail-B6uec2a6.js +1 -0
- package/core/built/admin/assets/members-BQF3CqhI.js +2 -0
- package/core/built/admin/assets/{modal-page-BMTzFHVO.js → modal-page-xCivTMvI.js} +1 -1
- package/core/built/admin/assets/{moderation-DzzPLDyl.js → moderation-DY4wF1Q3.js} +1 -1
- package/core/built/admin/assets/{multi-select-combobox-CGohnsxJ.js → multi-select-combobox-B6LiRzDO.js} +1 -1
- package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +1 -0
- package/core/built/admin/assets/newsletter-CyQA7m3m.js +1 -0
- package/core/built/admin/assets/{newsletter-detail-modal-DQU3r4g2.js → newsletter-detail-modal-BDrptuxn.js} +1 -1
- package/core/built/admin/assets/{newsletter-emails-D5ql1rud.js → newsletter-emails-CFeLlt4z.js} +1 -1
- package/core/built/admin/assets/newsletters-DXuNmC42.js +1 -0
- package/core/built/admin/assets/{note-i8k6pjaw.js → note-Cgo2LCJ6.js} +1 -1
- package/core/built/admin/assets/notifications-BC50AMku.js +1 -0
- package/core/built/admin/assets/{offers-container-modal-BWfjSdyg.js → offers-container-modal-DWycIMzT.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-Cu_ROWPn.js → onboarding-route-Dmc4IPm3.js} +1 -1
- package/core/built/admin/assets/overview-CVbFtn-k.js +1 -0
- package/core/built/admin/assets/overview-YKj7SZaK.js +1 -0
- package/core/built/admin/assets/{page-header-Cfds8pCg.js → page-header-BR_nbIPp.js} +1 -1
- package/core/built/admin/assets/{pintura-modal-TDTrJ89x.js → pintura-modal-BR9hEvS8.js} +1 -1
- package/core/built/admin/assets/popover-CCy0h5rq.js +1 -0
- package/core/built/admin/assets/{portal-frame-DjBF-uy2.js → portal-frame-dc4XKr0e.js} +1 -1
- package/core/built/admin/assets/portal-modal-BvvdF_B9.js +1 -0
- package/core/built/admin/assets/{post-analytics-Buz1COoY.js → post-analytics-DX470WRG.js} +1 -1
- package/core/built/admin/assets/{post-analytics-header-B-P0eFnT.js → post-analytics-header-CXpJmdDv.js} +1 -1
- package/core/built/admin/assets/{post-analytics-provider-Cu3jNeFh.js → post-analytics-provider-CLfKGWZy.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-P1_M6o-H.js → post-share-modal-Cl0NXRJr.js} +1 -1
- package/core/built/admin/assets/{preferences-Cq1aOiLy.js → preferences-DO0UUWFp.js} +2 -2
- package/core/built/admin/assets/{preview-chrome-BOVO6_PE.js → preview-chrome-BljUBlOk.js} +1 -1
- package/core/built/admin/assets/{preview-modal-MlTjP8UU.js → preview-modal-C-BL-F9k.js} +1 -1
- package/core/built/admin/assets/profile-CxT-0l4d.js +1 -0
- package/core/built/admin/assets/radio-group-Bgz60i81.js +1 -0
- package/core/built/admin/assets/{router-provider-CJC58zTy.js → router-provider-B0gZyiGJ.js} +1 -1
- package/core/built/admin/assets/{schemas-CRsC53ls.js → schemas-4UPic-oD.js} +2 -2
- package/core/built/admin/assets/select-DuDUFdPq.js +1 -0
- package/core/built/admin/assets/{settings-B5hJKZ5C.js → settings-BjCii7BM.js} +1 -1
- package/core/built/admin/assets/settings-b8kNzZSQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-sLDNedBW.js → settings-breadcrumbs-CyP_9zTo.js} +1 -1
- package/core/built/admin/assets/{shade-app-F7WxOyfk-nufqw1hd.js → shade-app-CcS3f8MZ-vwURazk8.js} +2 -2
- package/core/built/admin/assets/{share-modal-CLBfj377.js → share-modal-BV70mr9k.js} +1 -1
- package/core/built/admin/assets/{skeleton-D7hwjdry.js → skeleton-Ce9GgUpf.js} +1 -1
- package/core/built/admin/assets/{slack-modal-Bt9I_tEo.js → slack-modal-Be2_KK-z.js} +1 -1
- package/core/built/admin/assets/sort-button-Cc-Z96sA.js +1 -0
- package/core/built/admin/assets/{source-icon-BPsD5ees.js → source-icon-v6PUnmyM.js} +1 -1
- package/core/built/admin/assets/{stack-CIfOAAVf.js → stack-B2eszIiq.js} +1 -1
- package/core/built/admin/assets/{stats-B_YkF7bp.js → stats-C4reGh9O.js} +1 -1
- package/core/built/admin/assets/{stats-view-BOCp9RG8.js → stats-view-DP9pQ62k.js} +1 -1
- package/core/built/admin/assets/{step-1-CMta6YFO.js → step-1-DSRp2vBj.js} +1 -1
- package/core/built/admin/assets/{step-2-BG04nh5q.js → step-2-COqv2g0u.js} +1 -1
- package/core/built/admin/assets/{step-3-ZnMaVIPj.js → step-3-6W1NMVQc.js} +2 -2
- package/core/built/admin/assets/{stripe-button-DDoUKN5a.js → stripe-button-D2K42Uty.js} +1 -1
- package/core/built/admin/assets/{stripe-connect-modal-BeSukI8q.js → stripe-connect-modal-Z-UT_BGc.js} +1 -1
- package/core/built/admin/assets/{switch-C9djUIMO.js → switch-CI4gLWqb.js} +1 -1
- package/core/built/admin/assets/{table-CrqQtkR2.js → table-B2pIKjVh.js} +1 -1
- package/core/built/admin/assets/{tabs-CGisSbWq.js → tabs-SaW_UT-Q.js} +1 -1
- package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +2 -0
- package/core/built/admin/assets/tags-BLaXBwnb.js +1 -0
- package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +1 -0
- package/core/built/admin/assets/{text-DU9Hv33_.js → text-BQDSDsKs.js} +1 -1
- package/core/built/admin/assets/{textarea-d9qEkMOP.js → textarea-DJ1Q5V39.js} +1 -1
- package/core/built/admin/assets/{tier-detail-modal-CxRwgTPR.js → tier-detail-modal-BCFpWwUS.js} +1 -1
- package/core/built/admin/assets/{toggle-group-CtKZURJd.js → toggle-group-EM9WpESu.js} +1 -1
- package/core/built/admin/assets/{topic-filter-D8JT8Tq1.js → topic-filter-DY4gNHn2.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-6PUf0idx.js → transistor-modal-CpyPG-MH.js} +1 -1
- package/core/built/admin/assets/trend-badge-ByMkuAuH.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-BPuDBPja.js → unsplash-modal-Cv2Q6GtY.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data--opuzwcz.js → use-analytics-data-CHvjMYuT.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-Qv4SVXqw.js +2 -0
- package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +1 -0
- package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +1 -0
- package/core/built/admin/assets/{use-limiter-TDSNiHPB.js → use-limiter-cwJvsNBo.js} +1 -1
- package/core/built/admin/assets/{use-navigate-with-base-path-CK989MlC.js → use-navigate-with-base-path-BHPA228f.js} +1 -1
- package/core/built/admin/assets/{use-pintura-config-CnCGrDFk.js → use-pintura-config-CzTfnufz.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-ntphvtUd.js → use-pintura-editor-BDBXWUYq.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-CbXLFIex.js → use-post-referrers-DbY-mzci.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-JYFKSO83.js → use-reply-chain-data-BeQwz8oF.js} +1 -1
- package/core/built/admin/assets/{use-router-history-entry-BwfQS2mq.js → use-router-history-entry-Bg6BhBQQ.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-DQxV42-O.js → use-setting-group-VM7FQwMY.js} +1 -1
- package/core/built/admin/assets/{use-settings-navigation-BBzro2-V.js → use-settings-navigation-BbrSUVcf.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-B2TdNX67.js → use-simple-pagination-Dum6Irbs.js} +1 -1
- package/core/built/admin/assets/{use-staff-users-QrR9voeC.js → use-staff-users-cZg4ugOF.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-D2Aeoty5.js → use-tinybird-query-B1IMyAcv.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-DBuwMmln.js → use-tinybird-token-Bow0Zwn8.js} +1 -1
- package/core/built/admin/assets/{use-unsaved-changes-guard-DpYJcj1d.js → use-unsaved-changes-guard-BAy0RANR.js} +1 -1
- package/core/built/admin/assets/{use-url-input-WnIqckzS.js → use-url-input-Df5QzNa7.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +1 -0
- package/core/built/admin/assets/{virtual-list-D3MfPNOh.js → virtual-list-DEx8WgiO.js} +1 -1
- package/core/built/admin/assets/web-CKcXAu86.js +1 -0
- package/core/built/admin/assets/web-DR4TqM3E.js +2 -0
- package/core/built/admin/assets/{zapier-modal-BO6PS_Vd.js → zapier-modal-Cw3BTBUu.js} +1 -1
- package/core/built/admin/index.html +24 -25
- package/core/frontend/services/rendering/renderer.js +15 -0
- package/core/server/adapters/storage/LocalStorageBase.js +8 -5
- package/core/server/api/endpoints/db.js +19 -5
- package/core/server/api/endpoints/index.js +4 -0
- package/core/server/api/endpoints/member-custom-fields.js +4 -14
- package/core/server/api/endpoints/posts.js +1 -0
- package/core/server/api/endpoints/tiers-checkout-config.js +59 -0
- package/core/server/api/endpoints/utils/serializers/output/config.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +4 -0
- package/core/server/api/endpoints/utils/serializers/output/tiers-checkout-config.js +12 -0
- package/core/server/data/exporter/table-lists.js +3 -0
- package/core/server/data/importer/content-files.js +57 -0
- package/core/server/data/importer/handlers/image.js +2 -2
- package/core/server/data/importer/import-archive.js +131 -0
- package/core/server/data/importer/import-manager.js +18 -180
- package/core/server/data/importer/importers/content-file-importer.js +66 -4
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-30-20-add-written-by-to-members-custom-field-values.js +42 -0
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-38-53-add-members-custom-field-bindings-table.js +26 -0
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-48-06-add-tier-checkout-field-tables.js +35 -0
- package/core/server/data/schema/schema.js +90 -0
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-actions-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-runs-importer.js +5 -5
- package/core/server/data/seeders/importers/automations-importer.js +3 -3
- package/core/server/data/seeders/importers/comment-reports-importer.js +1 -1
- package/core/server/data/seeders/importers/comments-importer.js +1 -1
- package/core/server/data/seeders/importers/email-batches-importer.js +1 -1
- package/core/server/data/seeders/importers/email-recipients-importer.js +1 -1
- package/core/server/data/seeders/importers/emails-importer.js +1 -1
- package/core/server/data/seeders/importers/labels-importer.js +1 -1
- package/core/server/data/seeders/importers/members-click-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-created-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-feedback-importer.js +1 -1
- package/core/server/data/seeders/importers/members-importer.js +1 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-products-importer.js +1 -1
- package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +1 -1
- package/core/server/data/seeders/importers/offers-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-importer.js +1 -1
- package/core/server/data/seeders/importers/table-importer.js +2 -2
- package/core/server/data/seeders/importers/tags-importer.js +1 -1
- package/core/server/data/seeders/importers/users-importer.js +1 -1
- package/core/server/data/seeders/importers/web-mentions-importer.js +1 -1
- package/core/server/data/seeders/utils/random.js +3 -3
- package/core/server/ghost-server.js +3 -2
- package/core/server/lib/db-types/boolean.js +18 -0
- package/core/server/lib/promise-pool.js +31 -0
- package/core/server/services/automations/database-automations-repository.js +26 -26
- package/core/server/services/automations/poll.js +1 -0
- package/core/server/services/content-import/import/assets.js +74 -0
- package/core/server/services/content-import/import/importer.js +57 -14
- package/core/server/services/content-import/import/post-data.js +1 -0
- package/core/server/services/content-import/import/post-repository.js +78 -0
- package/core/server/services/content-import/import/relations.js +149 -0
- package/core/server/services/content-import/import/row.js +11 -0
- package/core/server/services/content-import/import/schema.js +29 -0
- package/core/server/services/content-import/import/source.js +125 -0
- package/core/server/services/content-import/index.js +6 -28
- package/core/server/services/email-analytics/email-analytics-service.js +8 -1
- package/core/server/services/email-analytics/lib/queries.js +7 -1
- package/core/server/services/gift-links/schema.js +3 -3
- package/core/server/services/gifts/CONTEXT.md +1 -1
- package/core/server/services/gifts/constants.js +1 -1
- package/core/server/services/gifts/gift-bookshelf-repository.js +2 -2
- package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +18 -9
- package/core/server/services/gifts/gift-delivery-schema.js +5 -5
- package/core/server/services/gifts/gift-delivery-service.js +4 -0
- package/core/server/services/gifts/gift-schema.js +10 -10
- package/core/server/services/gifts/gift-service.js +4 -2
- package/core/server/services/jobs-service/register-job-handlers.js +5 -5
- package/core/server/services/lib/mailgun-client.js +44 -8
- package/core/server/services/media-inliner/external-media-inliner-job.js +12 -0
- package/core/server/services/media-inliner/external-media-inliner.js +103 -29
- package/core/server/services/media-inliner/service.js +13 -41
- package/core/server/services/media-inliner/types.js +2 -0
- package/core/server/services/members/import-export/export/exporter.js +4 -6
- package/core/server/services/members/import-export/import/importer.js +21 -5
- package/core/server/services/members/import-export/import/stripe-utils.js +6 -1
- package/core/server/services/members/import-export/index.js +6 -1
- package/core/server/services/members/members-api/members-api.js +5 -0
- package/core/server/services/members/members-api/services/member-bread-service.js +19 -1
- package/core/server/services/members/members-api/services/payments-service.js +47 -1
- package/core/server/services/members-custom-fields/actions.js +27 -1
- package/core/server/services/members-custom-fields/bindings-service.js +119 -0
- package/core/server/services/members-custom-fields/definitions-service.js +104 -13
- package/core/server/services/members-custom-fields/index.js +11 -4
- package/core/server/services/members-custom-fields/queries.js +5 -0
- package/core/server/services/members-custom-fields/schema.js +37 -6
- package/core/server/services/members-custom-fields/values-service.js +15 -3
- package/core/server/services/public-config/config.js +7 -0
- package/core/server/services/stripe/services/checkout/allowed-countries.js +267 -0
- package/core/server/services/stripe/services/checkout/completed-session.js +68 -0
- package/core/server/services/stripe/services/checkout/field-ports.js +34 -0
- package/core/server/services/stripe/services/checkout/session-options.js +97 -0
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +49 -0
- package/core/server/services/stripe/stripe-api.js +29 -0
- package/core/server/services/stripe/stripe-service.js +7 -0
- package/core/server/services/tier-checkout-config/codec.js +95 -0
- package/core/server/services/tier-checkout-config/destinations.js +17 -0
- package/core/server/services/tier-checkout-config/index.js +26 -0
- package/core/server/services/tier-checkout-config/models.js +57 -0
- package/core/server/services/tier-checkout-config/queries.js +80 -0
- package/core/server/services/tier-checkout-config/schema.js +33 -0
- package/core/server/services/tier-checkout-config/serializers.js +122 -0
- package/core/server/services/tier-checkout-config/service.js +303 -0
- package/core/server/services/tiers/service.js +21 -0
- package/core/server/web/api/endpoints/admin/routes.js +24 -13
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/server/web/parent/app.js +5 -0
- package/core/server/web/parent/middleware/filter-query-parameters.js +94 -0
- package/core/server/web/parent/middleware/index.js +1 -0
- package/core/server/web/query-parameter-policy/index.js +9 -0
- package/core/server/web/query-parameter-policy/policy.json +58 -0
- package/core/server/web/query-parameter-policy/schema.js +38 -0
- package/core/shared/config/defaults.json +3 -0
- package/core/shared/config/env/config.testing-mysql.json +3 -0
- package/core/shared/config/env/config.testing.json +3 -0
- package/core/shared/config/overrides.json +4 -2
- package/core/shared/labs.js +5 -14
- package/package.json +12 -13
- package/pnpm-lock.yaml +44 -46
- package/pnpm-workspace.yaml +6 -6
- package/tsconfig.json +2 -3
- package/core/built/admin/assets/activitypub/layout-CXajhRsL.js +0 -1
- package/core/built/admin/assets/alert-dialog-wEbVxANM.js +0 -1
- package/core/built/admin/assets/app-utils-D2Wp7ZSS.js +0 -1
- package/core/built/admin/assets/audience-DTLMXAFH.js +0 -1
- package/core/built/admin/assets/chart-helpers-B3OqQ6Ez.js +0 -1
- package/core/built/admin/assets/command-DcLbSifC.js +0 -1
- package/core/built/admin/assets/comments-Bxy_3WyE.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-CZ-PkW7o.js +0 -23
- package/core/built/admin/assets/dialog-Db7c164G.js +0 -1
- package/core/built/admin/assets/edit-profile-By7v5XW5.js +0 -1
- package/core/built/admin/assets/growth-55vWlGT_.js +0 -1
- package/core/built/admin/assets/growth-DA5AluYw.js +0 -3
- package/core/built/admin/assets/history-modal-G1s5hHz7.js +0 -1
- package/core/built/admin/assets/html-field-BegxsWpw.js +0 -1
- package/core/built/admin/assets/index-CDP9tIOk.js +0 -4
- package/core/built/admin/assets/koenig-loader-DwVY_V-H.js +0 -1
- package/core/built/admin/assets/kpi-card-LERqWBoY.js +0 -1
- package/core/built/admin/assets/kpi-helpers-DB3ouhYF.js +0 -1
- package/core/built/admin/assets/kpi-tabs-BGvtMD8S.js +0 -1
- package/core/built/admin/assets/layout-Di5K7doM.js +0 -1
- package/core/built/admin/assets/member-detail-D7mZDDp3.js +0 -1
- package/core/built/admin/assets/member-route-0Aa52Ntz.js +0 -1
- package/core/built/admin/assets/members-DvZG9t69.js +0 -2
- package/core/built/admin/assets/navigation-modal-CI8Bx5vn.js +0 -1
- package/core/built/admin/assets/newsletter-Bg3cq5U-.js +0 -1
- package/core/built/admin/assets/newsletters-DMDfZbMS.js +0 -1
- package/core/built/admin/assets/notifications-psA_2Ph4.js +0 -1
- package/core/built/admin/assets/overview-C-6xOLaf.js +0 -1
- package/core/built/admin/assets/overview-DaLhpRrC.js +0 -1
- package/core/built/admin/assets/popover-CFgqFDvC.js +0 -1
- package/core/built/admin/assets/portal-modal-Cf0pBAk8.js +0 -1
- package/core/built/admin/assets/profile-D6dHYf2o.js +0 -1
- package/core/built/admin/assets/radio-group-CjtmsCPl.js +0 -1
- package/core/built/admin/assets/select-D_RQaXgt.js +0 -1
- package/core/built/admin/assets/settings-DIo_F-MF.js +0 -4
- package/core/built/admin/assets/sort-button-DMBXF7tP.js +0 -1
- package/core/built/admin/assets/tag-detail-2a3BfzTW.js +0 -2
- package/core/built/admin/assets/tags-Dp2_8UzL.js +0 -1
- package/core/built/admin/assets/testimonials-modal-C9nrypNo.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-C9-QhGdV.js +0 -1
- package/core/built/admin/assets/use-filter-params-CSpAvgD2.js +0 -1
- package/core/built/admin/assets/use-growth-stats-BOmoPDHJ.js +0 -1
- package/core/built/admin/assets/user-detail-modal-59mBJqxN.js +0 -1
- package/core/built/admin/assets/web-DUuX7nE2.js +0 -2
- package/core/built/admin/assets/web-N8ucs2aG.js +0 -1
- /package/core/server/lib/{db-date.js → db-types/date.js} +0 -0
|
@@ -13,7 +13,7 @@ const dequal_1 = require("dequal");
|
|
|
13
13
|
const lexical_1 = __importDefault(require("../../lib/lexical"));
|
|
14
14
|
const url_utils_1 = __importDefault(require("../../../shared/url-utils"));
|
|
15
15
|
const constants_1 = require("../member-welcome-emails/constants");
|
|
16
|
-
const
|
|
16
|
+
const date_1 = require("../../lib/db-types/date");
|
|
17
17
|
const stale_lock_cutoff_1 = require("./stale-lock-cutoff");
|
|
18
18
|
const HOUR_MS = 60 * 60 * 1000;
|
|
19
19
|
const DEFAULT_WELCOME_EMAIL_AUTOMATIONS = [
|
|
@@ -93,7 +93,7 @@ function createDatabaseAutomationsRepository({ knex, fakeWaitHoursMultiplier, })
|
|
|
93
93
|
const updatedAutomation = await updateAutomation(trx, {
|
|
94
94
|
...automation,
|
|
95
95
|
status: data.status,
|
|
96
|
-
updated_at: (0,
|
|
96
|
+
updated_at: (0, date_1.toDatabaseDate)(now),
|
|
97
97
|
});
|
|
98
98
|
await replaceAutomationGraph(trx, updatedAutomation.id, data.actions, data.edges);
|
|
99
99
|
if (updatedAutomation.status === 'inactive') {
|
|
@@ -130,7 +130,7 @@ function createDatabaseAutomationsRepository({ knex, fakeWaitHoursMultiplier, })
|
|
|
130
130
|
.update({
|
|
131
131
|
email_sent_count: trx.raw('COALESCE(??, 0) + ?', ['email_sent_count', 1]),
|
|
132
132
|
});
|
|
133
|
-
const now = (0,
|
|
133
|
+
const now = (0, date_1.toDatabaseDate)(new Date());
|
|
134
134
|
await trx('automated_email_recipients').insert({
|
|
135
135
|
id: (0, bson_objectid_1.default)().toHexString(),
|
|
136
136
|
member_id: options.memberId,
|
|
@@ -281,7 +281,7 @@ async function ensureDefaultAutomations(trx) {
|
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
async function ensureAutomation(trx, defaults) {
|
|
284
|
-
const now = (0,
|
|
284
|
+
const now = (0, date_1.toDatabaseDate)(new Date());
|
|
285
285
|
const id = (0, bson_objectid_1.default)().toHexString();
|
|
286
286
|
await trx('automations')
|
|
287
287
|
.insert({
|
|
@@ -313,7 +313,7 @@ async function ensureWelcomeEmailAction(trx, automationId) {
|
|
|
313
313
|
if (!email) {
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
|
-
const now = (0,
|
|
316
|
+
const now = (0, date_1.toDatabaseDate)(new Date());
|
|
317
317
|
const actionId = (0, bson_objectid_1.default)().toHexString();
|
|
318
318
|
await insertActions(trx, [
|
|
319
319
|
{
|
|
@@ -347,7 +347,7 @@ async function trigger(trx, options) {
|
|
|
347
347
|
return;
|
|
348
348
|
}
|
|
349
349
|
const now = new Date();
|
|
350
|
-
const nowString = (0,
|
|
350
|
+
const nowString = (0, date_1.toDatabaseDate)(now);
|
|
351
351
|
const readyAt = getReadyAtForAction(firstAction, now, fakeWaitHoursMultiplier);
|
|
352
352
|
const run = {
|
|
353
353
|
id: (0, bson_objectid_1.default)().toHexString(),
|
|
@@ -366,14 +366,14 @@ async function trigger(trx, options) {
|
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
368
|
async function insertRunStep(trx, { automationRunId, automationActionRevisionId, now, readyAt, }) {
|
|
369
|
-
const nowString = (0,
|
|
369
|
+
const nowString = (0, date_1.toDatabaseDate)(now);
|
|
370
370
|
await trx('automation_run_steps').insert({
|
|
371
371
|
id: (0, bson_objectid_1.default)().toHexString(),
|
|
372
372
|
created_at: nowString,
|
|
373
373
|
updated_at: nowString,
|
|
374
374
|
automation_run_id: automationRunId,
|
|
375
375
|
automation_action_revision_id: automationActionRevisionId,
|
|
376
|
-
ready_at: (0,
|
|
376
|
+
ready_at: (0, date_1.toDatabaseDate)(readyAt),
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
async function fetchAndLockSteps(trx, limit) {
|
|
@@ -388,9 +388,9 @@ async function fetchAndLockSteps(trx, limit) {
|
|
|
388
388
|
// 2. Try to lock those rows.
|
|
389
389
|
// 3. Select any rows we successfully locked.
|
|
390
390
|
const now = new Date();
|
|
391
|
-
const nowString = (0,
|
|
391
|
+
const nowString = (0, date_1.toDatabaseDate)(now);
|
|
392
392
|
const staleLockCutoff = (0, stale_lock_cutoff_1.getStaleLockCutoff)(now);
|
|
393
|
-
const staleLockCutoffString = (0,
|
|
393
|
+
const staleLockCutoffString = (0, date_1.toDatabaseDate)(staleLockCutoff);
|
|
394
394
|
const lockId = node_crypto_1.default.randomUUID();
|
|
395
395
|
// 1. Select up to `limit` candidate rows.
|
|
396
396
|
const candidates = await trx('automation_run_steps')
|
|
@@ -444,17 +444,17 @@ async function findNextPendingReadyAt(trx, staleLockCutoff) {
|
|
|
444
444
|
.select({ next_ready_at: 'ready_at' })
|
|
445
445
|
.where('status', 'pending')
|
|
446
446
|
.where((builder) => {
|
|
447
|
-
builder.whereNull('locked_by').orWhere('locked_at', '<', (0,
|
|
447
|
+
builder.whereNull('locked_by').orWhere('locked_at', '<', (0, date_1.toDatabaseDate)(staleLockCutoff));
|
|
448
448
|
})
|
|
449
449
|
.orderBy('ready_at')
|
|
450
450
|
.first();
|
|
451
|
-
return row?.next_ready_at ? (0,
|
|
451
|
+
return row?.next_ready_at ? (0, date_1.fromDatabaseDate)(row.next_ready_at) : null;
|
|
452
452
|
}
|
|
453
453
|
function buildStepToRun(row) {
|
|
454
454
|
const base = {
|
|
455
455
|
id: row.id,
|
|
456
456
|
step_attempts: row.step_attempts,
|
|
457
|
-
ready_at: (0,
|
|
457
|
+
ready_at: (0, date_1.fromDatabaseDate)(row.ready_at),
|
|
458
458
|
locked_by: row.locked_by,
|
|
459
459
|
automation_run_id: row.automation_run_id,
|
|
460
460
|
automation_id: row.automation_id,
|
|
@@ -544,7 +544,7 @@ async function findNextActionRevision(trx, sourceActionId) {
|
|
|
544
544
|
return row ?? null;
|
|
545
545
|
}
|
|
546
546
|
async function markStepTerminal(trx, step, status) {
|
|
547
|
-
const nowString = (0,
|
|
547
|
+
const nowString = (0, date_1.toDatabaseDate)(new Date());
|
|
548
548
|
return await updateStep(trx, step, {
|
|
549
549
|
status,
|
|
550
550
|
finished_at: nowString,
|
|
@@ -556,12 +556,12 @@ async function retryStep(trx, step, retryAt) {
|
|
|
556
556
|
await markStepTerminal(trx, step, 'automation disabled');
|
|
557
557
|
return false;
|
|
558
558
|
}
|
|
559
|
-
const nowString = (0,
|
|
559
|
+
const nowString = (0, date_1.toDatabaseDate)(new Date());
|
|
560
560
|
return await updateStep(trx, step, {
|
|
561
561
|
status: 'pending',
|
|
562
562
|
started_at: null,
|
|
563
563
|
finished_at: null,
|
|
564
|
-
ready_at: (0,
|
|
564
|
+
ready_at: (0, date_1.toDatabaseDate)(retryAt),
|
|
565
565
|
updated_at: nowString,
|
|
566
566
|
});
|
|
567
567
|
}
|
|
@@ -586,8 +586,8 @@ function getReadyAtForAction(action, now, fakeWaitHoursMultiplier) {
|
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
588
|
async function cancelCancelablePendingStepsForAutomation(trx, automationId, now) {
|
|
589
|
-
const nowString = (0,
|
|
590
|
-
const staleLockCutoff = (0,
|
|
589
|
+
const nowString = (0, date_1.toDatabaseDate)(now);
|
|
590
|
+
const staleLockCutoff = (0, date_1.toDatabaseDate)((0, stale_lock_cutoff_1.getStaleLockCutoff)(now));
|
|
591
591
|
await trx('automation_run_steps')
|
|
592
592
|
.update({
|
|
593
593
|
status: 'automation disabled',
|
|
@@ -707,7 +707,7 @@ async function replaceAutomationGraph(trx, automationId, submittedActions, edges
|
|
|
707
707
|
revision.action_id,
|
|
708
708
|
revision,
|
|
709
709
|
]));
|
|
710
|
-
const now = (0,
|
|
710
|
+
const now = (0, date_1.toDatabaseDate)(new Date());
|
|
711
711
|
const actionsToInsert = [];
|
|
712
712
|
const revisionsToInsert = [];
|
|
713
713
|
for (const action of actions) {
|
|
@@ -884,14 +884,14 @@ async function insertActionRevisions(trx, revisions) {
|
|
|
884
884
|
}
|
|
885
885
|
function getNextRevisionCreatedAt(latestCreatedAt, requestedCreatedAt) {
|
|
886
886
|
if (!latestCreatedAt) {
|
|
887
|
-
return (0,
|
|
887
|
+
return (0, date_1.toDatabaseDate)(requestedCreatedAt);
|
|
888
888
|
}
|
|
889
|
-
const requestedTime = (0,
|
|
890
|
-
const latestTime = (0,
|
|
889
|
+
const requestedTime = (0, date_1.fromDatabaseDate)(requestedCreatedAt).getTime();
|
|
890
|
+
const latestTime = (0, date_1.fromDatabaseDate)(latestCreatedAt).getTime();
|
|
891
891
|
if (requestedTime > latestTime) {
|
|
892
|
-
return (0,
|
|
892
|
+
return (0, date_1.toDatabaseDate)(requestedCreatedAt);
|
|
893
893
|
}
|
|
894
|
-
return (0,
|
|
894
|
+
return (0, date_1.toDatabaseDate)(new Date(latestTime + 1000));
|
|
895
895
|
}
|
|
896
896
|
function buildActionRevision(actionId, action, createdAt) {
|
|
897
897
|
switch (action.type) {
|
|
@@ -970,7 +970,7 @@ function buildAutomationBrowseResult(automation) {
|
|
|
970
970
|
...buildAutomationSummary(automation),
|
|
971
971
|
stats: {
|
|
972
972
|
last_run_created_at: automation.last_run_created_at
|
|
973
|
-
? (0,
|
|
973
|
+
? (0, date_1.fromDatabaseDate)(automation.last_run_created_at)
|
|
974
974
|
: null,
|
|
975
975
|
total_run_count: Number(automation.total_run_count ?? 0),
|
|
976
976
|
in_progress_run_count: Number(automation.in_progress_run_count ?? 0),
|
|
@@ -978,7 +978,7 @@ function buildAutomationBrowseResult(automation) {
|
|
|
978
978
|
};
|
|
979
979
|
}
|
|
980
980
|
function serializeDate(date) {
|
|
981
|
-
const normalizedDate = (0,
|
|
981
|
+
const normalizedDate = (0, date_1.fromDatabaseDate)(date);
|
|
982
982
|
normalizedDate.setMilliseconds(0);
|
|
983
983
|
return normalizedDate.toISOString();
|
|
984
984
|
}
|
|
@@ -119,6 +119,7 @@ const processStep = async ({ automationsApi, memberWelcomeEmailService, schedule
|
|
|
119
119
|
subject: step.email_subject,
|
|
120
120
|
},
|
|
121
121
|
member: {
|
|
122
|
+
// Note that we don't use `automation_run.member_email` here. That's only for the historical record, not for functionality.
|
|
122
123
|
email: member.get('email'),
|
|
123
124
|
name: member.get('name'),
|
|
124
125
|
uuid: member.get('uuid'),
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PreparedAssetBatch = void 0;
|
|
4
|
+
exports.prepareAssetBatch = prepareAssetBatch;
|
|
5
|
+
exports.belongsToAssetDirectory = belongsToAssetDirectory;
|
|
6
|
+
const content_files_1 = require("../../../data/importer/content-files");
|
|
7
|
+
class PreparedAssetBatch {
|
|
8
|
+
groups;
|
|
9
|
+
constructor(groups) {
|
|
10
|
+
this.groups = groups;
|
|
11
|
+
}
|
|
12
|
+
get files() {
|
|
13
|
+
return this.groups.flatMap((group) => group.files);
|
|
14
|
+
}
|
|
15
|
+
async store() {
|
|
16
|
+
const results = await Promise.allSettled(this.groups.map((group) => group.importer.doImport(group.files)));
|
|
17
|
+
const failure = results.find((result) => result.status === 'rejected');
|
|
18
|
+
if (failure) {
|
|
19
|
+
const rollbackResults = await Promise.allSettled(results.flatMap((result, index) => result.status === 'fulfilled' ? [this.groups[index].importer.rollback(result.value)] : []));
|
|
20
|
+
const rollbackFailures = rollbackResults.flatMap((result) => result.status === 'rejected' ? [result.reason] : []);
|
|
21
|
+
if (rollbackFailures.length > 0) {
|
|
22
|
+
// AggregateError is the native error for preserving storage and rollback
|
|
23
|
+
// failures; unlike GhostError it takes an iterable before its options.
|
|
24
|
+
// eslint-disable-next-line ghost/ghost-custom/ghost-error-usage
|
|
25
|
+
throw new AggregateError([failure.reason, ...rollbackFailures], 'Asset storage failed and rollback was incomplete.', { cause: failure.reason });
|
|
26
|
+
}
|
|
27
|
+
throw failure.reason;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
rewriteRows(rows) {
|
|
31
|
+
const importData = {
|
|
32
|
+
data: { data: { posts: rows, tags: [], users: [] } },
|
|
33
|
+
};
|
|
34
|
+
for (const group of this.groups) {
|
|
35
|
+
importData[group.type] = group.files;
|
|
36
|
+
group.importer.preProcess(importData);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.PreparedAssetBatch = PreparedAssetBatch;
|
|
41
|
+
async function prepareAssetBatch(archive, directory, baseDirectory, deps = {}) {
|
|
42
|
+
const handlers = deps.handlers ?? (0, content_files_1.createContentFileHandlers)();
|
|
43
|
+
const importers = deps.importers ?? (0, content_files_1.createContentFileImporters)();
|
|
44
|
+
const importerByType = new Map(importers.map((importer) => [importer.type, importer]));
|
|
45
|
+
const groups = await Promise.all(handlers.map(async (handler) => {
|
|
46
|
+
const files = archive
|
|
47
|
+
.getFiles(directory, handler.extensions)
|
|
48
|
+
.filter((file) => belongsToAssetDirectory(file.name, handler.type, baseDirectory));
|
|
49
|
+
if (files.length === 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const importer = importerByType.get(handler.type);
|
|
53
|
+
if (!importer) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
type: handler.type,
|
|
58
|
+
files: await handler.loadFile(files, baseDirectory),
|
|
59
|
+
importer,
|
|
60
|
+
};
|
|
61
|
+
}));
|
|
62
|
+
const presentGroups = groups.filter((group) => group !== undefined);
|
|
63
|
+
return presentGroups.length > 0 ? new PreparedAssetBatch(presentGroups) : undefined;
|
|
64
|
+
}
|
|
65
|
+
function belongsToAssetDirectory(fileName, type, baseDirectory) {
|
|
66
|
+
const parts = fileName.split('/').filter(Boolean);
|
|
67
|
+
if (baseDirectory && parts[0] === baseDirectory) {
|
|
68
|
+
parts.shift();
|
|
69
|
+
}
|
|
70
|
+
if (parts[0] === 'content') {
|
|
71
|
+
parts.shift();
|
|
72
|
+
}
|
|
73
|
+
return parts.length > 1 && parts[0] === type;
|
|
74
|
+
}
|
|
@@ -45,7 +45,7 @@ const messages = {
|
|
|
45
45
|
unreadableFile: 'The file could not be parsed as a CSV file.',
|
|
46
46
|
tooManyPosts: 'This file contains more than {max} posts. Imports are temporarily limited to {max} posts at a time — please split the file into smaller files and try again.',
|
|
47
47
|
allWritesFailed: 'Content import failed to write all {count} attempted {postNoun}.',
|
|
48
|
-
urlResolutionFailed: 'Content import could not resolve a URL for {count}
|
|
48
|
+
urlResolutionFailed: 'Content import could not resolve a URL for {count} imported {postNoun}.',
|
|
49
49
|
};
|
|
50
50
|
function logLifecycle(message) {
|
|
51
51
|
try {
|
|
@@ -64,6 +64,7 @@ function buildImportTagNames(runId, timezone, now) {
|
|
|
64
64
|
}
|
|
65
65
|
class ContentCSVImporter {
|
|
66
66
|
_readRows;
|
|
67
|
+
_prepareSource;
|
|
67
68
|
_posts;
|
|
68
69
|
_getHtmlToLexical;
|
|
69
70
|
_getMarkdownToHtml;
|
|
@@ -75,8 +76,9 @@ class ContentCSVImporter {
|
|
|
75
76
|
_newRunId;
|
|
76
77
|
_getTimezone;
|
|
77
78
|
_now;
|
|
78
|
-
constructor({ readRows, posts, getHtmlToLexical, getMarkdownToHtml, getCleanHTML, addJob, report, store, urlForPost, newRunId, getTimezone, now = () => new Date(), }) {
|
|
79
|
+
constructor({ readRows, prepareSource = async ({ filePath }) => ({ filePath, cleanup: async () => { } }), posts, getHtmlToLexical, getMarkdownToHtml, getCleanHTML, addJob, report, store, urlForPost, newRunId, getTimezone, now = () => new Date(), }) {
|
|
79
80
|
this._readRows = readRows;
|
|
81
|
+
this._prepareSource = prepareSource;
|
|
80
82
|
this._posts = posts;
|
|
81
83
|
this._getHtmlToLexical = getHtmlToLexical;
|
|
82
84
|
this._getMarkdownToHtml = getMarkdownToHtml;
|
|
@@ -90,11 +92,13 @@ class ContentCSVImporter {
|
|
|
90
92
|
this._now = now;
|
|
91
93
|
}
|
|
92
94
|
async importCSV(request) {
|
|
95
|
+
const source = await this._prepareSource(request);
|
|
93
96
|
let rows;
|
|
94
97
|
try {
|
|
95
|
-
rows = await this._readRows(
|
|
98
|
+
rows = await this._readRows(source.filePath, request.mapping);
|
|
96
99
|
}
|
|
97
100
|
catch (error) {
|
|
101
|
+
await this.cleanupSource(source.cleanup);
|
|
98
102
|
throw new errors.ValidationError({
|
|
99
103
|
message: tpl(messages.unreadableFile),
|
|
100
104
|
err: error,
|
|
@@ -103,6 +107,7 @@ class ContentCSVImporter {
|
|
|
103
107
|
// Temporary while import state is held in memory: the durable job
|
|
104
108
|
// system milestone removes the cap.
|
|
105
109
|
if (rows.length > MAX_POSTS) {
|
|
110
|
+
await this.cleanupSource(source.cleanup);
|
|
106
111
|
throw new errors.ValidationError({
|
|
107
112
|
message: tpl(messages.tooManyPosts, { max: MAX_POSTS }),
|
|
108
113
|
});
|
|
@@ -111,26 +116,36 @@ class ContentCSVImporter {
|
|
|
111
116
|
const importTagNames = buildImportTagNames(runId, this._getTimezone(), this._now());
|
|
112
117
|
this._store.create(runId, rows.length);
|
|
113
118
|
logLifecycle('queued');
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
try {
|
|
120
|
+
this._addJob({
|
|
121
|
+
job: () => this.runImportJob(runId, importTagNames, rows, source),
|
|
122
|
+
offloaded: false,
|
|
123
|
+
name: 'content-import',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this._store.fail(runId, messageOf(error));
|
|
128
|
+
await this.cleanupSource(source.cleanup);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
119
131
|
return { importId: runId, total: rows.length };
|
|
120
132
|
}
|
|
121
133
|
// Must resolve in every case: the job manager reads a rejected inline job as a
|
|
122
134
|
// defect in the job itself, and there is no retry behind it.
|
|
123
|
-
async runImportJob(runId, importTagNames, rows) {
|
|
135
|
+
async runImportJob(runId, importTagNames, rows, source) {
|
|
124
136
|
const startedAt = Date.now();
|
|
125
137
|
logLifecycle('started');
|
|
126
138
|
let urlFailureCount = 0;
|
|
127
139
|
let firstUrlFailure;
|
|
128
140
|
let failed = false;
|
|
129
141
|
try {
|
|
142
|
+
if (source.assets) {
|
|
143
|
+
await source.assets.store();
|
|
144
|
+
source.assets.rewriteRows(rows);
|
|
145
|
+
}
|
|
130
146
|
const htmlToLexical = this._getHtmlToLexical();
|
|
131
147
|
const markdownToHtml = this._getMarkdownToHtml();
|
|
132
148
|
const cleanHTML = this._getCleanHTML();
|
|
133
|
-
let attemptedWrites = 0;
|
|
134
149
|
let successfulWrites = 0;
|
|
135
150
|
let failedWrites = 0;
|
|
136
151
|
let firstWriteFailure;
|
|
@@ -154,8 +169,9 @@ class ContentCSVImporter {
|
|
|
154
169
|
// failure. Stop the run before misclassifying it as a lost write.
|
|
155
170
|
throw error;
|
|
156
171
|
}
|
|
157
|
-
attemptedWrites += 1;
|
|
158
172
|
let post;
|
|
173
|
+
let writeStatus;
|
|
174
|
+
let warnings;
|
|
159
175
|
try {
|
|
160
176
|
// options.importing preserves the supplied timestamps and keeps the import silent:
|
|
161
177
|
// the webhook, Slack, IndexNow and mention consumers all stand down on it, and a
|
|
@@ -163,10 +179,27 @@ class ContentCSVImporter {
|
|
|
163
179
|
// it, one reason status is never 'scheduled'). Pinned by
|
|
164
180
|
// test/e2e-webhooks/posts-importer.test.js. A fresh options object per row: the
|
|
165
181
|
// model layer mutates it.
|
|
166
|
-
|
|
182
|
+
const result = await this._posts.write(data, {
|
|
167
183
|
importing: true,
|
|
168
184
|
context: { internal: true },
|
|
185
|
+
}, {
|
|
186
|
+
sourceUpdatedAt: row.updated_at,
|
|
187
|
+
authorNames: row.authors,
|
|
188
|
+
authorEmails: row.author_emails,
|
|
189
|
+
tagNames: row.tags,
|
|
169
190
|
});
|
|
191
|
+
if (result.status === 'skipped') {
|
|
192
|
+
this._store.record(runId, {
|
|
193
|
+
line,
|
|
194
|
+
title: row.title,
|
|
195
|
+
status: 'skipped',
|
|
196
|
+
reason: result.reason,
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
post = result.post;
|
|
201
|
+
writeStatus = result.status;
|
|
202
|
+
warnings = result.warnings;
|
|
170
203
|
successfulWrites += 1;
|
|
171
204
|
}
|
|
172
205
|
catch (error) {
|
|
@@ -185,8 +218,9 @@ class ContentCSVImporter {
|
|
|
185
218
|
const outcome = {
|
|
186
219
|
line,
|
|
187
220
|
title: row.title,
|
|
188
|
-
status:
|
|
221
|
+
status: writeStatus,
|
|
189
222
|
postId: post.id,
|
|
223
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
190
224
|
};
|
|
191
225
|
try {
|
|
192
226
|
outcome.url = this._urlForPost(post);
|
|
@@ -199,7 +233,7 @@ class ContentCSVImporter {
|
|
|
199
233
|
}
|
|
200
234
|
this._store.record(runId, outcome);
|
|
201
235
|
}
|
|
202
|
-
if (
|
|
236
|
+
if (failedWrites > 0 && successfulWrites === 0) {
|
|
203
237
|
this._report(new errors.InternalServerError({
|
|
204
238
|
message: tpl(messages.allWritesFailed, {
|
|
205
239
|
count: failedWrites,
|
|
@@ -218,6 +252,7 @@ class ContentCSVImporter {
|
|
|
218
252
|
this._store.fail(runId, messageOf(error));
|
|
219
253
|
}
|
|
220
254
|
finally {
|
|
255
|
+
await this.cleanupSource(source.cleanup);
|
|
221
256
|
const outcome = failed ? 'failed after' : 'completed in';
|
|
222
257
|
logLifecycle(`${outcome} ${Date.now() - startedAt}ms`);
|
|
223
258
|
}
|
|
@@ -233,6 +268,14 @@ class ContentCSVImporter {
|
|
|
233
268
|
}));
|
|
234
269
|
}
|
|
235
270
|
}
|
|
271
|
+
async cleanupSource(cleanup) {
|
|
272
|
+
try {
|
|
273
|
+
await cleanup();
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
this._report(error);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
236
279
|
}
|
|
237
280
|
function messageOf(error) {
|
|
238
281
|
if (error &&
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BookshelfPostsRepository = void 0;
|
|
4
|
+
const relations_1 = require("./relations");
|
|
5
|
+
class BookshelfPostsRepository {
|
|
6
|
+
_models;
|
|
7
|
+
_relations;
|
|
8
|
+
constructor(models, relations = new relations_1.BookshelfPostRelationsResolver(models)) {
|
|
9
|
+
this._models = models;
|
|
10
|
+
this._relations = relations;
|
|
11
|
+
}
|
|
12
|
+
write(data, options, metadata = {}) {
|
|
13
|
+
return this._models.Base.transaction(async (transacting) => {
|
|
14
|
+
const writeOptions = { ...options, transacting };
|
|
15
|
+
const lookupOptions = { ...writeOptions, forUpdate: true };
|
|
16
|
+
let existingMatch;
|
|
17
|
+
if (data.comment_id) {
|
|
18
|
+
const existing = await this._models.Post.findOne({ comment_id: data.comment_id, status: 'all' }, lookupOptions);
|
|
19
|
+
if (existing) {
|
|
20
|
+
existingMatch = {
|
|
21
|
+
post: existing,
|
|
22
|
+
duplicateReason: `A post with the source ID "${data.comment_id}" already exists.`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!existingMatch) {
|
|
27
|
+
const existing = await this._models.Post.findOne({ slug: data.slug, status: 'all' }, lookupOptions);
|
|
28
|
+
if (existing) {
|
|
29
|
+
existingMatch = {
|
|
30
|
+
post: existing,
|
|
31
|
+
duplicateReason: `A post with the slug "${data.slug}" already exists.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (existingMatch) {
|
|
36
|
+
const { post: existing, duplicateReason } = existingMatch;
|
|
37
|
+
if (!metadata.sourceUpdatedAt) {
|
|
38
|
+
return { status: 'skipped', reason: duplicateReason };
|
|
39
|
+
}
|
|
40
|
+
const incomingInstant = new Date(metadata.sourceUpdatedAt).getTime();
|
|
41
|
+
const storedUpdatedAt = existing.toJSON().updated_at;
|
|
42
|
+
const storedInstant = storedUpdatedAt
|
|
43
|
+
? new Date(storedUpdatedAt).getTime()
|
|
44
|
+
: undefined;
|
|
45
|
+
if (Number.isNaN(incomingInstant) ||
|
|
46
|
+
(storedInstant !== undefined && incomingInstant <= storedInstant)) {
|
|
47
|
+
return {
|
|
48
|
+
status: 'skipped',
|
|
49
|
+
reason: 'The existing post is newer than or as recent as the imported row.',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Ghost's collision plugin treats updated_at as the client's version token.
|
|
53
|
+
// First update the content against the locked server version, then persist
|
|
54
|
+
// the incoming source timestamp on its own. The second edit is safe because
|
|
55
|
+
// timestamp-only importing edits are excluded from collision detection.
|
|
56
|
+
const resolved = await this._relations.resolve(data, metadata, writeOptions);
|
|
57
|
+
const collisionSafeData = { ...resolved.data };
|
|
58
|
+
if (storedUpdatedAt) {
|
|
59
|
+
collisionSafeData.updated_at = storedUpdatedAt;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
delete collisionSafeData.updated_at;
|
|
63
|
+
}
|
|
64
|
+
const editOptions = {
|
|
65
|
+
...writeOptions,
|
|
66
|
+
id: existing.id,
|
|
67
|
+
};
|
|
68
|
+
await this._models.Post.edit(collisionSafeData, { ...editOptions });
|
|
69
|
+
const post = await this._models.Post.edit({ updated_at: metadata.sourceUpdatedAt }, { ...editOptions });
|
|
70
|
+
return { status: 'updated', post, warnings: resolved.warnings };
|
|
71
|
+
}
|
|
72
|
+
const resolved = await this._relations.resolve(data, metadata, writeOptions);
|
|
73
|
+
const post = await this._models.Post.add(resolved.data, writeOptions);
|
|
74
|
+
return { status: 'created', post, warnings: resolved.warnings };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.BookshelfPostsRepository = BookshelfPostsRepository;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BookshelfPostRelationsResolver = void 0;
|
|
4
|
+
exports.parseAuthorReferences = parseAuthorReferences;
|
|
5
|
+
exports.parseTagReferences = parseTagReferences;
|
|
6
|
+
const { slugify } = require('@tryghost/string');
|
|
7
|
+
const validator = require('@tryghost/validator');
|
|
8
|
+
function parseAuthorReferences(authorNames, authorEmails) {
|
|
9
|
+
const names = splitList(authorNames);
|
|
10
|
+
const emails = splitList(authorEmails);
|
|
11
|
+
return Array.from({ length: Math.max(names.length, emails.length) }, (_, index) => ({
|
|
12
|
+
...(names[index] ? { name: names[index] } : {}),
|
|
13
|
+
...(emails[index] ? { email: emails[index] } : {}),
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
function parseTagReferences(tagNames) {
|
|
17
|
+
return splitList(tagNames).filter((tag) => Boolean(tag));
|
|
18
|
+
}
|
|
19
|
+
class BookshelfPostRelationsResolver {
|
|
20
|
+
_models;
|
|
21
|
+
constructor(models) {
|
|
22
|
+
this._models = models;
|
|
23
|
+
}
|
|
24
|
+
async resolve(data, source, options) {
|
|
25
|
+
const { authors, warnings } = await this.resolveAuthors(source, options);
|
|
26
|
+
const tags = await this.resolveTags(source, options);
|
|
27
|
+
const resolved = {
|
|
28
|
+
...data,
|
|
29
|
+
tags: [...tags, ...data.tags],
|
|
30
|
+
};
|
|
31
|
+
if (authors.length > 0) {
|
|
32
|
+
resolved.authors = authors;
|
|
33
|
+
}
|
|
34
|
+
return { data: resolved, warnings };
|
|
35
|
+
}
|
|
36
|
+
async resolveAuthors(source, options) {
|
|
37
|
+
const authors = [];
|
|
38
|
+
const warnings = [];
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
const authorsByEmail = new Map();
|
|
41
|
+
let owner;
|
|
42
|
+
const useOwner = async (warning) => {
|
|
43
|
+
owner ??= await this._models.User.getOwnerUser({ ...options });
|
|
44
|
+
warnings.push(warning);
|
|
45
|
+
if (!seen.has(owner.id)) {
|
|
46
|
+
seen.add(owner.id);
|
|
47
|
+
authors.push({ id: owner.id });
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
for (const reference of parseAuthorReferences(source.authorNames, source.authorEmails)) {
|
|
51
|
+
let author = null;
|
|
52
|
+
if (reference.email) {
|
|
53
|
+
if (!validator.isEmail(reference.email)) {
|
|
54
|
+
await useOwner(`Author email "${reference.email}" is invalid; assigned Owner instead.`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const emailKey = reference.email.toLowerCase();
|
|
58
|
+
author = authorsByEmail.get(emailKey) ?? null;
|
|
59
|
+
if (!author) {
|
|
60
|
+
author = (await this._models.User.getByEmail(emailKey, { ...options })) ?? null;
|
|
61
|
+
}
|
|
62
|
+
if (!author && reference.name) {
|
|
63
|
+
author = await this._models.User.add({
|
|
64
|
+
name: reference.name,
|
|
65
|
+
email: emailKey,
|
|
66
|
+
roles: ['Contributor'],
|
|
67
|
+
}, { ...options });
|
|
68
|
+
}
|
|
69
|
+
if (!author) {
|
|
70
|
+
await useOwner(`Author email "${reference.email}" has no name; assigned Owner instead.`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
authorsByEmail.set(emailKey, author);
|
|
74
|
+
}
|
|
75
|
+
else if (reference.name) {
|
|
76
|
+
author = await this._models.User.findOne({ slug: slugify(reference.name) }, { ...options });
|
|
77
|
+
if (!author) {
|
|
78
|
+
await useOwner(`Author "${reference.name}" has no email; assigned Owner instead.`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await useOwner('An empty author entry was assigned to Owner instead.');
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (author && !seen.has(author.id)) {
|
|
87
|
+
seen.add(author.id);
|
|
88
|
+
authors.push({ id: author.id });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { authors, warnings };
|
|
92
|
+
}
|
|
93
|
+
async resolveTags(source, options) {
|
|
94
|
+
const tags = [];
|
|
95
|
+
const seen = new Set();
|
|
96
|
+
for (const reference of parseTagReferences(source.tagNames)) {
|
|
97
|
+
const normalizedSlug = slugify(reference);
|
|
98
|
+
const lookups = [{ name: reference }, { slug: reference }];
|
|
99
|
+
if (normalizedSlug && normalizedSlug !== reference) {
|
|
100
|
+
lookups.push({ slug: normalizedSlug });
|
|
101
|
+
}
|
|
102
|
+
const findTag = async (lookupOptions = {}) => {
|
|
103
|
+
for (const lookup of lookups) {
|
|
104
|
+
const tag = await this._models.Tag.findOne(lookup, {
|
|
105
|
+
...options,
|
|
106
|
+
...lookupOptions,
|
|
107
|
+
});
|
|
108
|
+
if (tag) {
|
|
109
|
+
return tag;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
};
|
|
114
|
+
let tag = await findTag();
|
|
115
|
+
if (!tag) {
|
|
116
|
+
try {
|
|
117
|
+
tag = await this._models.Tag.add({ name: reference }, { ...options });
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (!isUniqueConstraintError(error)) {
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
// A concurrent row or import may have created the same slug after our
|
|
124
|
+
// lookup. A locking read sees that committed row under MySQL's default
|
|
125
|
+
// repeatable-read isolation, where another ordinary select may not.
|
|
126
|
+
tag = await findTag({ forUpdate: true });
|
|
127
|
+
if (!tag) {
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (!seen.has(tag.id)) {
|
|
133
|
+
seen.add(tag.id);
|
|
134
|
+
tags.push({ id: tag.id });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return tags;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.BookshelfPostRelationsResolver = BookshelfPostRelationsResolver;
|
|
141
|
+
function splitList(value) {
|
|
142
|
+
return value?.split(',').map((part) => part.trim() || undefined) ?? [];
|
|
143
|
+
}
|
|
144
|
+
function isUniqueConstraintError(error) {
|
|
145
|
+
const code = typeof error === 'object' && error !== null && 'code' in error
|
|
146
|
+
? error.code
|
|
147
|
+
: undefined;
|
|
148
|
+
return (code === 'ER_DUP_ENTRY' || (typeof code === 'string' && code.startsWith('SQLITE_CONSTRAINT')));
|
|
149
|
+
}
|