@shapio/cms 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/apple-touch-icon.png +0 -0
- package/dist/admin/assets/{AssistError-hVUWpP3F.js → AssistError-BYR085vj.js} +2 -2
- package/dist/admin/assets/{AssistError-hVUWpP3F.js.map → AssistError-BYR085vj.js.map} +1 -1
- package/dist/admin/assets/{BlockMenuItems-B0g93m6M.js → BlockMenuItems-BnLF3Efc.js} +2 -2
- package/dist/admin/assets/{BlockMenuItems-B0g93m6M.js.map → BlockMenuItems-BnLF3Efc.js.map} +1 -1
- package/dist/admin/assets/Body-CCOZq2q9.js +2 -0
- package/dist/admin/assets/{Body-CFQKONK4.js.map → Body-CCOZq2q9.js.map} +1 -1
- package/dist/admin/assets/Builder-BZy9mphs.js +2 -0
- package/dist/admin/assets/Builder-BZy9mphs.js.map +1 -0
- package/dist/admin/assets/{ConfirmDialog-D6rs75TP.js → ConfirmDialog-Tu-gjEwm.js} +2 -2
- package/dist/admin/assets/{ConfirmDialog-D6rs75TP.js.map → ConfirmDialog-Tu-gjEwm.js.map} +1 -1
- package/dist/admin/assets/{CopyButton-Bc7crFDE.js → CopyButton-BuBaIlCk.js} +2 -2
- package/dist/admin/assets/{CopyButton-Bc7crFDE.js.map → CopyButton-BuBaIlCk.js.map} +1 -1
- package/dist/admin/assets/{CursorPager-BFPD_adV.js → CursorPager-Bvu3CXpD.js} +2 -2
- package/dist/admin/assets/{CursorPager-BFPD_adV.js.map → CursorPager-Bvu3CXpD.js.map} +1 -1
- package/dist/admin/assets/{ExternalLink-Cdfcyyed.js → ExternalLink-CCr5oRXI.js} +2 -2
- package/dist/admin/assets/{ExternalLink-Cdfcyyed.js.map → ExternalLink-CCr5oRXI.js.map} +1 -1
- package/dist/admin/assets/Form-Doc0JEzF.js +2 -0
- package/dist/admin/assets/Form-Doc0JEzF.js.map +1 -0
- package/dist/admin/assets/{FormCheckboxGroup-Bicl1CBP.js → FormCheckboxGroup-eg39IJvf.js} +2 -2
- package/dist/admin/assets/{FormCheckboxGroup-Bicl1CBP.js.map → FormCheckboxGroup-eg39IJvf.js.map} +1 -1
- package/dist/admin/assets/{FormRadioGroup-34iVSV4L.js → FormRadioGroup-ZppSTZK6.js} +2 -2
- package/dist/admin/assets/{FormRadioGroup-34iVSV4L.js.map → FormRadioGroup-ZppSTZK6.js.map} +1 -1
- package/dist/admin/assets/{FormSelectField-Ba4fIu-8.js → FormSelectField-Cosnxiz-.js} +2 -2
- package/dist/admin/assets/{FormSelectField-Ba4fIu-8.js.map → FormSelectField-Cosnxiz-.js.map} +1 -1
- package/dist/admin/assets/{FormSwitchField-DN6bfPC0.js → FormSwitchField-DsKQEZw6.js} +2 -2
- package/dist/admin/assets/{FormSwitchField-DN6bfPC0.js.map → FormSwitchField-DsKQEZw6.js.map} +1 -1
- package/dist/admin/assets/{FormTextareaField-WsNjLm32.js → FormTextareaField-BXidSgIE.js} +2 -2
- package/dist/admin/assets/{FormTextareaField-WsNjLm32.js.map → FormTextareaField-BXidSgIE.js.map} +1 -1
- package/dist/admin/assets/{HintedLabel-jKLM0ETt.js → HintedLabel-USQku9Yt.js} +2 -2
- package/dist/admin/assets/{HintedLabel-jKLM0ETt.js.map → HintedLabel-USQku9Yt.js.map} +1 -1
- package/dist/admin/assets/{InfoHint-DBqcrmZ3.js → InfoHint-Bz_yvehr.js} +2 -2
- package/dist/admin/assets/{InfoHint-DBqcrmZ3.js.map → InfoHint-Bz_yvehr.js.map} +1 -1
- package/dist/admin/assets/{InsertMenuItem-CRKXblK5.js → InsertMenuItem-DJXglsEd.js} +2 -2
- package/dist/admin/assets/{InsertMenuItem-CRKXblK5.js.map → InsertMenuItem-DJXglsEd.js.map} +1 -1
- package/dist/admin/assets/LinkTabs-D4U-rJyK.js +2 -0
- package/dist/admin/assets/{LinkTabs-Bn2JVqOL.js.map → LinkTabs-D4U-rJyK.js.map} +1 -1
- package/dist/admin/assets/Match-UAw1Ss8a.js +2 -0
- package/dist/admin/assets/{Match-DaYgKrk7.js.map → Match-UAw1Ss8a.js.map} +1 -1
- package/dist/admin/assets/{Panel-6wFe96Gr.js → Panel-BdjqCkKs.js} +2 -2
- package/dist/admin/assets/{Panel-6wFe96Gr.js.map → Panel-BdjqCkKs.js.map} +1 -1
- package/dist/admin/assets/{QueryView-DFZtm0fn.js → QueryView-Bkyizh1P.js} +2 -2
- package/dist/admin/assets/{QueryView-DFZtm0fn.js.map → QueryView-Bkyizh1P.js.map} +1 -1
- package/dist/admin/assets/{RadioTile-DsTyz7ml.js → RadioTile-VHTPl88w.js} +2 -2
- package/dist/admin/assets/RadioTile-VHTPl88w.js.map +1 -0
- package/dist/admin/assets/{RichTextField-BGkH403g.js → RichTextField-B21FqrT5.js} +4 -4
- package/dist/admin/assets/{RichTextField-BGkH403g.js.map → RichTextField-B21FqrT5.js.map} +1 -1
- package/dist/admin/assets/{RowTitle-sFt6NjO4.js → RowTitle-BD8QPs_v.js} +2 -2
- package/dist/admin/assets/{RowTitle-sFt6NjO4.js.map → RowTitle-BD8QPs_v.js.map} +1 -1
- package/dist/admin/assets/{SecretReveal-Z6MOFdcL.js → SecretReveal-BfJKUxXW.js} +2 -2
- package/dist/admin/assets/{SecretReveal-Z6MOFdcL.js.map → SecretReveal-BfJKUxXW.js.map} +1 -1
- package/dist/admin/assets/SharedGlyph-DDPdvsm_.js +2 -0
- package/dist/admin/assets/SharedGlyph-DDPdvsm_.js.map +1 -0
- package/dist/admin/assets/{StepList-BTP_EMzx.js → StepList-DKhkw1hN.js} +2 -2
- package/dist/admin/assets/{StepList-BTP_EMzx.js.map → StepList-DKhkw1hN.js.map} +1 -1
- package/dist/admin/assets/Structure-C7dr00Bp.js +2 -0
- package/dist/admin/assets/{Structure-D5-hCUuF.js.map → Structure-C7dr00Bp.js.map} +1 -1
- package/dist/admin/assets/SubNavLink-BAF2-wuF.js +2 -0
- package/dist/admin/assets/{SubNavLink-C5oN5mzB.js.map → SubNavLink-BAF2-wuF.js.map} +1 -1
- package/dist/admin/assets/{SuggestAltButton-BuN35BBL.js → SuggestAltButton-DEsbHvs4.js} +2 -2
- package/dist/admin/assets/{SuggestAltButton-BuN35BBL.js.map → SuggestAltButton-DEsbHvs4.js.map} +1 -1
- package/dist/admin/assets/TextLink-CegO3jom.js +2 -0
- package/dist/admin/assets/{TextLink-B4a6j3r6.js.map → TextLink-CegO3jom.js.map} +1 -1
- package/dist/admin/assets/{Thumbnail-u8NE5NhQ.js → Thumbnail-CTpKlrxR.js} +2 -2
- package/dist/admin/assets/{Thumbnail-u8NE5NhQ.js.map → Thumbnail-CTpKlrxR.js.map} +1 -1
- package/dist/admin/assets/UnsavedChangesGuard-Cbfg-Bgj.js +2 -0
- package/dist/admin/assets/{UnsavedChangesGuard-BYwMTDsE.js.map → UnsavedChangesGuard-Cbfg-Bgj.js.map} +1 -1
- package/dist/admin/assets/{UserAvatar-D-yX0ozh.js → UserAvatar-CiZ00-cr.js} +2 -2
- package/dist/admin/assets/{UserAvatar-D-yX0ozh.js.map → UserAvatar-CiZ00-cr.js.map} +1 -1
- package/dist/admin/assets/{arrow-up-Ws7W9KsZ.js → arrow-up-DwMJqZVV.js} +2 -2
- package/dist/admin/assets/{arrow-up-Ws7W9KsZ.js.map → arrow-up-DwMJqZVV.js.map} +1 -1
- package/dist/admin/assets/auth-BaVAo0u_.js +2 -0
- package/dist/admin/assets/auth-BaVAo0u_.js.map +1 -0
- package/dist/admin/assets/badge-IBeVOhKd.js +2 -0
- package/dist/admin/assets/{badge-CMox6-v3.js.map → badge-IBeVOhKd.js.map} +1 -1
- package/dist/admin/assets/boxes-BH0MDegO.js +2 -0
- package/dist/admin/assets/boxes-BH0MDegO.js.map +1 -0
- package/dist/admin/assets/{calendar-clock-9W0Lkt89.js → calendar-clock-DKUk-w81.js} +2 -2
- package/dist/admin/assets/{calendar-clock-9W0Lkt89.js.map → calendar-clock-DKUk-w81.js.map} +1 -1
- package/dist/admin/assets/{changeSets-CO7JdILW.js → changeSets-o_G67qPk.js} +2 -2
- package/dist/admin/assets/{changeSets-CO7JdILW.js.map → changeSets-o_G67qPk.js.map} +1 -1
- package/dist/admin/assets/chevron-left-D259hWJn.js +2 -0
- package/dist/admin/assets/{chevron-left-DyqYjcMB.js.map → chevron-left-D259hWJn.js.map} +1 -1
- package/dist/admin/assets/{circle-check-aJ2DW5t4.js → circle-check-Cidzt1Ww.js} +2 -2
- package/dist/admin/assets/{circle-check-aJ2DW5t4.js.map → circle-check-Cidzt1Ww.js.map} +1 -1
- package/dist/admin/assets/{circle-x-BnRBht9z.js → circle-x-c1RwmC6j.js} +2 -2
- package/dist/admin/assets/{circle-x-BnRBht9z.js.map → circle-x-c1RwmC6j.js.map} +1 -1
- package/dist/admin/assets/{clock-DRtFQbTs.js → clock-BOtPNp9-.js} +2 -2
- package/dist/admin/assets/{clock-DRtFQbTs.js.map → clock-BOtPNp9-.js.map} +1 -1
- package/dist/admin/assets/cn-M4_WHiur.js +2 -0
- package/dist/admin/assets/cn-M4_WHiur.js.map +1 -0
- package/dist/admin/assets/coerce-DWI5sgLW.js +2 -0
- package/dist/admin/assets/{coerce-COM3rk1i.js.map → coerce-DWI5sgLW.js.map} +1 -1
- package/dist/admin/assets/constants-CgUdkWmf.js +2 -0
- package/dist/admin/assets/constants-CgUdkWmf.js.map +1 -0
- package/dist/admin/assets/content-D25ainqV.js +3 -0
- package/dist/admin/assets/content-D25ainqV.js.map +1 -0
- package/dist/admin/assets/{contentCounts-Ct0tYgHD.js → contentCounts-T-LmvUf5.js} +2 -2
- package/dist/admin/assets/{contentCounts-Ct0tYgHD.js.map → contentCounts-T-LmvUf5.js.map} +1 -1
- package/dist/admin/assets/contentTypes-BSlp81l1.js +2 -0
- package/dist/admin/assets/contentTypes-BSlp81l1.js.map +1 -0
- package/dist/admin/assets/{copy-B5B8RjV0.js → copy-DT7dAzVm.js} +2 -2
- package/dist/admin/assets/{copy-B5B8RjV0.js.map → copy-DT7dAzVm.js.map} +1 -1
- package/dist/admin/assets/{dateTimeLocal-BuBv3it2.js → dateTimeLocal-DRq7DuYO.js} +2 -2
- package/dist/admin/assets/{dateTimeLocal-BuBv3it2.js.map → dateTimeLocal-DRq7DuYO.js.map} +1 -1
- package/dist/admin/assets/develop-qF10t9tM.js +11 -0
- package/dist/admin/assets/develop-qF10t9tM.js.map +1 -0
- package/dist/admin/assets/{dropdown-menu-CW3fabv5.js → dropdown-menu-Cl20LCvY.js} +2 -2
- package/dist/admin/assets/{dropdown-menu-CW3fabv5.js.map → dropdown-menu-Cl20LCvY.js.map} +1 -1
- package/dist/admin/assets/{ellipsis-DNdmromA.js → ellipsis-xDmHl1SK.js} +2 -2
- package/dist/admin/assets/{ellipsis-DNdmromA.js.map → ellipsis-xDmHl1SK.js.map} +1 -1
- package/dist/admin/assets/{external-link-YnuWXDBk.js → external-link-DtJAaOWk.js} +2 -2
- package/dist/admin/assets/{external-link-YnuWXDBk.js.map → external-link-DtJAaOWk.js.map} +1 -1
- package/dist/admin/assets/{file-text-CB7Bc0GA.js → file-text-BJkQMeQO.js} +2 -2
- package/dist/admin/assets/{file-text-CB7Bc0GA.js.map → file-text-BJkQMeQO.js.map} +1 -1
- package/dist/admin/assets/fileRoute-Buucm0CU.js +2 -0
- package/dist/admin/assets/{fileRoute-BYwireXG.js.map → fileRoute-Buucm0CU.js.map} +1 -1
- package/dist/admin/assets/followPlural-Cuocm4Sv.js +2 -0
- package/dist/admin/assets/followPlural-Cuocm4Sv.js.map +1 -0
- package/dist/admin/assets/{globe-Bvs3UC3V.js → globe-CAhrwBgv.js} +2 -2
- package/dist/admin/assets/{globe-Bvs3UC3V.js.map → globe-CAhrwBgv.js.map} +1 -1
- package/dist/admin/assets/{image-CeoxwJeM.js → image-CGN6ffEu.js} +2 -2
- package/dist/admin/assets/{image-CeoxwJeM.js.map → image-CGN6ffEu.js.map} +1 -1
- package/dist/admin/assets/{image-off-iVqYw2Zc.js → image-off-0zX_kODH.js} +2 -2
- package/dist/admin/assets/{image-off-iVqYw2Zc.js.map → image-off-0zX_kODH.js.map} +1 -1
- package/dist/admin/assets/inbox-CWq1hHqK.js +2 -0
- package/dist/admin/assets/{inbox-wDoqym--.js.map → inbox-CWq1hHqK.js.map} +1 -1
- package/dist/admin/assets/{inbox-DYbEOpaQ.js → inbox-CukkApHK.js} +2 -2
- package/dist/admin/assets/{inbox-DYbEOpaQ.js.map → inbox-CukkApHK.js.map} +1 -1
- package/dist/admin/assets/index-C9uJxg4J.js +4 -0
- package/dist/admin/assets/index-C9uJxg4J.js.map +1 -0
- package/dist/admin/assets/index-Cz3C-LxB.css +2 -0
- package/dist/admin/assets/{key-round-Bcw6qqcb.js → key-round-DnE-2gr0.js} +2 -2
- package/dist/admin/assets/{key-round-Bcw6qqcb.js.map → key-round-DnE-2gr0.js.map} +1 -1
- package/dist/admin/assets/link-2-off-Cbpvb3w4.js +2 -0
- package/dist/admin/assets/link-2-off-Cbpvb3w4.js.map +1 -0
- package/dist/admin/assets/{link-CKr_uUfb.js → link-DPniuk00.js} +2 -2
- package/dist/admin/assets/{link-CKr_uUfb.js.map → link-DPniuk00.js.map} +1 -1
- package/dist/admin/assets/{list-BElvKZw_.js → list-CkHF_4xh.js} +2 -2
- package/dist/admin/assets/{list-BElvKZw_.js.map → list-CkHF_4xh.js.map} +1 -1
- package/dist/admin/assets/{locales-Bnv3qcdZ.js → locales-DFLQ17ow.js} +2 -2
- package/dist/admin/assets/{locales-Bnv3qcdZ.js.map → locales-DFLQ17ow.js.map} +1 -1
- package/dist/admin/assets/{media-BfDqe_2P.js → media-CKTs3j83.js} +2 -2
- package/dist/admin/assets/{media-BfDqe_2P.js.map → media-CKTs3j83.js.map} +1 -1
- package/dist/admin/assets/media-CUfV74JZ.js +2 -0
- package/dist/admin/assets/{media-DjK3xGFN.js.map → media-CUfV74JZ.js.map} +1 -1
- package/dist/admin/assets/network-Fri1cjMt.js +2 -0
- package/dist/admin/assets/network-Fri1cjMt.js.map +1 -0
- package/dist/admin/assets/{pencil-Zd05MDs2.js → pencil-BqAgKC9v.js} +2 -2
- package/dist/admin/assets/{pencil-Zd05MDs2.js.map → pencil-BqAgKC9v.js.map} +1 -1
- package/dist/admin/assets/{plus-B0gd63Da.js → plus-DLRcpVhJ.js} +2 -2
- package/dist/admin/assets/{plus-B0gd63Da.js.map → plus-DLRcpVhJ.js.map} +1 -1
- package/dist/admin/assets/{preload-helper-CJKHb_rG.js → preload-helper-BHNBIF88.js} +2 -2
- package/dist/admin/assets/{preload-helper-CJKHb_rG.js.map → preload-helper-BHNBIF88.js.map} +1 -1
- package/dist/admin/assets/publishing-DaaE8PaU.js +2 -0
- package/dist/admin/assets/{publishing-vCVjaSGF.js.map → publishing-DaaE8PaU.js.map} +1 -1
- package/dist/admin/assets/{radio-group-DVR0mTpo.js → radio-group-CA3XlQcc.js} +2 -2
- package/dist/admin/assets/{radio-group-DVR0mTpo.js.map → radio-group-CA3XlQcc.js.map} +1 -1
- package/dist/admin/assets/{rotate-ccw-BOTDOdHf.js → rotate-ccw-CfrkcgYa.js} +2 -2
- package/dist/admin/assets/{rotate-ccw-BOTDOdHf.js.map → rotate-ccw-CfrkcgYa.js.map} +1 -1
- package/dist/admin/assets/{schedules-CG-ZphRK.js → schedules-hJQkjEhN.js} +2 -2
- package/dist/admin/assets/{schedules-CG-ZphRK.js.map → schedules-hJQkjEhN.js.map} +1 -1
- package/dist/admin/assets/schema--xSR7cf9.js +2 -0
- package/dist/admin/assets/schema--xSR7cf9.js.map +1 -0
- package/dist/admin/assets/{scroll-text-D-gHqYwR.js → scroll-text-BA9yh7Za.js} +2 -2
- package/dist/admin/assets/{scroll-text-D-gHqYwR.js.map → scroll-text-BA9yh7Za.js.map} +1 -1
- package/dist/admin/assets/{search-BcPL1qyc.js → search--71HzPvh.js} +2 -2
- package/dist/admin/assets/{search-BcPL1qyc.js.map → search--71HzPvh.js.map} +1 -1
- package/dist/admin/assets/searchSchema-Ypl6gq9g.js +2 -0
- package/dist/admin/assets/{searchSchema-BGrRDBL_.js.map → searchSchema-Ypl6gq9g.js.map} +1 -1
- package/dist/admin/assets/searchSchemas-C-nQbyhd.js +2 -0
- package/dist/admin/assets/searchSchemas-C-nQbyhd.js.map +1 -0
- package/dist/admin/assets/sections-BBN2JErq.js +2 -0
- package/dist/admin/assets/sections-BBN2JErq.js.map +1 -0
- package/dist/admin/assets/{sections-i9TeeK-h.js → sections-D0OZozD2.js} +2 -2
- package/dist/admin/assets/{sections-i9TeeK-h.js.map → sections-D0OZozD2.js.map} +1 -1
- package/dist/admin/assets/{select--nGB4WjU.js → select-De48SQdZ.js} +2 -2
- package/dist/admin/assets/{select--nGB4WjU.js.map → select-De48SQdZ.js.map} +1 -1
- package/dist/admin/assets/{send-Bu3RRfdI.js → send-NuSmLVSD.js} +2 -2
- package/dist/admin/assets/{send-Bu3RRfdI.js.map → send-NuSmLVSD.js.map} +1 -1
- package/dist/admin/assets/settings-CYXU73dP.js +2 -0
- package/dist/admin/assets/settings-CYXU73dP.js.map +1 -0
- package/dist/admin/assets/skeleton-CCwdO9oq.js +42 -0
- package/dist/admin/assets/skeleton-CCwdO9oq.js.map +1 -0
- package/dist/admin/assets/snapshots-BQeFK5cj.js +2 -0
- package/dist/admin/assets/{snapshots-CKjK-vkL.js.map → snapshots-BQeFK5cj.js.map} +1 -1
- package/dist/admin/assets/{sparkles-B_NX7PAY.js → sparkles-B-0I44es.js} +2 -2
- package/dist/admin/assets/{sparkles-B_NX7PAY.js.map → sparkles-B-0I44es.js.map} +1 -1
- package/dist/admin/assets/src-CQu-YxUM.js +4 -0
- package/dist/admin/assets/src-CQu-YxUM.js.map +1 -0
- package/dist/admin/assets/{stableId-Dmr2Afn_.js → stableId-rPnYVF-A.js} +2 -2
- package/dist/admin/assets/{stableId-Dmr2Afn_.js.map → stableId-rPnYVF-A.js.map} +1 -1
- package/dist/admin/assets/{store-B5xrQ1Ex.js → store-Bqd9L9DK.js} +2 -2
- package/dist/admin/assets/{store-B5xrQ1Ex.js.map → store-Bqd9L9DK.js.map} +1 -1
- package/dist/admin/assets/{switch-B-FbT-l8.js → switch-JmfyFAQG.js} +2 -2
- package/dist/admin/assets/{switch-B-FbT-l8.js.map → switch-JmfyFAQG.js.map} +1 -1
- package/dist/admin/assets/{table-DQzCPm7J.js → table-BEDv9IRM.js} +2 -2
- package/dist/admin/assets/{table-DQzCPm7J.js.map → table-BEDv9IRM.js.map} +1 -1
- package/dist/admin/assets/tabs-DSS1c-Kf.js +5 -0
- package/dist/admin/assets/{tabs-YThoOiE3.js.map → tabs-DSS1c-Kf.js.map} +1 -1
- package/dist/admin/assets/{textarea-_wcx8Mn1.js → textarea-eatLAWE_.js} +2 -2
- package/dist/admin/assets/{textarea-_wcx8Mn1.js.map → textarea-eatLAWE_.js.map} +1 -1
- package/dist/admin/assets/titles-Bi2fWNN4.js +2 -0
- package/dist/admin/assets/{titles-DyCBLXue.js.map → titles-Bi2fWNN4.js.map} +1 -1
- package/dist/admin/assets/{tokens-CqsxrApC.js → tokens-C2E7swse.js} +2 -2
- package/dist/admin/assets/{tokens-CqsxrApC.js.map → tokens-C2E7swse.js.map} +1 -1
- package/dist/admin/assets/{tooltip-BMe4HylJ.js → tooltip-Bji9fmSr.js} +2 -2
- package/dist/admin/assets/{tooltip-BMe4HylJ.js.map → tooltip-Bji9fmSr.js.map} +1 -1
- package/dist/admin/assets/{undo-2-DMOHOH6w.js → undo-2-BU-uqm7e.js} +2 -2
- package/dist/admin/assets/{undo-2-DMOHOH6w.js.map → undo-2-BU-uqm7e.js.map} +1 -1
- package/dist/admin/assets/{useAuditLabels-CSw_kwLs.js → useAuditLabels-D5nmbXqN.js} +2 -2
- package/dist/admin/assets/{useAuditLabels-CSw_kwLs.js.map → useAuditLabels-D5nmbXqN.js.map} +1 -1
- package/dist/admin/assets/useConfirmTarget-Y7_8kVX5.js +2 -0
- package/dist/admin/assets/useConfirmTarget-Y7_8kVX5.js.map +1 -0
- package/dist/admin/assets/useContentSchema-DJTqjlUk.js +2 -0
- package/dist/admin/assets/{useContentSchema-CS5QQcRk.js.map → useContentSchema-DJTqjlUk.js.map} +1 -1
- package/dist/admin/assets/{useCursorPager-Dt_axwd1.js → useCursorPager-CiJroOo7.js} +2 -2
- package/dist/admin/assets/{useCursorPager-Dt_axwd1.js.map → useCursorPager-CiJroOo7.js.map} +1 -1
- package/dist/admin/assets/{useDiscardGuard-7rRGpd4q.js → useDiscardGuard-t_N3l9TQ.js} +2 -2
- package/dist/admin/assets/{useDiscardGuard-7rRGpd4q.js.map → useDiscardGuard-t_N3l9TQ.js.map} +1 -1
- package/dist/admin/assets/{useMediaUploader-CBUcNW6d.js → useMediaUploader-CLHmKCwq.js} +2 -2
- package/dist/admin/assets/{useMediaUploader-CBUcNW6d.js.map → useMediaUploader-CLHmKCwq.js.map} +1 -1
- package/dist/admin/assets/{usePaletteItems-bbTIqpPu.js → usePaletteItems-BVjWCdow.js} +2 -2
- package/dist/admin/assets/{usePaletteItems-bbTIqpPu.js.map → usePaletteItems-BVjWCdow.js.map} +1 -1
- package/dist/admin/assets/usePlaces-CnSOZwtO.js +2 -0
- package/dist/admin/assets/usePlaces-CnSOZwtO.js.map +1 -0
- package/dist/admin/assets/{useQueries-DKGIdq6A.js → useQueries-BRolGUiw.js} +2 -2
- package/dist/admin/assets/{useQueries-DKGIdq6A.js.map → useQueries-BRolGUiw.js.map} +1 -1
- package/dist/admin/assets/useSchemaScopeAccess-bd8RQxuu.js +2 -0
- package/dist/admin/assets/useSchemaScopeAccess-bd8RQxuu.js.map +1 -0
- package/dist/admin/assets/{users-C1ZSee3N.js → users-CbErtM7w.js} +2 -2
- package/dist/admin/assets/{users-C1ZSee3N.js.map → users-CbErtM7w.js.map} +1 -1
- package/dist/admin/assets/{users-DMfQL0s4.js → users-bDGYpSXg.js} +2 -2
- package/dist/admin/assets/{users-DMfQL0s4.js.map → users-bDGYpSXg.js.map} +1 -1
- package/dist/admin/assets/{zod-Pf5A-ax4.js → zod-Gtw4e0qi.js} +2 -2
- package/dist/admin/assets/{zod-Pf5A-ax4.js.map → zod-Gtw4e0qi.js.map} +1 -1
- package/dist/admin/favicon-32.png +0 -0
- package/dist/admin/favicon.svg +2 -2
- package/dist/admin/index.html +61 -56
- package/dist/admin/theme-init.js +41 -9
- package/dist/cli.js +412 -168
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +15 -223
- package/dist/index.js +1 -1
- package/dist/{public-CKU0Gkfq.d.ts → public-B7fFHMNr.d.ts} +259 -5
- package/dist/{worker-C-MSYr4l.js → worker-BAmQPoTJ.js} +2867 -1314
- package/dist/worker-BAmQPoTJ.js.map +1 -0
- package/package.json +5 -6
- package/dist/admin/assets/Body-CFQKONK4.js +0 -2
- package/dist/admin/assets/Builder-Do5KVHV9.js +0 -2
- package/dist/admin/assets/Builder-Do5KVHV9.js.map +0 -1
- package/dist/admin/assets/LinkTabs-Bn2JVqOL.js +0 -2
- package/dist/admin/assets/Match-DaYgKrk7.js +0 -2
- package/dist/admin/assets/RadioTile-DsTyz7ml.js.map +0 -1
- package/dist/admin/assets/Structure-D5-hCUuF.js +0 -2
- package/dist/admin/assets/SubNavLink-C5oN5mzB.js +0 -2
- package/dist/admin/assets/TextLink-B4a6j3r6.js +0 -2
- package/dist/admin/assets/UnsavedChangesGuard-BYwMTDsE.js +0 -2
- package/dist/admin/assets/auth-b3_9QbtL.js +0 -2
- package/dist/admin/assets/auth-b3_9QbtL.js.map +0 -1
- package/dist/admin/assets/badge-CMox6-v3.js +0 -2
- package/dist/admin/assets/chevron-left-DyqYjcMB.js +0 -2
- package/dist/admin/assets/cn-DnXL75B0.js +0 -2
- package/dist/admin/assets/cn-DnXL75B0.js.map +0 -1
- package/dist/admin/assets/coerce-COM3rk1i.js +0 -2
- package/dist/admin/assets/constants-CS1ziZO0.js +0 -2
- package/dist/admin/assets/constants-CS1ziZO0.js.map +0 -1
- package/dist/admin/assets/content-Dic2fof_.js +0 -3
- package/dist/admin/assets/content-Dic2fof_.js.map +0 -1
- package/dist/admin/assets/contentTypes-D4_Ax9rb.js +0 -2
- package/dist/admin/assets/contentTypes-D4_Ax9rb.js.map +0 -1
- package/dist/admin/assets/develop-Brnb5YPC.js +0 -11
- package/dist/admin/assets/develop-Brnb5YPC.js.map +0 -1
- package/dist/admin/assets/errors-DfoXUVDI.js +0 -2
- package/dist/admin/assets/errors-DfoXUVDI.js.map +0 -1
- package/dist/admin/assets/fileRoute-BYwireXG.js +0 -2
- package/dist/admin/assets/inbox-wDoqym--.js +0 -2
- package/dist/admin/assets/index-57Mn6XLO.js +0 -4
- package/dist/admin/assets/index-57Mn6XLO.js.map +0 -1
- package/dist/admin/assets/index-BLYOgCKc.css +0 -2
- package/dist/admin/assets/link-2-DhM2vi4v.js +0 -2
- package/dist/admin/assets/link-2-DhM2vi4v.js.map +0 -1
- package/dist/admin/assets/link-2-off-DpDOMsa-.js +0 -2
- package/dist/admin/assets/link-2-off-DpDOMsa-.js.map +0 -1
- package/dist/admin/assets/media-DjK3xGFN.js +0 -2
- package/dist/admin/assets/network-CAJ76ILr.js +0 -2
- package/dist/admin/assets/network-CAJ76ILr.js.map +0 -1
- package/dist/admin/assets/publishing-vCVjaSGF.js +0 -2
- package/dist/admin/assets/schema-KXXSPVJn.js +0 -2
- package/dist/admin/assets/schema-KXXSPVJn.js.map +0 -1
- package/dist/admin/assets/searchSchema-BGrRDBL_.js +0 -2
- package/dist/admin/assets/searchSchemas-ChN9hhuQ.js +0 -2
- package/dist/admin/assets/searchSchemas-ChN9hhuQ.js.map +0 -1
- package/dist/admin/assets/sections-BrGcWOnw.js +0 -2
- package/dist/admin/assets/sections-BrGcWOnw.js.map +0 -1
- package/dist/admin/assets/settings-68BAxrPM.js +0 -2
- package/dist/admin/assets/settings-68BAxrPM.js.map +0 -1
- package/dist/admin/assets/skeleton-DUvclBAr.js +0 -42
- package/dist/admin/assets/skeleton-DUvclBAr.js.map +0 -1
- package/dist/admin/assets/snapshots-CKjK-vkL.js +0 -2
- package/dist/admin/assets/src-D6DABe5J.js +0 -4
- package/dist/admin/assets/src-D6DABe5J.js.map +0 -1
- package/dist/admin/assets/tabs-YThoOiE3.js +0 -5
- package/dist/admin/assets/titles-DyCBLXue.js +0 -2
- package/dist/admin/assets/useConfirmTarget-9JmfiY2X.js +0 -2
- package/dist/admin/assets/useConfirmTarget-9JmfiY2X.js.map +0 -1
- package/dist/admin/assets/useContentSchema-CS5QQcRk.js +0 -2
- package/dist/admin/assets/usePlaces-D1raoQ2s.js +0 -2
- package/dist/admin/assets/usePlaces-D1raoQ2s.js.map +0 -1
- package/dist/worker-C-MSYr4l.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as update, B as
|
|
2
|
+
import { $ as toTypeName, A as update, B as checkThemeContrast, C as BEFORE_EVENTS, D as listForAssets, E as buildEditorManifest, F as RESERVED_FIELD_API_KEYS, G as mergePulledLock, H as hashDefinition, I as RESERVED_TYPE_NAMES, J as schemaFilePath, K as parseDefinition, L as SCHEMA_KIND_DIRECTORIES, M as describeError, O as listLiveByDriver, P as RESERVED_DEFINITION_API_KEYS, Q as suggestPlural, R as SITES_DIRECTORY, S as AFTER_EVENTS, T as EXTENSION_ROUTE_ROOT, U as lockCoversSite, V as foldApiKey, W as lockEntriesForSite, X as serializeDefinition, Y as scopeDirectory, Z as serializeLockFile, _ as createLogger, a as loadConfig, at as createStableId, b as SITE_KEY_PATTERN, c as getPendingMigrations, ct as sqliteDriverOf, d as createMediaStorage, et as upgradeLockFile, f as createSite, g as generateToken, h as SYSTEM_CLI_ACTOR, i as assertDatabaseReachable, it as SAFE_LINK_PATTERN, j as STORAGE_DRIVERS, k as lockById, l as migrateToLatest, m as createAdminDirect, n as startServer, nt as withSiteCovered, o as loadEnvFileIfPresent, ot as validateRichText, p as listAllSites, q as parseLockFile, r as copyObjectVerified, rt as RICHTEXT_FORMAT, st as createDb, t as startDedicatedWorker, tt as validateSchema, u as SHAPIO_VERSION, v as createUrlBuilder, w as EXTENSION_JOB_PREFIX, x as loadProjectConfig, y as loopbackUrl, z as THEME_VARIANTS } from "./worker-BAmQPoTJ.js";
|
|
3
3
|
import { createDecipheriv, createHash, scryptSync } from "node:crypto";
|
|
4
4
|
import { createReadStream, createWriteStream, existsSync, openAsBlob } from "node:fs";
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, posix, relative, resolve } from "node:path";
|
|
@@ -17,7 +17,7 @@ import { parseFragment } from "parse5";
|
|
|
17
17
|
import { marked } from "marked";
|
|
18
18
|
import { extract } from "tar";
|
|
19
19
|
import { SaxesParser } from "saxes";
|
|
20
|
-
//#region ../../packages/client/dist/errors-
|
|
20
|
+
//#region ../../packages/client/dist/errors-DAG8eK8c.js
|
|
21
21
|
/** Every admin route the client calls (packages B, D, F and G, `apps/api/src/routes/admin/**`). */
|
|
22
22
|
const ADMIN_PATHS = {
|
|
23
23
|
setup: "/api/admin/setup",
|
|
@@ -54,6 +54,7 @@ const ADMIN_PATHS = {
|
|
|
54
54
|
contentHealth: "/api/admin/content-health",
|
|
55
55
|
presence: "/api/admin/presence",
|
|
56
56
|
editorManifest: "/api/admin/extensions/editors",
|
|
57
|
+
extensionThemes: "/api/admin/extensions/themes",
|
|
57
58
|
jobs: "/api/admin/jobs",
|
|
58
59
|
schedules: "/api/admin/publishing/schedules",
|
|
59
60
|
changeSets: "/api/admin/change-sets",
|
|
@@ -355,9 +356,12 @@ const createContentApi = (request) => ({
|
|
|
355
356
|
body: { expectedVersion }
|
|
356
357
|
})
|
|
357
358
|
},
|
|
358
|
-
extensions: {
|
|
359
|
-
|
|
360
|
-
editors: () => request(ADMIN_PATHS.editorManifest)
|
|
359
|
+
extensions: {
|
|
360
|
+
/** Custom field editors the project installed (`shapio.config` + `extensions/editors/*.js`). */
|
|
361
|
+
editors: () => request(ADMIN_PATHS.editorManifest),
|
|
362
|
+
/** Admin colour themes the project declares (`shapio.config` `themes`). Public: no session needed. */
|
|
363
|
+
themes: () => request(ADMIN_PATHS.extensionThemes)
|
|
364
|
+
}
|
|
361
365
|
});
|
|
362
366
|
const entryPath = (base, modelKey, id) => withId(withId(base, modelKey), id);
|
|
363
367
|
/** The entry document's endpoints: pre-flight, content health (the Inbox), presence and counts. */
|
|
@@ -472,7 +476,8 @@ const DEFINITION_PATHS = {
|
|
|
472
476
|
const createDefinitionApi = (request, category) => {
|
|
473
477
|
const base = DEFINITION_PATHS[category];
|
|
474
478
|
return {
|
|
475
|
-
|
|
479
|
+
/** The site's view (shared definitions and its own); `{ scope: 'network' }` lists the shared ones. */
|
|
480
|
+
list: async (options = {}) => (await request(`${base}${toQueryString(options)}`)).items,
|
|
476
481
|
get: (id) => request(withId(base, id)),
|
|
477
482
|
/** Creates and activates a definition (`pending` when prerequisites must run first). */
|
|
478
483
|
create: (body) => request(base, {
|
|
@@ -480,9 +485,17 @@ const createDefinitionApi = (request, category) => {
|
|
|
480
485
|
body
|
|
481
486
|
}),
|
|
482
487
|
/** Plan preview of a create: classification and impact, nothing written. */
|
|
483
|
-
planCreate: (definition) => request(`${base}/plan`, {
|
|
488
|
+
planCreate: (definition, scope) => request(`${base}/plan`, {
|
|
484
489
|
method: "POST",
|
|
485
|
-
body:
|
|
490
|
+
body: scope ? {
|
|
491
|
+
definition,
|
|
492
|
+
scope
|
|
493
|
+
} : { definition }
|
|
494
|
+
}),
|
|
495
|
+
/** Shares a definition with all sites, or keeps it on one (needs schema.create on every site). */
|
|
496
|
+
changeScope: (id, body) => request(`${withId(base, id)}/scope`, {
|
|
497
|
+
method: "PUT",
|
|
498
|
+
body
|
|
486
499
|
}),
|
|
487
500
|
/** Changes a definition; 409 SCHEMA_VERSION_CONFLICT when `expectedVersion` is stale. */
|
|
488
501
|
update: (id, body) => request(withId(base, id), {
|
|
@@ -508,7 +521,7 @@ const createSchemaApi = (request) => ({
|
|
|
508
521
|
summary: () => request(ADMIN_PATHS.schema),
|
|
509
522
|
change: (changeId) => request(withId(ADMIN_PATHS.schemaChanges, changeId)),
|
|
510
523
|
/** Every definition in the pull format, with the schema version they come from (`shapio schema pull`). */
|
|
511
|
-
export: () => request(ADMIN_PATHS.schemaExport),
|
|
524
|
+
export: (options = {}) => request(`${ADMIN_PATHS.schemaExport}${toQueryString(options)}`),
|
|
512
525
|
/** Applies definition files live through the change planner; refuses when the base moved (409). */
|
|
513
526
|
apply: (body) => request(ADMIN_PATHS.schemaApply, {
|
|
514
527
|
method: "POST",
|
|
@@ -2802,41 +2815,48 @@ const writeOrder = (entries) => {
|
|
|
2802
2815
|
deferred
|
|
2803
2816
|
};
|
|
2804
2817
|
};
|
|
2805
|
-
/**
|
|
2806
|
-
const
|
|
2807
|
-
const definitionPath = (dir, definition) => join(dir, definition.kind === "component" ? "components" : "models", `${definition.apiKey}.json`);
|
|
2818
|
+
/** The file of a definition in a scope (`site`: its site key, null when shared). */
|
|
2819
|
+
const definitionPath = (dir, definition, site) => join(dir, ...schemaFilePath(definition, site).split("/"));
|
|
2808
2820
|
const isMissing = (error) => error.code === "ENOENT";
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
names = (await readdir(join(dir, kindDir))).filter((name) => name.endsWith(".json")).sort();
|
|
2816
|
-
} catch (error) {
|
|
2817
|
-
if (isMissing(error)) continue;
|
|
2818
|
-
throw error;
|
|
2819
|
-
}
|
|
2820
|
-
for (const name of names) {
|
|
2821
|
-
const path = join(dir, kindDir, name);
|
|
2822
|
-
let raw;
|
|
2823
|
-
try {
|
|
2824
|
-
raw = JSON.parse(await readFile(path, "utf8"));
|
|
2825
|
-
} catch (error) {
|
|
2826
|
-
throw new Error(`${path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
2827
|
-
}
|
|
2828
|
-
const parsed = parseDefinition(raw);
|
|
2829
|
-
files.push(parsed.ok ? {
|
|
2830
|
-
path,
|
|
2831
|
-
raw,
|
|
2832
|
-
id: parsed.definition.id,
|
|
2833
|
-
hash: await hashDefinition(parsed.definition)
|
|
2834
|
-
} : {
|
|
2835
|
-
path,
|
|
2836
|
-
raw
|
|
2837
|
-
});
|
|
2838
|
-
}
|
|
2821
|
+
const listNames = async (path, filter) => {
|
|
2822
|
+
try {
|
|
2823
|
+
return (await readdir(path, { withFileTypes: true })).filter(filter).map((entry) => entry.name).sort();
|
|
2824
|
+
} catch (error) {
|
|
2825
|
+
if (isMissing(error)) return [];
|
|
2826
|
+
throw error;
|
|
2839
2827
|
}
|
|
2828
|
+
};
|
|
2829
|
+
const readLocalFile = async (path, site) => {
|
|
2830
|
+
let raw;
|
|
2831
|
+
try {
|
|
2832
|
+
raw = JSON.parse(await readFile(path, "utf8"));
|
|
2833
|
+
} catch (error) {
|
|
2834
|
+
throw new Error(`${path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
2835
|
+
}
|
|
2836
|
+
const parsed = parseDefinition(raw);
|
|
2837
|
+
return parsed.ok ? {
|
|
2838
|
+
path,
|
|
2839
|
+
raw,
|
|
2840
|
+
site,
|
|
2841
|
+
id: parsed.definition.id,
|
|
2842
|
+
hash: await hashDefinition(parsed.definition)
|
|
2843
|
+
} : {
|
|
2844
|
+
path,
|
|
2845
|
+
raw,
|
|
2846
|
+
site
|
|
2847
|
+
};
|
|
2848
|
+
};
|
|
2849
|
+
/** The files of one scope's folder (`models/` and `components/` under it). */
|
|
2850
|
+
const readScopeFiles = async (dir, site) => {
|
|
2851
|
+
const base = join(dir, ...scopeDirectory(site).split("/").filter(Boolean));
|
|
2852
|
+
const files = [];
|
|
2853
|
+
for (const kindDir of SCHEMA_KIND_DIRECTORIES) for (const name of await listNames(join(base, kindDir), (entry) => entry.isFile() && entry.name.endsWith(".json"))) files.push(await readLocalFile(join(base, kindDir, name), site));
|
|
2854
|
+
return files;
|
|
2855
|
+
};
|
|
2856
|
+
/** Every schema file under `dir`: the shared folders first, then each site folder by key. */
|
|
2857
|
+
const readLocalFiles = async (dir) => {
|
|
2858
|
+
const files = await readScopeFiles(dir, null);
|
|
2859
|
+
for (const siteKey of await listNames(join(dir, SITES_DIRECTORY), (entry) => entry.isDirectory() && !entry.name.startsWith("."))) files.push(...await readScopeFiles(dir, siteKey));
|
|
2840
2860
|
return files;
|
|
2841
2861
|
};
|
|
2842
2862
|
const readLockFile = async (path) => {
|
|
@@ -2851,8 +2871,8 @@ const writeLockFile = async (path, lock) => {
|
|
|
2851
2871
|
await mkdir(dirname(path), { recursive: true });
|
|
2852
2872
|
await writeFile(path, serializeLockFile(lock));
|
|
2853
2873
|
};
|
|
2854
|
-
const writeDefinitionFile = async (dir, definition) => {
|
|
2855
|
-
const path = definitionPath(dir, definition);
|
|
2874
|
+
const writeDefinitionFile = async (dir, definition, site) => {
|
|
2875
|
+
const path = definitionPath(dir, definition, site);
|
|
2856
2876
|
await mkdir(dirname(path), { recursive: true });
|
|
2857
2877
|
await writeFile(path, serializeDefinition(definition));
|
|
2858
2878
|
return path;
|
|
@@ -2905,23 +2925,103 @@ const formatItem = (item, names) => {
|
|
|
2905
2925
|
].filter(Boolean);
|
|
2906
2926
|
return [`${item.kind === "component" ? "component" : "model"} ${item.apiKey}: ${describeDecision(item)}${flags.length ? ` [${flags.join(", ")}]` : ""}`, ...item.changes.map((change) => ` ${formatChange(change, names)}`)].join("\n");
|
|
2907
2927
|
};
|
|
2908
|
-
|
|
2928
|
+
const FOLDER_OF = {
|
|
2929
|
+
network: "the shared folders",
|
|
2930
|
+
site: "the site’s folder"
|
|
2931
|
+
};
|
|
2932
|
+
const PLACE_OF = {
|
|
2933
|
+
network: "shared",
|
|
2934
|
+
site: "on one site"
|
|
2935
|
+
};
|
|
2936
|
+
const SCOPE_OF = {
|
|
2937
|
+
network: "shared",
|
|
2938
|
+
site: "site"
|
|
2939
|
+
};
|
|
2940
|
+
/** One line per item of a scope refusal (`SCOPE_MISMATCH`, `FORBIDDEN_SCOPE`). */
|
|
2941
|
+
const scopeItemLines = (code, items) => items.map((item) => {
|
|
2942
|
+
const name = item.apiKey ?? item.definitionId ?? "?";
|
|
2943
|
+
return code === "SCOPE_MISMATCH" ? ` ${name}: its file is in ${FOLDER_OF[item.local ?? ""] ?? "?"}, but on the instance it is ${PLACE_OF[item.remote ?? ""] ?? "?"}` : ` ${name} (${SCOPE_OF[item.scope ?? ""] ?? item.scope ?? "?"}): ${item.message ?? "not allowed"}`;
|
|
2944
|
+
});
|
|
2945
|
+
/** What to do next, per refusal. */
|
|
2946
|
+
const HINTS = {
|
|
2947
|
+
SCHEMA_CHANGE_NOT_ACKNOWLEDGED: "Review the changes with `shapio schema diff`, then re-run with --allow-breaking and/or --allow-destructive.",
|
|
2948
|
+
SCHEMA_SYNC_CONFLICT: "Commit your files, run `shapio schema pull --force`, reconcile with git, then apply again.",
|
|
2949
|
+
SCOPE_MISMATCH: "Sync never moves a definition between sites: run `shapio schema pull` to get the instance’s layout, or change the scope with `shapio schema scope`.",
|
|
2950
|
+
FORBIDDEN_SCOPE: "Shared definitions (in models/ and components/) need schema permission on every site: apply with a network token, or leave those files unchanged."
|
|
2951
|
+
};
|
|
2952
|
+
const issueWhere = (found, filePaths) => {
|
|
2953
|
+
const match = /^\/definitions\/(\d+)(.*)$/.exec(found.path ?? "");
|
|
2954
|
+
const where = match ? `${filePaths[Number(match[1])] ?? `definition ${match[1]}`} ${match[2] || "/"}` : found.path ?? "";
|
|
2955
|
+
return found.siteKey ? `${where} (site ${found.siteKey})` : where;
|
|
2956
|
+
};
|
|
2957
|
+
const siteCountLines = (sites) => (sites ?? []).flatMap((site) => typeof site === "object" && site.entries !== void 0 ? [` site ${site.key ?? site.siteId ?? "?"}: ${site.entries} entr${site.entries === 1 ? "y" : "ies"}`] : []);
|
|
2958
|
+
/** Explains a refused request: conflicts with diffs, invalid files, scope refusals, missing acknowledgements. */
|
|
2909
2959
|
const formatApiError = (error, filePaths, names) => {
|
|
2910
2960
|
const details = error.details ?? {};
|
|
2911
2961
|
const lines = [`${error.code}: ${error.message}`];
|
|
2912
2962
|
for (const conflict of details.conflicts ?? []) lines.push(formatItem(conflict, names));
|
|
2913
|
-
for (const found of details.issues ?? []) {
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
if (error.code === "SCHEMA_CHANGE_NOT_ACKNOWLEDGED") lines.push("Review the changes with `shapio schema diff`, then re-run with --allow-breaking and/or --allow-destructive.");
|
|
2919
|
-
if (error.code === "SCHEMA_SYNC_CONFLICT") lines.push("Commit your files, run `shapio schema pull --force`, reconcile with git, then apply again.");
|
|
2963
|
+
for (const found of details.issues ?? []) lines.push(` ${issueWhere(found, filePaths)}: ${found.message ?? ""}`);
|
|
2964
|
+
lines.push(...scopeItemLines(error.code, details.items ?? []));
|
|
2965
|
+
if (error.code === "SCOPE_IN_USE") lines.push(...siteCountLines(details.sites));
|
|
2966
|
+
const hint = HINTS[error.code];
|
|
2967
|
+
if (hint) lines.push(hint);
|
|
2920
2968
|
return `${lines.join("\n")}\n`;
|
|
2921
2969
|
};
|
|
2970
|
+
/**
|
|
2971
|
+
* Which part of a schema tree one site's sync is about (plan site-schema): the shared folders and that site's
|
|
2972
|
+
* own folder. One tree may hold several sites (`sites/<key>/`); a pull or apply for one never reads, sends,
|
|
2973
|
+
* rewrites or removes another site's files or lock entries.
|
|
2974
|
+
*/
|
|
2975
|
+
const inView = (site, siteKey) => site === null || site === siteKey;
|
|
2976
|
+
/** The files an apply for `siteKey` sends: the shared ones and that site's. */
|
|
2977
|
+
const filesForSite = (files, siteKey) => files.filter((file) => inView(file.site, siteKey));
|
|
2978
|
+
/** Each sent file's scope, parallel to the files: its folder decides (only creates use it). */
|
|
2979
|
+
const scopesOf = (files) => files.map((file) => file.site === null ? "network" : "site");
|
|
2980
|
+
/**
|
|
2981
|
+
* The base an apply for `siteKey` sends: the lock in format 2 with only the shared entries and that site's,
|
|
2982
|
+
* so neither the three-way decision nor `--prune` can ever see another site's IDs. `sites` is kept: the
|
|
2983
|
+
* server refuses a tree that covers other sites only.
|
|
2984
|
+
*/
|
|
2985
|
+
const baseForSite = (lock, siteKey) => {
|
|
2986
|
+
const upgraded = upgradeLockFile(lock);
|
|
2987
|
+
return {
|
|
2988
|
+
...upgraded,
|
|
2989
|
+
definitions: lockEntriesForSite(upgraded, siteKey)
|
|
2990
|
+
};
|
|
2991
|
+
};
|
|
2992
|
+
/** Files in the site's view that differ from what was last pulled (or were never pulled). */
|
|
2993
|
+
const editedFilesForSite = (files, lock, siteKey) => {
|
|
2994
|
+
const base = lockEntriesForSite(lock, siteKey);
|
|
2995
|
+
if (Object.keys(base).length === 0) return [];
|
|
2996
|
+
return filesForSite(files, siteKey).filter((file) => {
|
|
2997
|
+
const entry = file.id ? base[file.id] : void 0;
|
|
2998
|
+
return !entry || file.hash !== entry.hash;
|
|
2999
|
+
});
|
|
3000
|
+
};
|
|
3001
|
+
/**
|
|
3002
|
+
* Files a pull of `siteKey` leaves stale: in the pulled scopes and not written (deleted or renamed on the
|
|
3003
|
+
* instance), plus any other file of a definition that was just written elsewhere (its scope changed on the
|
|
3004
|
+
* instance: it is the same definition, and two files with one ID would make the next apply fail).
|
|
3005
|
+
*/
|
|
3006
|
+
const staleFilesAfterPull = (files, siteKey, written) => {
|
|
3007
|
+
const writtenPaths = new Set(written.values());
|
|
3008
|
+
return files.filter((file) => {
|
|
3009
|
+
if (writtenPaths.has(file.path)) return false;
|
|
3010
|
+
return inView(file.site, siteKey) || file.id !== void 0 && written.has(file.id);
|
|
3011
|
+
});
|
|
3012
|
+
};
|
|
3013
|
+
/**
|
|
3014
|
+
* The lock after an apply for `siteKey` recorded its entries: the tree covers the site once it holds one of
|
|
3015
|
+
* the site's own definitions. A shared-only tree stays uncovered, so it still applies to every site.
|
|
3016
|
+
*/
|
|
3017
|
+
const coverSiteIfHeld = (lock, siteKey) => Object.values(lock.definitions).some((entry) => entry.site === siteKey) ? {
|
|
3018
|
+
...lock,
|
|
3019
|
+
sites: withSiteCovered(lock.sites, siteKey)
|
|
3020
|
+
} : lock;
|
|
2922
3021
|
const EMPTY_LOCK = {
|
|
2923
|
-
formatVersion:
|
|
3022
|
+
formatVersion: 2,
|
|
2924
3023
|
schemaVersion: 0,
|
|
3024
|
+
sites: [],
|
|
2925
3025
|
definitions: {}
|
|
2926
3026
|
};
|
|
2927
3027
|
/** Field and definition names by ID from the local files, for readable diffs. */
|
|
@@ -2942,17 +3042,45 @@ const readLocalState = async (options) => {
|
|
|
2942
3042
|
names: collectNames(files)
|
|
2943
3043
|
};
|
|
2944
3044
|
};
|
|
2945
|
-
/** The
|
|
2946
|
-
const
|
|
3045
|
+
/** The admin client for the command's site (`--site`, else the token's site, else the primary). */
|
|
3046
|
+
const clientFor = (options) => createClient({
|
|
2947
3047
|
baseUrl: options.baseUrl,
|
|
2948
|
-
token: options.token
|
|
2949
|
-
|
|
2950
|
-
|
|
3048
|
+
token: options.token,
|
|
3049
|
+
...options.site ? { site: options.site } : {}
|
|
3050
|
+
});
|
|
3051
|
+
/** The schema sync endpoints (`export`, `apply`, `change`) of @shapio/client. */
|
|
3052
|
+
const apiFor = (options) => clientFor(options).admin.schema;
|
|
3053
|
+
/** The key of the site the command works on, as the instance resolved it (an unnamed site is the default). */
|
|
3054
|
+
const resolveSiteKey = async (options) => {
|
|
3055
|
+
if (options.site) return options.site;
|
|
3056
|
+
const { site } = await apiFor(options).export({ scope: "network" });
|
|
3057
|
+
if (!site) throw new Error("The instance did not say which site this token works on; pass --site <key>");
|
|
3058
|
+
return site.key;
|
|
3059
|
+
};
|
|
3060
|
+
/**
|
|
3061
|
+
* The local tree narrowed to one site (shared files and `sites/<key>/`), or undefined (after explaining)
|
|
3062
|
+
* when the tree was pulled for other sites only.
|
|
3063
|
+
*/
|
|
3064
|
+
const readSiteState = async (options, io) => {
|
|
3065
|
+
const siteKey = await resolveSiteKey(options);
|
|
3066
|
+
const state = await readLocalState(options);
|
|
3067
|
+
if (!lockCoversSite(state.lock, siteKey)) {
|
|
3068
|
+
io.stderr(`${displayPath(options.lockPath)} covers the site(s) ${(state.lock.sites ?? []).join(", ")}, not "${siteKey}".\nApply it with --site <one of those>, or pull "${siteKey}" into this tree first: shapio schema pull --site ${siteKey}\n`);
|
|
3069
|
+
return;
|
|
3070
|
+
}
|
|
3071
|
+
return {
|
|
3072
|
+
...state,
|
|
3073
|
+
siteKey,
|
|
3074
|
+
sent: filesForSite(state.files, siteKey)
|
|
3075
|
+
};
|
|
3076
|
+
};
|
|
3077
|
+
/** Sends the site's files with their lock-file bases; the server decides per definition (three-way). */
|
|
2951
3078
|
const sendApply = async (options, state, dryRun, io) => {
|
|
2952
3079
|
try {
|
|
2953
3080
|
return await apiFor(options).apply({
|
|
2954
|
-
definitions: state.
|
|
2955
|
-
|
|
3081
|
+
definitions: state.sent.map((file) => file.raw),
|
|
3082
|
+
scopes: scopesOf(state.sent),
|
|
3083
|
+
base: baseForSite(state.lock, state.siteKey),
|
|
2956
3084
|
prune: options.prune,
|
|
2957
3085
|
dryRun,
|
|
2958
3086
|
acknowledgeBreaking: options.allowBreaking,
|
|
@@ -2960,7 +3088,7 @@ const sendApply = async (options, state, dryRun, io) => {
|
|
|
2960
3088
|
});
|
|
2961
3089
|
} catch (error) {
|
|
2962
3090
|
if (error instanceof ShapioApiError) {
|
|
2963
|
-
io.stderr(formatApiError(error, state.
|
|
3091
|
+
io.stderr(formatApiError(error, state.sent.map((file) => displayPath(file.path)), state.names));
|
|
2964
3092
|
return;
|
|
2965
3093
|
}
|
|
2966
3094
|
throw error;
|
|
@@ -3048,11 +3176,12 @@ const checkPlanDirectory = async (dir, force) => {
|
|
|
3048
3176
|
if (Object.keys(previous.state.entries).length > 0 || Object.keys(previous.state.media).length > 0) throw new Error(`${dir} holds an import that has started (${Object.keys(previous.state.entries).length} entries created): re-run --map to continue it, or plan into a new directory`);
|
|
3049
3177
|
if (!force) throw new Error(`${dir} already holds a plan: pass --force to replace it (new IDs), or use another directory`);
|
|
3050
3178
|
};
|
|
3051
|
-
const plannedMap = (source, info, ids) => ({
|
|
3179
|
+
const plannedMap = (source, info, ids, site) => ({
|
|
3052
3180
|
format: IMPORT_MAP_FORMAT,
|
|
3053
3181
|
formatVersion: 1,
|
|
3054
3182
|
source: info,
|
|
3055
3183
|
plannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3184
|
+
site: site ?? null,
|
|
3056
3185
|
definitions: ids,
|
|
3057
3186
|
media: Object.fromEntries(source.media.map((media) => [media.sourceId, {
|
|
3058
3187
|
filename: media.filename,
|
|
@@ -3070,9 +3199,11 @@ const plannedMap = (source, info, ids) => ({
|
|
|
3070
3199
|
/**
|
|
3071
3200
|
* `--plan`: writes one pull-format schema file per planned definition under `<dir>/schema`, an empty lock file
|
|
3072
3201
|
* beside them (so `schema apply` treats every definition as new and leaves the project's own lock alone), and
|
|
3073
|
-
* `import-map.json`. Nothing is sent to an instance.
|
|
3202
|
+
* `import-map.json`. Nothing is sent to an instance. With `site` (`--site` / SHAPIO_SITE) the files go under
|
|
3203
|
+
* `schema/sites/<site>/`, so `schema apply --site <site>` creates them on that site; without one they go in
|
|
3204
|
+
* the shared folders and are created shared with all sites.
|
|
3074
3205
|
*/
|
|
3075
|
-
const writePlan = async (dir, source, info, { force = false } = {}) => {
|
|
3206
|
+
const writePlan = async (dir, source, info, { force = false, site } = {}) => {
|
|
3076
3207
|
await checkPlanDirectory(dir, force);
|
|
3077
3208
|
const ids = assignIds(source.definitions);
|
|
3078
3209
|
const definitions = buildDefinitions(source, ids);
|
|
@@ -3081,9 +3212,9 @@ const writePlan = async (dir, source, info, { force = false } = {}) => {
|
|
|
3081
3212
|
recursive: true,
|
|
3082
3213
|
force: true
|
|
3083
3214
|
});
|
|
3084
|
-
for (const definition of definitions) await writeDefinitionFile(schemaDir, definition);
|
|
3215
|
+
for (const definition of definitions) await writeDefinitionFile(schemaDir, definition, site ?? null);
|
|
3085
3216
|
await writeLockFile(join(dir, PLAN_LOCK_FILE), EMPTY_LOCK);
|
|
3086
|
-
const map = plannedMap(source, info, ids);
|
|
3217
|
+
const map = plannedMap(source, info, ids, site);
|
|
3087
3218
|
await writeImportMap(dir, map);
|
|
3088
3219
|
return {
|
|
3089
3220
|
definitions,
|
|
@@ -3198,17 +3329,27 @@ var ImportRefusedError = class extends Error {
|
|
|
3198
3329
|
this.name = "ImportRefusedError";
|
|
3199
3330
|
}
|
|
3200
3331
|
};
|
|
3332
|
+
/**
|
|
3333
|
+
* The site `--map` writes to: `--site` (or SHAPIO_SITE), else the site the plan was made for. A requested site
|
|
3334
|
+
* that disagrees with the planned one is refused: the planned models exist on that site only.
|
|
3335
|
+
*/
|
|
3336
|
+
const resolveMapSite = (map, requested) => {
|
|
3337
|
+
const planned = map.site ?? void 0;
|
|
3338
|
+
if (requested !== void 0 && planned !== void 0 && requested !== planned) throw new ImportRefusedError(`This import was planned for site "${planned}", not "${requested}": run --map with --site ${planned}, or plan again with --site ${requested}`);
|
|
3339
|
+
return requested ?? planned;
|
|
3340
|
+
};
|
|
3201
3341
|
const messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
3202
|
-
const checkSourceAndSite = async (map,
|
|
3342
|
+
const checkSourceAndSite = async (map, site) => {
|
|
3203
3343
|
if (await sha256File(map.source.path).catch((error) => {
|
|
3204
3344
|
throw new ImportRefusedError(`The source ${map.source.path} cannot be read: ${messageOf(error)}`);
|
|
3205
3345
|
}) !== map.source.sha256) throw new ImportRefusedError(`${map.source.path} changed since it was planned: plan again into a new directory`);
|
|
3206
|
-
if ((Object.keys(map.state.entries).length > 0 || Object.keys(map.state.media).length > 0) && (map.state.site ?? null) !== (
|
|
3346
|
+
if ((Object.keys(map.state.entries).length > 0 || Object.keys(map.state.media).length > 0) && (map.state.site ?? null) !== (site ?? null)) throw new ImportRefusedError(`This import was started on site ${map.state.site ?? "(default)"}: re-run it with the same --site`);
|
|
3207
3347
|
};
|
|
3208
|
-
|
|
3348
|
+
/** The target site's view (the client names the site): its own definitions and the shared ones. */
|
|
3349
|
+
const loadLiveSchema = async (client, map, dir, site) => {
|
|
3209
3350
|
const exported = await client.admin.schema.export();
|
|
3210
3351
|
const live = resolveLiveSchema(map, exported.definitions.map((entry) => entry.definition));
|
|
3211
|
-
if (live.missing.length > 0) throw new ImportRefusedError(`The instance does not have the planned models yet: ${live.missing.join(", ")}.\nApply them first: shapio schema apply --dir ${join(dir, PLAN_SCHEMA_DIR)} --lock ${join(dir, PLAN_LOCK_FILE)}`);
|
|
3352
|
+
if (live.missing.length > 0) throw new ImportRefusedError(`The instance does not have the planned models yet: ${live.missing.join(", ")}.\nApply them first: shapio schema apply --dir ${join(dir, PLAN_SCHEMA_DIR)} --lock ${join(dir, PLAN_LOCK_FILE)}${site ? ` --site ${site}` : ""}`);
|
|
3212
3353
|
return live;
|
|
3213
3354
|
};
|
|
3214
3355
|
const checkLocales = async (client, source) => {
|
|
@@ -3362,17 +3503,18 @@ const printSummary = (run, source, changeSetFailures) => {
|
|
|
3362
3503
|
};
|
|
3363
3504
|
const runMap = async (options, io) => {
|
|
3364
3505
|
const map = await readImportMap(options.dir);
|
|
3365
|
-
|
|
3506
|
+
const site = resolveMapSite(map, options.site);
|
|
3507
|
+
await checkSourceAndSite(map, site);
|
|
3366
3508
|
const client = createClient({
|
|
3367
3509
|
baseUrl: options.baseUrl,
|
|
3368
3510
|
token: options.token,
|
|
3369
3511
|
fetch: createRateLimitedFetch(options.fetch),
|
|
3370
|
-
...
|
|
3512
|
+
...site ? { site } : {}
|
|
3371
3513
|
});
|
|
3372
|
-
const live = await loadLiveSchema(client, map, options.dir);
|
|
3514
|
+
const live = await loadLiveSchema(client, map, options.dir, site);
|
|
3373
3515
|
const source = await options.load(map);
|
|
3374
3516
|
await checkLocales(client, source);
|
|
3375
|
-
map.state.site =
|
|
3517
|
+
map.state.site = site ?? null;
|
|
3376
3518
|
map.state.baseUrl = options.baseUrl;
|
|
3377
3519
|
const run = {
|
|
3378
3520
|
options,
|
|
@@ -3396,7 +3538,7 @@ const display = (path) => {
|
|
|
3396
3538
|
return !shown ? "." : shown.startsWith("..") || isAbsolute(shown) ? path : shown;
|
|
3397
3539
|
};
|
|
3398
3540
|
/** What `--plan` wrote and what to do next. */
|
|
3399
|
-
const formatPlanSummary = (source, definitions, dir, command) => {
|
|
3541
|
+
const formatPlanSummary = (source, definitions, dir, command, site) => {
|
|
3400
3542
|
const keyOf = new Map(source.definitions.map((definition) => [definition.apiKey, definition.key]));
|
|
3401
3543
|
const lines = [`Planned ${definitions.length} definition(s) in ${display(`${dir}/${PLAN_SCHEMA_DIR}`)}:`];
|
|
3402
3544
|
for (const definition of definitions) {
|
|
@@ -3407,7 +3549,9 @@ const formatPlanSummary = (source, definitions, dir, command) => {
|
|
|
3407
3549
|
lines.push(`Media: ${source.media.length} file(s) to import.`);
|
|
3408
3550
|
lines.push("Every model has draft and publish on: nothing goes live until you ship the change set(s) the import opens.");
|
|
3409
3551
|
lines.push(...source.notes);
|
|
3410
|
-
lines.push(
|
|
3552
|
+
lines.push(site ? `The models and components will belong to site "${site}".` : "No --site given: the models and components will be shared with all sites. Plan again with --site <key> to keep them on one site.");
|
|
3553
|
+
const siteFlag = site ? ` --site ${site}` : "";
|
|
3554
|
+
lines.push("Next:", " 1. Review the files: rename API IDs or delete fields you do not want (entries follow the stable IDs).", ` 2. shapio schema apply --dir ${display(`${dir}/${PLAN_SCHEMA_DIR}`)} --lock ${display(`${dir}/${PLAN_LOCK_FILE}`)}${siteFlag}`, ` 3. shapio import ${command} --map ${display(dir)}${siteFlag}`);
|
|
3411
3555
|
return `${lines.join("\n")}\n`;
|
|
3412
3556
|
};
|
|
3413
3557
|
const parse = (args, spec) => {
|
|
@@ -3436,24 +3580,30 @@ const parse = (args, spec) => {
|
|
|
3436
3580
|
file: positionals[0]
|
|
3437
3581
|
};
|
|
3438
3582
|
};
|
|
3583
|
+
/** `--site`, else SHAPIO_SITE: the site the plan's schema belongs to and the map step writes to. */
|
|
3584
|
+
const siteOf = (values, io) => (values.site ?? io.env.SHAPIO_SITE)?.trim() || void 0;
|
|
3439
3585
|
const runPlan = async (spec, file, values, io) => {
|
|
3440
3586
|
const dir = resolve(String(values.plan));
|
|
3441
3587
|
const path = resolve(file);
|
|
3442
3588
|
await checkPlanDirectory(dir, values.force === true);
|
|
3443
3589
|
const source = await spec.plan(path, dir, values);
|
|
3590
|
+
const site = siteOf(values, io);
|
|
3444
3591
|
const { definitions } = await writePlan(dir, source, {
|
|
3445
3592
|
kind: source.kind,
|
|
3446
3593
|
path,
|
|
3447
3594
|
sha256: await sha256File(path)
|
|
3448
|
-
}, {
|
|
3449
|
-
|
|
3595
|
+
}, {
|
|
3596
|
+
force: values.force === true,
|
|
3597
|
+
site
|
|
3598
|
+
});
|
|
3599
|
+
io.stdout(formatPlanSummary(source, definitions, dir, spec.name, site));
|
|
3450
3600
|
return 0;
|
|
3451
3601
|
};
|
|
3452
3602
|
const runMapStep = (spec, values, io) => {
|
|
3453
3603
|
const token = values.token ?? io.env.SHAPIO_TOKEN;
|
|
3454
3604
|
if (!token) throw new UsageError$1("An admin API token is required: pass --token or set SHAPIO_TOKEN");
|
|
3455
3605
|
const dir = resolve(String(values.map));
|
|
3456
|
-
const site = values
|
|
3606
|
+
const site = siteOf(values, io);
|
|
3457
3607
|
return runMap({
|
|
3458
3608
|
dir,
|
|
3459
3609
|
baseUrl: values.url ?? io.env.SHAPIO_URL ?? "http://localhost:4300",
|
|
@@ -3492,7 +3642,7 @@ const importStrapiCommand = createImporterCommand({
|
|
|
3492
3642
|
name: "strapi",
|
|
3493
3643
|
label: "Strapi",
|
|
3494
3644
|
summary: "Import a Strapi 5 export: --plan writes schema files, --map creates drafts and change sets",
|
|
3495
|
-
usage: "shapio import strapi <export.tar.gz[.enc]> --plan <dir> [--key <encryption key>] [--force]\n shapio import strapi --map <dir> [--url <origin>] [--token <admin token>] [--site <key>]\n --plan unpacks the export into <dir>/source (--key for an encrypted export) and writes the planned models\n and components (<dir>/schema) and <dir>/import-map.json; it sends nothing. Apply the schema with\n `shapio schema apply --dir <dir>/schema --lock <dir>/schema-lock.json`, then run --map
|
|
3645
|
+
usage: "shapio import strapi <export.tar.gz[.enc]> --plan <dir> [--key <encryption key>] [--site <key>] [--force]\n shapio import strapi --map <dir> [--url <origin>] [--token <admin token>] [--site <key>]\n --plan unpacks the export into <dir>/source (--key for an encrypted export) and writes the planned models\n and components (<dir>/schema) and <dir>/import-map.json; it sends nothing. With --site (or SHAPIO_SITE)\n they belong to that site (<dir>/schema/sites/<key>/); without, they are shared (--map\n without --site imports into the planned site). Apply the schema with\n `shapio schema apply --dir <dir>/schema --lock <dir>/schema-lock.json [--site <key>]`, then run --map:\n it uploads the media, creates every document as a draft and opens change sets with the published ones.\n Strapi 5 only.",
|
|
3496
3646
|
options: ["key"],
|
|
3497
3647
|
plan: async (file, dir, values) => {
|
|
3498
3648
|
await extractStrapiExport(file, sourceDir(dir), typeof values.key === "string" ? values.key : void 0);
|
|
@@ -4086,7 +4236,7 @@ const importWordPressCommand = createImporterCommand({
|
|
|
4086
4236
|
name: "wordpress",
|
|
4087
4237
|
label: "WordPress",
|
|
4088
4238
|
summary: "Import a WordPress export (WXR): --plan writes schema files, --map creates drafts and change sets",
|
|
4089
|
-
usage: "shapio import wordpress <export.xml> --plan <dir> [--force]\n shapio import wordpress --map <dir> [--url <origin>] [--token <admin token>] [--site <key>] [--media-dir <uploads dir>]\n --plan reads the export and writes the planned models (<dir>/schema) and <dir>/import-map.json; it sends nothing.\n Apply the models with `shapio schema apply --dir <dir>/schema --lock <dir>/schema-lock.json`, then run --map:\n it uploads the media (downloaded from the site, or read from --media-dir, a copy of wp-content/uploads),\n creates every post and page as a draft, and opens change sets with the published ones. Re-run --map to resume.",
|
|
4239
|
+
usage: "shapio import wordpress <export.xml> --plan <dir> [--site <key>] [--force]\n shapio import wordpress --map <dir> [--url <origin>] [--token <admin token>] [--site <key>] [--media-dir <uploads dir>]\n --plan reads the export and writes the planned models (<dir>/schema) and <dir>/import-map.json; it sends nothing.\n With --site (or SHAPIO_SITE) the models belong to that site (<dir>/schema/sites/<key>/); without, they are shared;\n --map without --site then imports into the planned site.\n Apply the models with `shapio schema apply --dir <dir>/schema --lock <dir>/schema-lock.json [--site <key>]`, then run --map:\n it uploads the media (downloaded from the site, or read from --media-dir, a copy of wp-content/uploads),\n creates every post and page as a draft, and opens change sets with the published ones. Re-run --map to resume.",
|
|
4090
4240
|
options: ["media-dir"],
|
|
4091
4241
|
plan: async (file) => wordpressSource(await readWxr(file)),
|
|
4092
4242
|
load: async (map, _dir, values) => wordpressSource(await readWxr(map.source.path), mediaDirOf(values))
|
|
@@ -4268,13 +4418,28 @@ var UsageError = class extends Error {
|
|
|
4268
4418
|
this.name = "UsageError";
|
|
4269
4419
|
}
|
|
4270
4420
|
};
|
|
4271
|
-
|
|
4421
|
+
const CONNECTION_FLAGS = {
|
|
4422
|
+
url: { type: "string" },
|
|
4423
|
+
token: { type: "string" },
|
|
4424
|
+
site: { type: "string" }
|
|
4425
|
+
};
|
|
4426
|
+
/** URL, token and site from the flags, else SHAPIO_URL / SHAPIO_TOKEN / SHAPIO_SITE. */
|
|
4427
|
+
const resolveConnection = (values, io) => {
|
|
4428
|
+
const token = values.token ?? io.env.SHAPIO_TOKEN;
|
|
4429
|
+
if (!token) throw new UsageError("An admin API token is required: pass --token or set SHAPIO_TOKEN");
|
|
4430
|
+
const site = (values.site ?? io.env.SHAPIO_SITE)?.trim() || void 0;
|
|
4431
|
+
return {
|
|
4432
|
+
baseUrl: values.url ?? io.env.SHAPIO_URL ?? "http://localhost:4300",
|
|
4433
|
+
token,
|
|
4434
|
+
...site ? { site } : {}
|
|
4435
|
+
};
|
|
4436
|
+
};
|
|
4437
|
+
/** Shared flags of `shapio schema pull|diff|apply`. */
|
|
4272
4438
|
const parseSchemaOptions = (args, io) => {
|
|
4273
4439
|
const { values } = parseArgs({
|
|
4274
4440
|
args: [...args],
|
|
4275
4441
|
options: {
|
|
4276
|
-
|
|
4277
|
-
token: { type: "string" },
|
|
4442
|
+
...CONNECTION_FLAGS,
|
|
4278
4443
|
dir: { type: "string" },
|
|
4279
4444
|
lock: { type: "string" },
|
|
4280
4445
|
prune: {
|
|
@@ -4301,13 +4466,11 @@ const parseSchemaOptions = (args, io) => {
|
|
|
4301
4466
|
},
|
|
4302
4467
|
allowPositionals: false
|
|
4303
4468
|
});
|
|
4304
|
-
const
|
|
4305
|
-
if (!token) throw new UsageError("An admin API token is required: pass --token or set SHAPIO_TOKEN");
|
|
4469
|
+
const connection = resolveConnection(values, io);
|
|
4306
4470
|
const waitTimeoutSeconds = Number(values["wait-timeout"] ?? 600);
|
|
4307
4471
|
if (!Number.isFinite(waitTimeoutSeconds) || waitTimeoutSeconds <= 0) throw new UsageError("--wait-timeout must be a positive number of seconds");
|
|
4308
4472
|
return {
|
|
4309
|
-
|
|
4310
|
-
token,
|
|
4473
|
+
...connection,
|
|
4311
4474
|
dir: resolve(values.dir ?? "schema"),
|
|
4312
4475
|
lockPath: resolve(values.lock ?? ".shapio/schema-lock.json"),
|
|
4313
4476
|
prune: values.prune,
|
|
@@ -4318,7 +4481,7 @@ const parseSchemaOptions = (args, io) => {
|
|
|
4318
4481
|
waitTimeoutMs: waitTimeoutSeconds * 1e3
|
|
4319
4482
|
};
|
|
4320
4483
|
};
|
|
4321
|
-
const COMMON_USAGE =
|
|
4484
|
+
const COMMON_USAGE = `[--url <origin>] [--token <admin token>] [--site <key>] [--dir schema] [--lock .shapio/schema-lock.json]`;
|
|
4322
4485
|
const POLL_INTERVAL_MS = 500;
|
|
4323
4486
|
/** Waits for planned changes (prerequisite jobs) to activate or fail. Returns the failed ones. */
|
|
4324
4487
|
const waitForChanges = async (options, items, io) => {
|
|
@@ -4350,16 +4513,18 @@ const waitForChanges = async (options, items, io) => {
|
|
|
4350
4513
|
};
|
|
4351
4514
|
/**
|
|
4352
4515
|
* Records the new state in the lock file and rewrites the files of applied definitions in canonical form
|
|
4353
|
-
* (e.g. with server-assigned field IDs). Definitions skipped because they were
|
|
4354
|
-
* their old base, so a later apply still treats them as untouched.
|
|
4516
|
+
* (e.g. with server-assigned field IDs), in their scope's folder. Definitions skipped because they were
|
|
4517
|
+
* unchanged locally keep their old base, so a later apply still treats them as untouched. Other sites'
|
|
4518
|
+
* entries are kept as they are.
|
|
4355
4519
|
*/
|
|
4356
4520
|
const updateLocalState = async (options, state, response) => {
|
|
4357
4521
|
const exported = await apiFor(options).export();
|
|
4358
4522
|
const remote = new Map(exported.definitions.map((entry) => [entry.definition.id, entry]));
|
|
4523
|
+
const upgraded = upgradeLockFile(state.lock);
|
|
4359
4524
|
const lock = {
|
|
4360
|
-
...
|
|
4525
|
+
...upgraded,
|
|
4361
4526
|
schemaVersion: exported.schemaVersion,
|
|
4362
|
-
definitions: { ...
|
|
4527
|
+
definitions: { ...upgraded.definitions }
|
|
4363
4528
|
};
|
|
4364
4529
|
for (const item of response.results) {
|
|
4365
4530
|
if (!(item.outcome === "activated" || item.decision.action === "skip" && item.decision.reason === "alreadyApplied")) continue;
|
|
@@ -4368,97 +4533,152 @@ const updateLocalState = async (options, state, response) => {
|
|
|
4368
4533
|
delete lock.definitions[item.definitionId];
|
|
4369
4534
|
continue;
|
|
4370
4535
|
}
|
|
4371
|
-
const previous = state.
|
|
4372
|
-
const path = await writeDefinitionFile(options.dir, entry.definition);
|
|
4536
|
+
const previous = state.sent.find((file) => file.id === item.definitionId);
|
|
4537
|
+
const path = await writeDefinitionFile(options.dir, entry.definition, entry.site);
|
|
4373
4538
|
if (previous && previous.path !== path) await removeFile(previous.path);
|
|
4374
4539
|
lock.definitions[item.definitionId] = {
|
|
4375
4540
|
kind: entry.definition.kind,
|
|
4376
4541
|
apiKey: entry.definition.apiKey,
|
|
4377
4542
|
version: entry.version,
|
|
4378
|
-
hash: entry.hash
|
|
4543
|
+
hash: entry.hash,
|
|
4544
|
+
site: entry.site
|
|
4379
4545
|
};
|
|
4380
4546
|
}
|
|
4381
|
-
await writeLockFile(options.lockPath, lock);
|
|
4547
|
+
await writeLockFile(options.lockPath, coverSiteIfHeld(lock, state.siteKey));
|
|
4548
|
+
};
|
|
4549
|
+
/**
|
|
4550
|
+
* `shapio schema apply`: applies local schema files to a running instance live, through the same planner as
|
|
4551
|
+
* the admin UI. Three-way per definition against the lock file; refuses (and changes nothing) on conflicts.
|
|
4552
|
+
* It sends the shared files and the site's own (`sites/<key>/`), never another site's.
|
|
4553
|
+
*/
|
|
4554
|
+
const schemaApplyCommand = {
|
|
4555
|
+
summary: "Apply local schema files to the instance live (three-way, per model; refuses on conflicts)",
|
|
4556
|
+
usage: `shapio schema apply ${COMMON_USAGE} [--prune] [--allow-breaking] [--allow-destructive] [--no-wait] [--wait-timeout <s>]`,
|
|
4557
|
+
run: async (args, io) => {
|
|
4558
|
+
const options = parseSchemaOptions(args, io);
|
|
4559
|
+
const state = await readSiteState(options, io);
|
|
4560
|
+
if (!state) return 1;
|
|
4561
|
+
const response = await sendApply(options, state, false, io);
|
|
4562
|
+
if (!response) return 1;
|
|
4563
|
+
printResults(response, state, io, false);
|
|
4564
|
+
const pending = response.results.filter((item) => item.outcome === "pending");
|
|
4565
|
+
const failed = options.wait ? await waitForChanges(options, pending, io) : [];
|
|
4566
|
+
await updateLocalState(options, state, response);
|
|
4567
|
+
const applied = response.results.filter((item) => item.outcome === "activated").length;
|
|
4568
|
+
io.stdout(`Applied ${applied} definition(s) at schema version ${response.schemaVersion}.\n`);
|
|
4569
|
+
return failed.length > 0 ? 1 : 0;
|
|
4570
|
+
}
|
|
4571
|
+
};
|
|
4572
|
+
/** `shapio schema diff`: what `apply` would do, per definition, without changing anything. */
|
|
4573
|
+
const schemaDiffCommand = {
|
|
4574
|
+
summary: "Show what `shapio schema apply` would change on the instance (dry run)",
|
|
4575
|
+
usage: `shapio schema diff ${COMMON_USAGE} [--prune]`,
|
|
4576
|
+
run: async (args, io) => {
|
|
4577
|
+
const options = parseSchemaOptions(args, io);
|
|
4578
|
+
const state = await readSiteState(options, io);
|
|
4579
|
+
if (!state) return 1;
|
|
4580
|
+
const response = await sendApply(options, state, true, io);
|
|
4581
|
+
if (!response) return 1;
|
|
4582
|
+
printResults(response, state, io, false);
|
|
4583
|
+
const pending = response.results.filter((item) => [
|
|
4584
|
+
"create",
|
|
4585
|
+
"update",
|
|
4586
|
+
"delete"
|
|
4587
|
+
].includes(item.decision.action));
|
|
4588
|
+
io.stdout(pending.length === 0 ? "Nothing to apply.\n" : `${pending.length} definition(s) would change.\n`);
|
|
4589
|
+
return 0;
|
|
4590
|
+
}
|
|
4591
|
+
};
|
|
4592
|
+
/**
|
|
4593
|
+
* `shapio schema pull`: writes one site's view of the instance (the shared definitions in `models/` and
|
|
4594
|
+
* `components/`, the site's own under `sites/<key>/`) as canonical JSON, one file per model or component,
|
|
4595
|
+
* and records what was pulled in the lock file. Another site's folder and lock entries are left alone, so one
|
|
4596
|
+
* tree can hold several sites. Refuses to overwrite local edits in the pulled folders unless --force.
|
|
4597
|
+
*/
|
|
4598
|
+
const schemaPullCommand = {
|
|
4599
|
+
summary: "Write a site’s models and components (shared and its own) to schema files and the lock file",
|
|
4600
|
+
usage: `shapio schema pull ${COMMON_USAGE} [--force]`,
|
|
4601
|
+
run: async (args, io) => {
|
|
4602
|
+
const options = parseSchemaOptions(args, io);
|
|
4603
|
+
const exported = await apiFor(options).export();
|
|
4604
|
+
if (!exported.site) throw new Error("The instance did not say which site this view is; pass --site <key>");
|
|
4605
|
+
const siteKey = exported.site.key;
|
|
4606
|
+
const state = await readLocalState(options);
|
|
4607
|
+
const edited = editedFilesForSite(state.files, state.lock, siteKey);
|
|
4608
|
+
if (edited.length > 0 && !options.force) {
|
|
4609
|
+
io.stderr(`Local schema files have changes that are not on the instance:\n${edited.map((file) => ` ${displayPath(file.path)}`).join("\n")}\nApply them first, or commit them and re-run with --force to overwrite them with the instance’s schema.
|
|
4610
|
+
`);
|
|
4611
|
+
return 1;
|
|
4612
|
+
}
|
|
4613
|
+
const written = /* @__PURE__ */ new Map();
|
|
4614
|
+
for (const { definition, site } of exported.definitions) written.set(definition.id, await writeDefinitionFile(options.dir, definition, site));
|
|
4615
|
+
for (const file of staleFilesAfterPull(state.files, siteKey, written)) await removeFile(file.path);
|
|
4616
|
+
await writeLockFile(options.lockPath, mergePulledLock(state.lock, {
|
|
4617
|
+
schemaVersion: exported.schemaVersion,
|
|
4618
|
+
siteKey,
|
|
4619
|
+
definitions: exported.definitions
|
|
4620
|
+
}));
|
|
4621
|
+
io.stdout(`Pulled ${exported.definitions.length} definition(s) of site "${siteKey}" at schema version ${exported.schemaVersion} into ${displayPath(options.dir)}\n`);
|
|
4622
|
+
return 0;
|
|
4623
|
+
}
|
|
4624
|
+
};
|
|
4625
|
+
const parseScopeOptions = (args, io) => {
|
|
4626
|
+
const { values, positionals } = parseArgs({
|
|
4627
|
+
args: [...args],
|
|
4628
|
+
options: {
|
|
4629
|
+
...CONNECTION_FLAGS,
|
|
4630
|
+
shared: {
|
|
4631
|
+
type: "boolean",
|
|
4632
|
+
default: false
|
|
4633
|
+
},
|
|
4634
|
+
version: { type: "string" }
|
|
4635
|
+
},
|
|
4636
|
+
allowPositionals: true
|
|
4637
|
+
});
|
|
4638
|
+
const [apiKey, ...extra] = positionals;
|
|
4639
|
+
if (!apiKey || extra.length > 0) throw new UsageError("Name one model or component by its API ID");
|
|
4640
|
+
if (!values.shared && !values.site) throw new UsageError("Pass --site <key> to keep it on that site, or --shared to share it with all sites");
|
|
4641
|
+
const version = values.version === void 0 ? void 0 : Number(values.version);
|
|
4642
|
+
if (version !== void 0 && (!Number.isInteger(version) || version < 1)) throw new UsageError("--version must be a positive integer");
|
|
4643
|
+
return {
|
|
4644
|
+
...resolveConnection(values, io),
|
|
4645
|
+
apiKey,
|
|
4646
|
+
shared: values.shared,
|
|
4647
|
+
version
|
|
4648
|
+
};
|
|
4382
4649
|
};
|
|
4383
4650
|
const SUBCOMMANDS$1 = {
|
|
4384
|
-
pull:
|
|
4385
|
-
|
|
4386
|
-
|
|
4651
|
+
pull: schemaPullCommand,
|
|
4652
|
+
diff: schemaDiffCommand,
|
|
4653
|
+
apply: schemaApplyCommand,
|
|
4654
|
+
scope: {
|
|
4655
|
+
summary: "Keep a shared model or component on one site (--site), or share a site’s with all sites (--shared)",
|
|
4656
|
+
usage: "shapio schema scope <apiKey> (--site <key> | --shared [--site <key>]) [--url <origin>] [--token <admin token>] [--version <n>]",
|
|
4387
4657
|
run: async (args, io) => {
|
|
4388
|
-
const options =
|
|
4389
|
-
const
|
|
4390
|
-
const
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
io.stderr(`Local schema files have changes that are not on the instance:\n${edited.map((file) => ` ${displayPath(file.path)}`).join("\n")}\nApply them first, or commit them and re-run with --force to overwrite them with the instance’s schema.
|
|
4396
|
-
`);
|
|
4658
|
+
const options = parseScopeOptions(args, io);
|
|
4659
|
+
const client = clientFor(options);
|
|
4660
|
+
const exported = await client.admin.schema.export();
|
|
4661
|
+
const siteKey = options.site ?? exported.site?.key ?? "(default)";
|
|
4662
|
+
const found = exported.definitions.find(({ definition }) => definition.apiKey === options.apiKey);
|
|
4663
|
+
if (!found) {
|
|
4664
|
+
io.stderr(`Site "${siteKey}" has no model or component with API ID "${options.apiKey}".\n`);
|
|
4397
4665
|
return 1;
|
|
4398
4666
|
}
|
|
4399
|
-
const
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
};
|
|
4405
|
-
const written = /* @__PURE__ */ new Set();
|
|
4406
|
-
for (const { definition, version, hash } of exported.definitions) {
|
|
4407
|
-
written.add(await writeDefinitionFile(options.dir, definition));
|
|
4408
|
-
lock.definitions[definition.id] = {
|
|
4409
|
-
kind: definition.kind,
|
|
4410
|
-
apiKey: definition.apiKey,
|
|
4411
|
-
version,
|
|
4412
|
-
hash
|
|
4413
|
-
};
|
|
4414
|
-
}
|
|
4415
|
-
for (const file of state.files.filter((candidate) => !written.has(candidate.path))) await removeFile(file.path);
|
|
4416
|
-
await writeLockFile(options.lockPath, lock);
|
|
4417
|
-
io.stdout(`Pulled ${exported.definitions.length} definition(s) at schema version ${exported.schemaVersion} into ${displayPath(options.dir)}\n`);
|
|
4418
|
-
return 0;
|
|
4419
|
-
}
|
|
4420
|
-
},
|
|
4421
|
-
diff: {
|
|
4422
|
-
summary: "Show what `shapio schema apply` would change on the instance (dry run)",
|
|
4423
|
-
usage: `shapio schema diff ${COMMON_USAGE} [--prune]`,
|
|
4424
|
-
run: async (args, io) => {
|
|
4425
|
-
const options = parseSchemaOptions(args, io);
|
|
4426
|
-
const state = await readLocalState(options);
|
|
4427
|
-
const response = await sendApply(options, state, true, io);
|
|
4428
|
-
if (!response) return 1;
|
|
4429
|
-
printResults(response, state, io, false);
|
|
4430
|
-
const pending = response.results.filter((item) => [
|
|
4431
|
-
"create",
|
|
4432
|
-
"update",
|
|
4433
|
-
"delete"
|
|
4434
|
-
].includes(item.decision.action));
|
|
4435
|
-
io.stdout(pending.length === 0 ? "Nothing to apply.\n" : `${pending.length} definition(s) would change.\n`);
|
|
4667
|
+
const outcome = await (found.definition.kind === "component" ? client.admin.components : client.admin.models).changeScope(found.definition.id, {
|
|
4668
|
+
scope: options.shared ? "network" : "site",
|
|
4669
|
+
version: options.version ?? found.version
|
|
4670
|
+
});
|
|
4671
|
+
const where = outcome.scope === "network" ? "shared with all sites" : `on site "${siteKey}" only`;
|
|
4672
|
+
io.stdout(outcome.status === "unchanged" ? `${options.apiKey} is already ${where}.\n` : `${options.apiKey} is now ${where} (version ${outcome.version}, schema version ${outcome.schemaVersion}).\nPull to move its file: shapio schema pull --site ${siteKey}\n`);
|
|
4436
4673
|
return 0;
|
|
4437
4674
|
}
|
|
4438
|
-
},
|
|
4439
|
-
apply: {
|
|
4440
|
-
summary: "Apply local schema files to the instance live (three-way, per model; refuses on conflicts)",
|
|
4441
|
-
usage: `shapio schema apply ${COMMON_USAGE} [--prune] [--allow-breaking] [--allow-destructive] [--no-wait] [--wait-timeout <s>]`,
|
|
4442
|
-
run: async (args, io) => {
|
|
4443
|
-
const options = parseSchemaOptions(args, io);
|
|
4444
|
-
const state = await readLocalState(options);
|
|
4445
|
-
const response = await sendApply(options, state, false, io);
|
|
4446
|
-
if (!response) return 1;
|
|
4447
|
-
printResults(response, state, io, false);
|
|
4448
|
-
const pending = response.results.filter((item) => item.outcome === "pending");
|
|
4449
|
-
const failed = options.wait ? await waitForChanges(options, pending, io) : [];
|
|
4450
|
-
await updateLocalState(options, state, response);
|
|
4451
|
-
const applied = response.results.filter((item) => item.outcome === "activated").length;
|
|
4452
|
-
io.stdout(`Applied ${applied} definition(s) at schema version ${response.schemaVersion}.\n`);
|
|
4453
|
-
return failed.length > 0 ? 1 : 0;
|
|
4454
|
-
}
|
|
4455
4675
|
}
|
|
4456
4676
|
};
|
|
4457
4677
|
const help$1 = () => `Usage:\n${Object.values(SUBCOMMANDS$1).map((command) => ` ${command.usage}\n ${command.summary}`).join("\n")}\n`;
|
|
4458
|
-
/** `shapio schema <pull|diff|apply>`: git-like schema sync over HTTP with an admin API token. */
|
|
4678
|
+
/** `shapio schema <pull|diff|apply|scope>`: git-like schema sync over HTTP with an admin API token. */
|
|
4459
4679
|
const schemaCommand = {
|
|
4460
|
-
summary: "Sync models and components with schema files: pull | diff | apply",
|
|
4461
|
-
usage: "shapio schema <pull|diff|apply> [options]",
|
|
4680
|
+
summary: "Sync models and components with schema files: pull | diff | apply | scope",
|
|
4681
|
+
usage: "shapio schema <pull|diff|apply|scope> [options]",
|
|
4462
4682
|
run: async (args, io) => {
|
|
4463
4683
|
const [name, ...rest] = args;
|
|
4464
4684
|
const command = name ? SUBCOMMANDS$1[name] : void 0;
|
|
@@ -4473,6 +4693,10 @@ const schemaCommand = {
|
|
|
4473
4693
|
io.stderr(`shapio schema ${name}: ${error.message}\n`);
|
|
4474
4694
|
return 1;
|
|
4475
4695
|
}
|
|
4696
|
+
if (error instanceof ShapioApiError) {
|
|
4697
|
+
io.stderr(formatApiError(error, [], /* @__PURE__ */ new Map()));
|
|
4698
|
+
return 1;
|
|
4699
|
+
}
|
|
4476
4700
|
throw error;
|
|
4477
4701
|
}
|
|
4478
4702
|
}
|
|
@@ -4594,6 +4818,22 @@ const backupDatabase = async (db, target) => {
|
|
|
4594
4818
|
return path;
|
|
4595
4819
|
};
|
|
4596
4820
|
//#endregion
|
|
4821
|
+
//#region src/extensions/themeContrast.ts
|
|
4822
|
+
const HEX = /^#[0-9a-fA-F]{6}$/;
|
|
4823
|
+
/**
|
|
4824
|
+
* WCAG AA warnings for extension themes (`shapio extensions check`): the pairs the built-in themes are held
|
|
4825
|
+
* to. Warnings, not errors: a project may knowingly ship a theme that fails. Only `#rrggbb` values can be
|
|
4826
|
+
* measured; a variant with other values is reported as not fully checked.
|
|
4827
|
+
*/
|
|
4828
|
+
const themeContrastWarnings = (themes) => themes.flatMap((theme) => THEME_VARIANTS.flatMap((variant) => {
|
|
4829
|
+
const tokens = theme[variant];
|
|
4830
|
+
if (!tokens) return [];
|
|
4831
|
+
const values = Object.fromEntries(Object.entries(tokens).filter((entry) => typeof entry[1] === "string"));
|
|
4832
|
+
const failures = checkThemeContrast(values, variant).filter(({ ratio, required }) => ratio !== void 0 && ratio < required).map(({ foreground, background, ratio, required }) => `${theme.key} (${variant}): --${foreground} on --${background} is ${(ratio ?? 0).toFixed(2)}:1, needs ${required}:1`);
|
|
4833
|
+
const unmeasured = Object.values(values).some((value) => !HEX.test(value)) ? [`${theme.key} (${variant}): only #rrggbb values are checked for contrast`] : [];
|
|
4834
|
+
return [...failures, ...unmeasured];
|
|
4835
|
+
}));
|
|
4836
|
+
//#endregion
|
|
4597
4837
|
//#region src/extensions/check.ts
|
|
4598
4838
|
const AFTER = new Set(AFTER_EVENTS);
|
|
4599
4839
|
/** Collects the editor manifest's complaints (missing files, bad names) instead of printing log lines. */
|
|
@@ -4619,6 +4859,8 @@ const checkExtensions = async (options) => {
|
|
|
4619
4859
|
const editorProblems = [];
|
|
4620
4860
|
const editors = buildEditorManifest(projectDir, config.editors ?? [], collectingLogger(editorProblems));
|
|
4621
4861
|
const hooks = Object.entries(config.hooks ?? {}).flatMap(([key, modelHooks]) => [...BEFORE_EVENTS, ...AFTER_EVENTS].filter((event) => modelHooks[event]).map((event) => `${key}.${event} (${AFTER.has(event) ? "after commit, as a job" : "in the transaction"})`));
|
|
4862
|
+
const themes = config.themes ?? [];
|
|
4863
|
+
const contrastWarnings = themeContrastWarnings(themes);
|
|
4622
4864
|
const section = (title, items) => `${title} (${items.length})\n${items.map((item) => ` ${item}\n`).join("")}`;
|
|
4623
4865
|
const text = [
|
|
4624
4866
|
`Config: ${relative(process.cwd(), loaded.file) || loaded.file}\n`,
|
|
@@ -4627,7 +4869,9 @@ const checkExtensions = async (options) => {
|
|
|
4627
4869
|
section("Services", Object.keys(config.services ?? {})),
|
|
4628
4870
|
section("Jobs", Object.keys(config.jobs ?? {}).map((name) => `${EXTENSION_JOB_PREFIX}${name}`)),
|
|
4629
4871
|
section("Editors", editors.entries.map((entry) => entry.file)),
|
|
4630
|
-
|
|
4872
|
+
section("Themes", themes.map((theme) => `${theme.key} (${theme.name})`)),
|
|
4873
|
+
...editorProblems.length > 0 ? [section("Problems", editorProblems)] : [],
|
|
4874
|
+
...contrastWarnings.length > 0 ? [section("Warnings", contrastWarnings)] : []
|
|
4631
4875
|
].join("");
|
|
4632
4876
|
return {
|
|
4633
4877
|
ok: editorProblems.length === 0,
|
|
@@ -5050,7 +5294,7 @@ const adminCommand = {
|
|
|
5050
5294
|
}
|
|
5051
5295
|
}
|
|
5052
5296
|
};
|
|
5053
|
-
const SITES_USAGE = "shapio sites list\nshapio sites create --key <key> --name <name>\n Lists or creates sites directly in the database (sites share the
|
|
5297
|
+
const SITES_USAGE = "shapio sites list\nshapio sites create --key <key> --name <name>\n Lists or creates sites directly in the database (sites share the admins, roles and shared content types;\n each has its own content types, content, media, tokens and snapshots). Keys are lower case and fixed once\n created.";
|
|
5054
5298
|
const SITE_KEY_RE = new RegExp(SITE_KEY_PATTERN);
|
|
5055
5299
|
const runSites = async (subcommand, options, io) => {
|
|
5056
5300
|
const config = loadConfig();
|