@shapio/cms 0.2.1 → 0.3.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.
Files changed (302) hide show
  1. package/dist/admin/assets/{AssistError-hVUWpP3F.js → AssistError-BITJ_tpg.js} +2 -2
  2. package/dist/admin/assets/{AssistError-hVUWpP3F.js.map → AssistError-BITJ_tpg.js.map} +1 -1
  3. package/dist/admin/assets/{BlockMenuItems-B0g93m6M.js → BlockMenuItems-CcluK965.js} +2 -2
  4. package/dist/admin/assets/{BlockMenuItems-B0g93m6M.js.map → BlockMenuItems-CcluK965.js.map} +1 -1
  5. package/dist/admin/assets/Body-BXa9DhtH.js +2 -0
  6. package/dist/admin/assets/{Body-CFQKONK4.js.map → Body-BXa9DhtH.js.map} +1 -1
  7. package/dist/admin/assets/Builder-DcpRfXuc.js +2 -0
  8. package/dist/admin/assets/Builder-DcpRfXuc.js.map +1 -0
  9. package/dist/admin/assets/{ConfirmDialog-D6rs75TP.js → ConfirmDialog-B9nWhIIz.js} +2 -2
  10. package/dist/admin/assets/{ConfirmDialog-D6rs75TP.js.map → ConfirmDialog-B9nWhIIz.js.map} +1 -1
  11. package/dist/admin/assets/{CopyButton-Bc7crFDE.js → CopyButton-BoWb-xi1.js} +2 -2
  12. package/dist/admin/assets/{CopyButton-Bc7crFDE.js.map → CopyButton-BoWb-xi1.js.map} +1 -1
  13. package/dist/admin/assets/{CursorPager-BFPD_adV.js → CursorPager-BnxmTk47.js} +2 -2
  14. package/dist/admin/assets/{CursorPager-BFPD_adV.js.map → CursorPager-BnxmTk47.js.map} +1 -1
  15. package/dist/admin/assets/{ExternalLink-Cdfcyyed.js → ExternalLink-Eeb9mONJ.js} +2 -2
  16. package/dist/admin/assets/{ExternalLink-Cdfcyyed.js.map → ExternalLink-Eeb9mONJ.js.map} +1 -1
  17. package/dist/admin/assets/Form-DZRLD3vH.js +2 -0
  18. package/dist/admin/assets/Form-DZRLD3vH.js.map +1 -0
  19. package/dist/admin/assets/{FormCheckboxGroup-Bicl1CBP.js → FormCheckboxGroup-uIC2XQIL.js} +2 -2
  20. package/dist/admin/assets/{FormCheckboxGroup-Bicl1CBP.js.map → FormCheckboxGroup-uIC2XQIL.js.map} +1 -1
  21. package/dist/admin/assets/{FormRadioGroup-34iVSV4L.js → FormRadioGroup-hd95vPOu.js} +2 -2
  22. package/dist/admin/assets/{FormRadioGroup-34iVSV4L.js.map → FormRadioGroup-hd95vPOu.js.map} +1 -1
  23. package/dist/admin/assets/{FormSelectField-Ba4fIu-8.js → FormSelectField-D7VHmnx5.js} +2 -2
  24. package/dist/admin/assets/{FormSelectField-Ba4fIu-8.js.map → FormSelectField-D7VHmnx5.js.map} +1 -1
  25. package/dist/admin/assets/{FormSwitchField-DN6bfPC0.js → FormSwitchField-ft8Jbe6E.js} +2 -2
  26. package/dist/admin/assets/{FormSwitchField-DN6bfPC0.js.map → FormSwitchField-ft8Jbe6E.js.map} +1 -1
  27. package/dist/admin/assets/{FormTextareaField-WsNjLm32.js → FormTextareaField-DzyOzfZb.js} +2 -2
  28. package/dist/admin/assets/{FormTextareaField-WsNjLm32.js.map → FormTextareaField-DzyOzfZb.js.map} +1 -1
  29. package/dist/admin/assets/{HintedLabel-jKLM0ETt.js → HintedLabel-CvC46uy_.js} +2 -2
  30. package/dist/admin/assets/{HintedLabel-jKLM0ETt.js.map → HintedLabel-CvC46uy_.js.map} +1 -1
  31. package/dist/admin/assets/{InfoHint-DBqcrmZ3.js → InfoHint-GJLRVumb.js} +2 -2
  32. package/dist/admin/assets/{InfoHint-DBqcrmZ3.js.map → InfoHint-GJLRVumb.js.map} +1 -1
  33. package/dist/admin/assets/{InsertMenuItem-CRKXblK5.js → InsertMenuItem-D976G0Rj.js} +2 -2
  34. package/dist/admin/assets/{InsertMenuItem-CRKXblK5.js.map → InsertMenuItem-D976G0Rj.js.map} +1 -1
  35. package/dist/admin/assets/LinkTabs-BZSlA57U.js +2 -0
  36. package/dist/admin/assets/{LinkTabs-Bn2JVqOL.js.map → LinkTabs-BZSlA57U.js.map} +1 -1
  37. package/dist/admin/assets/Match-DdxTHHM4.js +2 -0
  38. package/dist/admin/assets/{Match-DaYgKrk7.js.map → Match-DdxTHHM4.js.map} +1 -1
  39. package/dist/admin/assets/{Panel-6wFe96Gr.js → Panel-iIKjEURi.js} +2 -2
  40. package/dist/admin/assets/{Panel-6wFe96Gr.js.map → Panel-iIKjEURi.js.map} +1 -1
  41. package/dist/admin/assets/{QueryView-DFZtm0fn.js → QueryView-D6aZQJRN.js} +2 -2
  42. package/dist/admin/assets/{QueryView-DFZtm0fn.js.map → QueryView-D6aZQJRN.js.map} +1 -1
  43. package/dist/admin/assets/{RadioTile-DsTyz7ml.js → RadioTile-BpB79rIK.js} +2 -2
  44. package/dist/admin/assets/{RadioTile-DsTyz7ml.js.map → RadioTile-BpB79rIK.js.map} +1 -1
  45. package/dist/admin/assets/{RichTextField-BGkH403g.js → RichTextField-BuHCONPu.js} +2 -2
  46. package/dist/admin/assets/{RichTextField-BGkH403g.js.map → RichTextField-BuHCONPu.js.map} +1 -1
  47. package/dist/admin/assets/{RowTitle-sFt6NjO4.js → RowTitle--uFCpx8n.js} +2 -2
  48. package/dist/admin/assets/{RowTitle-sFt6NjO4.js.map → RowTitle--uFCpx8n.js.map} +1 -1
  49. package/dist/admin/assets/{SecretReveal-Z6MOFdcL.js → SecretReveal-DFpzeNE0.js} +2 -2
  50. package/dist/admin/assets/{SecretReveal-Z6MOFdcL.js.map → SecretReveal-DFpzeNE0.js.map} +1 -1
  51. package/dist/admin/assets/SharedGlyph-Cqkx30tn.js +2 -0
  52. package/dist/admin/assets/SharedGlyph-Cqkx30tn.js.map +1 -0
  53. package/dist/admin/assets/{StepList-BTP_EMzx.js → StepList-BY5MuJPJ.js} +2 -2
  54. package/dist/admin/assets/{StepList-BTP_EMzx.js.map → StepList-BY5MuJPJ.js.map} +1 -1
  55. package/dist/admin/assets/Structure-CqRa2t7z.js +2 -0
  56. package/dist/admin/assets/{Structure-D5-hCUuF.js.map → Structure-CqRa2t7z.js.map} +1 -1
  57. package/dist/admin/assets/SubNavLink-B2o4kSZ9.js +2 -0
  58. package/dist/admin/assets/{SubNavLink-C5oN5mzB.js.map → SubNavLink-B2o4kSZ9.js.map} +1 -1
  59. package/dist/admin/assets/{SuggestAltButton-BuN35BBL.js → SuggestAltButton-4rf2xhEJ.js} +2 -2
  60. package/dist/admin/assets/{SuggestAltButton-BuN35BBL.js.map → SuggestAltButton-4rf2xhEJ.js.map} +1 -1
  61. package/dist/admin/assets/TextLink-D15oyXlc.js +2 -0
  62. package/dist/admin/assets/{TextLink-B4a6j3r6.js.map → TextLink-D15oyXlc.js.map} +1 -1
  63. package/dist/admin/assets/{Thumbnail-u8NE5NhQ.js → Thumbnail-CP5mfgNY.js} +2 -2
  64. package/dist/admin/assets/{Thumbnail-u8NE5NhQ.js.map → Thumbnail-CP5mfgNY.js.map} +1 -1
  65. package/dist/admin/assets/UnsavedChangesGuard-DgOASh0p.js +2 -0
  66. package/dist/admin/assets/{UnsavedChangesGuard-BYwMTDsE.js.map → UnsavedChangesGuard-DgOASh0p.js.map} +1 -1
  67. package/dist/admin/assets/{UserAvatar-D-yX0ozh.js → UserAvatar-BocvNVL-.js} +2 -2
  68. package/dist/admin/assets/{UserAvatar-D-yX0ozh.js.map → UserAvatar-BocvNVL-.js.map} +1 -1
  69. package/dist/admin/assets/{arrow-up-Ws7W9KsZ.js → arrow-up-gf_j6SVQ.js} +2 -2
  70. package/dist/admin/assets/{arrow-up-Ws7W9KsZ.js.map → arrow-up-gf_j6SVQ.js.map} +1 -1
  71. package/dist/admin/assets/auth-pcS9CCXu.js +2 -0
  72. package/dist/admin/assets/auth-pcS9CCXu.js.map +1 -0
  73. package/dist/admin/assets/badge-DD0mE96i.js +2 -0
  74. package/dist/admin/assets/{badge-CMox6-v3.js.map → badge-DD0mE96i.js.map} +1 -1
  75. package/dist/admin/assets/boxes-DcK-kaSa.js +2 -0
  76. package/dist/admin/assets/boxes-DcK-kaSa.js.map +1 -0
  77. package/dist/admin/assets/{calendar-clock-9W0Lkt89.js → calendar-clock-DEwACe0M.js} +2 -2
  78. package/dist/admin/assets/{calendar-clock-9W0Lkt89.js.map → calendar-clock-DEwACe0M.js.map} +1 -1
  79. package/dist/admin/assets/{changeSets-CO7JdILW.js → changeSets-BjUEfYbj.js} +2 -2
  80. package/dist/admin/assets/{changeSets-CO7JdILW.js.map → changeSets-BjUEfYbj.js.map} +1 -1
  81. package/dist/admin/assets/chevron-left-BsQYDUEC.js +2 -0
  82. package/dist/admin/assets/{chevron-left-DyqYjcMB.js.map → chevron-left-BsQYDUEC.js.map} +1 -1
  83. package/dist/admin/assets/{circle-check-aJ2DW5t4.js → circle-check-Dq1owihe.js} +2 -2
  84. package/dist/admin/assets/{circle-check-aJ2DW5t4.js.map → circle-check-Dq1owihe.js.map} +1 -1
  85. package/dist/admin/assets/{circle-x-BnRBht9z.js → circle-x-Dx6zIZko.js} +2 -2
  86. package/dist/admin/assets/{circle-x-BnRBht9z.js.map → circle-x-Dx6zIZko.js.map} +1 -1
  87. package/dist/admin/assets/{clock-DRtFQbTs.js → clock-DQvkffqQ.js} +2 -2
  88. package/dist/admin/assets/{clock-DRtFQbTs.js.map → clock-DQvkffqQ.js.map} +1 -1
  89. package/dist/admin/assets/cn-BQ3voEft.js +2 -0
  90. package/dist/admin/assets/cn-BQ3voEft.js.map +1 -0
  91. package/dist/admin/assets/coerce-OsqQRj19.js +2 -0
  92. package/dist/admin/assets/{coerce-COM3rk1i.js.map → coerce-OsqQRj19.js.map} +1 -1
  93. package/dist/admin/assets/constants-SrImR6C3.js +2 -0
  94. package/dist/admin/assets/constants-SrImR6C3.js.map +1 -0
  95. package/dist/admin/assets/content-_SLbbnfX.js +3 -0
  96. package/dist/admin/assets/content-_SLbbnfX.js.map +1 -0
  97. package/dist/admin/assets/{contentCounts-Ct0tYgHD.js → contentCounts-Ch3uMXVU.js} +2 -2
  98. package/dist/admin/assets/{contentCounts-Ct0tYgHD.js.map → contentCounts-Ch3uMXVU.js.map} +1 -1
  99. package/dist/admin/assets/contentTypes-BCOdUK_Z.js +2 -0
  100. package/dist/admin/assets/contentTypes-BCOdUK_Z.js.map +1 -0
  101. package/dist/admin/assets/{copy-B5B8RjV0.js → copy-BDc3r0fO.js} +2 -2
  102. package/dist/admin/assets/{copy-B5B8RjV0.js.map → copy-BDc3r0fO.js.map} +1 -1
  103. package/dist/admin/assets/{dateTimeLocal-BuBv3it2.js → dateTimeLocal-C92tyKwz.js} +2 -2
  104. package/dist/admin/assets/{dateTimeLocal-BuBv3it2.js.map → dateTimeLocal-C92tyKwz.js.map} +1 -1
  105. package/dist/admin/assets/develop-DS9TlDmv.js +11 -0
  106. package/dist/admin/assets/develop-DS9TlDmv.js.map +1 -0
  107. package/dist/admin/assets/{dropdown-menu-CW3fabv5.js → dropdown-menu-gmP2Rp3n.js} +2 -2
  108. package/dist/admin/assets/{dropdown-menu-CW3fabv5.js.map → dropdown-menu-gmP2Rp3n.js.map} +1 -1
  109. package/dist/admin/assets/{ellipsis-DNdmromA.js → ellipsis-NK0pWgqU.js} +2 -2
  110. package/dist/admin/assets/{ellipsis-DNdmromA.js.map → ellipsis-NK0pWgqU.js.map} +1 -1
  111. package/dist/admin/assets/{external-link-YnuWXDBk.js → external-link-D3Aoi2yX.js} +2 -2
  112. package/dist/admin/assets/{external-link-YnuWXDBk.js.map → external-link-D3Aoi2yX.js.map} +1 -1
  113. package/dist/admin/assets/{file-text-CB7Bc0GA.js → file-text-DxXAmmxL.js} +2 -2
  114. package/dist/admin/assets/{file-text-CB7Bc0GA.js.map → file-text-DxXAmmxL.js.map} +1 -1
  115. package/dist/admin/assets/fileRoute-C2tOrKhO.js +2 -0
  116. package/dist/admin/assets/{fileRoute-BYwireXG.js.map → fileRoute-C2tOrKhO.js.map} +1 -1
  117. package/dist/admin/assets/followPlural-CN6cQfNk.js +2 -0
  118. package/dist/admin/assets/followPlural-CN6cQfNk.js.map +1 -0
  119. package/dist/admin/assets/{globe-Bvs3UC3V.js → globe-CuElSilX.js} +2 -2
  120. package/dist/admin/assets/{globe-Bvs3UC3V.js.map → globe-CuElSilX.js.map} +1 -1
  121. package/dist/admin/assets/{image-CeoxwJeM.js → image-CsH8dZve.js} +2 -2
  122. package/dist/admin/assets/{image-CeoxwJeM.js.map → image-CsH8dZve.js.map} +1 -1
  123. package/dist/admin/assets/{image-off-iVqYw2Zc.js → image-off-B_uBCsYu.js} +2 -2
  124. package/dist/admin/assets/{image-off-iVqYw2Zc.js.map → image-off-B_uBCsYu.js.map} +1 -1
  125. package/dist/admin/assets/{inbox-DYbEOpaQ.js → inbox-3iAHL3pC.js} +2 -2
  126. package/dist/admin/assets/{inbox-DYbEOpaQ.js.map → inbox-3iAHL3pC.js.map} +1 -1
  127. package/dist/admin/assets/inbox-DsuXNaKE.js +2 -0
  128. package/dist/admin/assets/{inbox-wDoqym--.js.map → inbox-DsuXNaKE.js.map} +1 -1
  129. package/dist/admin/assets/index-BSzXA60W.js +4 -0
  130. package/dist/admin/assets/index-BSzXA60W.js.map +1 -0
  131. package/dist/admin/assets/{key-round-Bcw6qqcb.js → key-round-B17HiT3K.js} +2 -2
  132. package/dist/admin/assets/{key-round-Bcw6qqcb.js.map → key-round-B17HiT3K.js.map} +1 -1
  133. package/dist/admin/assets/link-2-off-hWGS8LW6.js +2 -0
  134. package/dist/admin/assets/link-2-off-hWGS8LW6.js.map +1 -0
  135. package/dist/admin/assets/{link-CKr_uUfb.js → link-bhHmG4Tc.js} +2 -2
  136. package/dist/admin/assets/{link-CKr_uUfb.js.map → link-bhHmG4Tc.js.map} +1 -1
  137. package/dist/admin/assets/{list-BElvKZw_.js → list-DRtRdjBC.js} +2 -2
  138. package/dist/admin/assets/{list-BElvKZw_.js.map → list-DRtRdjBC.js.map} +1 -1
  139. package/dist/admin/assets/{locales-Bnv3qcdZ.js → locales-BzPbz8FF.js} +2 -2
  140. package/dist/admin/assets/{locales-Bnv3qcdZ.js.map → locales-BzPbz8FF.js.map} +1 -1
  141. package/dist/admin/assets/media-WLnWaL7U.js +2 -0
  142. package/dist/admin/assets/{media-DjK3xGFN.js.map → media-WLnWaL7U.js.map} +1 -1
  143. package/dist/admin/assets/{media-BfDqe_2P.js → media-Xzot1ueY.js} +2 -2
  144. package/dist/admin/assets/{media-BfDqe_2P.js.map → media-Xzot1ueY.js.map} +1 -1
  145. package/dist/admin/assets/network-DHGqLwji.js +2 -0
  146. package/dist/admin/assets/network-DHGqLwji.js.map +1 -0
  147. package/dist/admin/assets/{pencil-Zd05MDs2.js → pencil-Bdp2z9D_.js} +2 -2
  148. package/dist/admin/assets/{pencil-Zd05MDs2.js.map → pencil-Bdp2z9D_.js.map} +1 -1
  149. package/dist/admin/assets/{plus-B0gd63Da.js → plus-BN_iSsnp.js} +2 -2
  150. package/dist/admin/assets/{plus-B0gd63Da.js.map → plus-BN_iSsnp.js.map} +1 -1
  151. package/dist/admin/assets/{preload-helper-CJKHb_rG.js → preload-helper-DAbCfXID.js} +2 -2
  152. package/dist/admin/assets/{preload-helper-CJKHb_rG.js.map → preload-helper-DAbCfXID.js.map} +1 -1
  153. package/dist/admin/assets/publishing-d9yhMwgA.js +2 -0
  154. package/dist/admin/assets/{publishing-vCVjaSGF.js.map → publishing-d9yhMwgA.js.map} +1 -1
  155. package/dist/admin/assets/{radio-group-DVR0mTpo.js → radio-group-DHIxK-IU.js} +2 -2
  156. package/dist/admin/assets/{radio-group-DVR0mTpo.js.map → radio-group-DHIxK-IU.js.map} +1 -1
  157. package/dist/admin/assets/{rotate-ccw-BOTDOdHf.js → rotate-ccw-CBoZle-b.js} +2 -2
  158. package/dist/admin/assets/{rotate-ccw-BOTDOdHf.js.map → rotate-ccw-CBoZle-b.js.map} +1 -1
  159. package/dist/admin/assets/{schedules-CG-ZphRK.js → schedules-Dy89Nm6P.js} +2 -2
  160. package/dist/admin/assets/{schedules-CG-ZphRK.js.map → schedules-Dy89Nm6P.js.map} +1 -1
  161. package/dist/admin/assets/schema-B4smPFcR.js +2 -0
  162. package/dist/admin/assets/schema-B4smPFcR.js.map +1 -0
  163. package/dist/admin/assets/{scroll-text-D-gHqYwR.js → scroll-text-UbyzpWh-.js} +2 -2
  164. package/dist/admin/assets/{scroll-text-D-gHqYwR.js.map → scroll-text-UbyzpWh-.js.map} +1 -1
  165. package/dist/admin/assets/{search-BcPL1qyc.js → search-_YlNpPvU.js} +2 -2
  166. package/dist/admin/assets/{search-BcPL1qyc.js.map → search-_YlNpPvU.js.map} +1 -1
  167. package/dist/admin/assets/searchSchema-CvOhn4FV.js +2 -0
  168. package/dist/admin/assets/{searchSchema-BGrRDBL_.js.map → searchSchema-CvOhn4FV.js.map} +1 -1
  169. package/dist/admin/assets/searchSchemas-BxRnxenn.js +2 -0
  170. package/dist/admin/assets/searchSchemas-BxRnxenn.js.map +1 -0
  171. package/dist/admin/assets/{sections-i9TeeK-h.js → sections-Bb3Q4bcY.js} +2 -2
  172. package/dist/admin/assets/{sections-i9TeeK-h.js.map → sections-Bb3Q4bcY.js.map} +1 -1
  173. package/dist/admin/assets/{sections-BrGcWOnw.js → sections-DiySZZHb.js} +2 -2
  174. package/dist/admin/assets/{sections-BrGcWOnw.js.map → sections-DiySZZHb.js.map} +1 -1
  175. package/dist/admin/assets/{select--nGB4WjU.js → select-tHVWX85X.js} +2 -2
  176. package/dist/admin/assets/{select--nGB4WjU.js.map → select-tHVWX85X.js.map} +1 -1
  177. package/dist/admin/assets/{send-Bu3RRfdI.js → send-BqECHYi6.js} +2 -2
  178. package/dist/admin/assets/{send-Bu3RRfdI.js.map → send-BqECHYi6.js.map} +1 -1
  179. package/dist/admin/assets/settings-D-v8gL8F.js +2 -0
  180. package/dist/admin/assets/settings-D-v8gL8F.js.map +1 -0
  181. package/dist/admin/assets/skeleton-C3QLIh7W.js +42 -0
  182. package/dist/admin/assets/skeleton-C3QLIh7W.js.map +1 -0
  183. package/dist/admin/assets/snapshots-BQrhsCmC.js +2 -0
  184. package/dist/admin/assets/{snapshots-CKjK-vkL.js.map → snapshots-BQrhsCmC.js.map} +1 -1
  185. package/dist/admin/assets/{sparkles-B_NX7PAY.js → sparkles-BW4CbD6W.js} +2 -2
  186. package/dist/admin/assets/{sparkles-B_NX7PAY.js.map → sparkles-BW4CbD6W.js.map} +1 -1
  187. package/dist/admin/assets/src-XaU0N8C-.js +4 -0
  188. package/dist/admin/assets/src-XaU0N8C-.js.map +1 -0
  189. package/dist/admin/assets/{stableId-Dmr2Afn_.js → stableId-C9Yq5iUw.js} +2 -2
  190. package/dist/admin/assets/{stableId-Dmr2Afn_.js.map → stableId-C9Yq5iUw.js.map} +1 -1
  191. package/dist/admin/assets/{store-B5xrQ1Ex.js → store-BphwUzV5.js} +2 -2
  192. package/dist/admin/assets/{store-B5xrQ1Ex.js.map → store-BphwUzV5.js.map} +1 -1
  193. package/dist/admin/assets/{switch-B-FbT-l8.js → switch-BVy02Vc8.js} +2 -2
  194. package/dist/admin/assets/{switch-B-FbT-l8.js.map → switch-BVy02Vc8.js.map} +1 -1
  195. package/dist/admin/assets/{table-DQzCPm7J.js → table-CZYtdoHn.js} +2 -2
  196. package/dist/admin/assets/{table-DQzCPm7J.js.map → table-CZYtdoHn.js.map} +1 -1
  197. package/dist/admin/assets/tabs-quHbeVxA.js +5 -0
  198. package/dist/admin/assets/{tabs-YThoOiE3.js.map → tabs-quHbeVxA.js.map} +1 -1
  199. package/dist/admin/assets/{textarea-_wcx8Mn1.js → textarea-B8Vl5gJ1.js} +2 -2
  200. package/dist/admin/assets/{textarea-_wcx8Mn1.js.map → textarea-B8Vl5gJ1.js.map} +1 -1
  201. package/dist/admin/assets/titles-B2z5ckxG.js +2 -0
  202. package/dist/admin/assets/{titles-DyCBLXue.js.map → titles-B2z5ckxG.js.map} +1 -1
  203. package/dist/admin/assets/{tokens-CqsxrApC.js → tokens-Fh_OeIlf.js} +2 -2
  204. package/dist/admin/assets/{tokens-CqsxrApC.js.map → tokens-Fh_OeIlf.js.map} +1 -1
  205. package/dist/admin/assets/{tooltip-BMe4HylJ.js → tooltip-DwqM3opE.js} +2 -2
  206. package/dist/admin/assets/{tooltip-BMe4HylJ.js.map → tooltip-DwqM3opE.js.map} +1 -1
  207. package/dist/admin/assets/{undo-2-DMOHOH6w.js → undo-2-D4Fz4JMZ.js} +2 -2
  208. package/dist/admin/assets/{undo-2-DMOHOH6w.js.map → undo-2-D4Fz4JMZ.js.map} +1 -1
  209. package/dist/admin/assets/{useAuditLabels-CSw_kwLs.js → useAuditLabels-DKrsrciQ.js} +2 -2
  210. package/dist/admin/assets/{useAuditLabels-CSw_kwLs.js.map → useAuditLabels-DKrsrciQ.js.map} +1 -1
  211. package/dist/admin/assets/useConfirmTarget-Dp8mCulm.js +2 -0
  212. package/dist/admin/assets/useConfirmTarget-Dp8mCulm.js.map +1 -0
  213. package/dist/admin/assets/{useContentSchema-CS5QQcRk.js → useContentSchema-Bv4_6VJR.js} +2 -2
  214. package/dist/admin/assets/{useContentSchema-CS5QQcRk.js.map → useContentSchema-Bv4_6VJR.js.map} +1 -1
  215. package/dist/admin/assets/{useCursorPager-Dt_axwd1.js → useCursorPager-B1upg5EU.js} +2 -2
  216. package/dist/admin/assets/{useCursorPager-Dt_axwd1.js.map → useCursorPager-B1upg5EU.js.map} +1 -1
  217. package/dist/admin/assets/{useDiscardGuard-7rRGpd4q.js → useDiscardGuard-34ynGJIk.js} +2 -2
  218. package/dist/admin/assets/{useDiscardGuard-7rRGpd4q.js.map → useDiscardGuard-34ynGJIk.js.map} +1 -1
  219. package/dist/admin/assets/{useMediaUploader-CBUcNW6d.js → useMediaUploader-QXSjZStv.js} +2 -2
  220. package/dist/admin/assets/{useMediaUploader-CBUcNW6d.js.map → useMediaUploader-QXSjZStv.js.map} +1 -1
  221. package/dist/admin/assets/{usePaletteItems-bbTIqpPu.js → usePaletteItems-62-TZhy7.js} +2 -2
  222. package/dist/admin/assets/{usePaletteItems-bbTIqpPu.js.map → usePaletteItems-62-TZhy7.js.map} +1 -1
  223. package/dist/admin/assets/usePlaces-CY6f2DmI.js +2 -0
  224. package/dist/admin/assets/usePlaces-CY6f2DmI.js.map +1 -0
  225. package/dist/admin/assets/{useQueries-DKGIdq6A.js → useQueries-BuKJBA0M.js} +2 -2
  226. package/dist/admin/assets/{useQueries-DKGIdq6A.js.map → useQueries-BuKJBA0M.js.map} +1 -1
  227. package/dist/admin/assets/useSchemaScopeAccess-DUBeS9kU.js +2 -0
  228. package/dist/admin/assets/useSchemaScopeAccess-DUBeS9kU.js.map +1 -0
  229. package/dist/admin/assets/{users-C1ZSee3N.js → users-BOn2ohJQ.js} +2 -2
  230. package/dist/admin/assets/{users-C1ZSee3N.js.map → users-BOn2ohJQ.js.map} +1 -1
  231. package/dist/admin/assets/{users-DMfQL0s4.js → users-BkKc4MNB.js} +2 -2
  232. package/dist/admin/assets/{users-DMfQL0s4.js.map → users-BkKc4MNB.js.map} +1 -1
  233. package/dist/admin/assets/{zod-Pf5A-ax4.js → zod-BHAd1wTC.js} +2 -2
  234. package/dist/admin/assets/{zod-Pf5A-ax4.js.map → zod-BHAd1wTC.js.map} +1 -1
  235. package/dist/admin/index.html +56 -55
  236. package/dist/cli.js +383 -163
  237. package/dist/cli.js.map +1 -1
  238. package/dist/config.d.ts +1 -1
  239. package/dist/config.js.map +1 -1
  240. package/dist/index.d.ts +6 -1
  241. package/dist/index.js +1 -1
  242. package/dist/{public-CKU0Gkfq.d.ts → public-CGO8RBn3.d.ts} +10 -2
  243. package/dist/{worker-C-MSYr4l.js → worker-BLXy8aRW.js} +2307 -1220
  244. package/dist/worker-BLXy8aRW.js.map +1 -0
  245. package/package.json +5 -6
  246. package/dist/admin/assets/Body-CFQKONK4.js +0 -2
  247. package/dist/admin/assets/Builder-Do5KVHV9.js +0 -2
  248. package/dist/admin/assets/Builder-Do5KVHV9.js.map +0 -1
  249. package/dist/admin/assets/LinkTabs-Bn2JVqOL.js +0 -2
  250. package/dist/admin/assets/Match-DaYgKrk7.js +0 -2
  251. package/dist/admin/assets/Structure-D5-hCUuF.js +0 -2
  252. package/dist/admin/assets/SubNavLink-C5oN5mzB.js +0 -2
  253. package/dist/admin/assets/TextLink-B4a6j3r6.js +0 -2
  254. package/dist/admin/assets/UnsavedChangesGuard-BYwMTDsE.js +0 -2
  255. package/dist/admin/assets/auth-b3_9QbtL.js +0 -2
  256. package/dist/admin/assets/auth-b3_9QbtL.js.map +0 -1
  257. package/dist/admin/assets/badge-CMox6-v3.js +0 -2
  258. package/dist/admin/assets/chevron-left-DyqYjcMB.js +0 -2
  259. package/dist/admin/assets/cn-DnXL75B0.js +0 -2
  260. package/dist/admin/assets/cn-DnXL75B0.js.map +0 -1
  261. package/dist/admin/assets/coerce-COM3rk1i.js +0 -2
  262. package/dist/admin/assets/constants-CS1ziZO0.js +0 -2
  263. package/dist/admin/assets/constants-CS1ziZO0.js.map +0 -1
  264. package/dist/admin/assets/content-Dic2fof_.js +0 -3
  265. package/dist/admin/assets/content-Dic2fof_.js.map +0 -1
  266. package/dist/admin/assets/contentTypes-D4_Ax9rb.js +0 -2
  267. package/dist/admin/assets/contentTypes-D4_Ax9rb.js.map +0 -1
  268. package/dist/admin/assets/develop-Brnb5YPC.js +0 -11
  269. package/dist/admin/assets/develop-Brnb5YPC.js.map +0 -1
  270. package/dist/admin/assets/errors-DfoXUVDI.js +0 -2
  271. package/dist/admin/assets/errors-DfoXUVDI.js.map +0 -1
  272. package/dist/admin/assets/fileRoute-BYwireXG.js +0 -2
  273. package/dist/admin/assets/inbox-wDoqym--.js +0 -2
  274. package/dist/admin/assets/index-57Mn6XLO.js +0 -4
  275. package/dist/admin/assets/index-57Mn6XLO.js.map +0 -1
  276. package/dist/admin/assets/link-2-DhM2vi4v.js +0 -2
  277. package/dist/admin/assets/link-2-DhM2vi4v.js.map +0 -1
  278. package/dist/admin/assets/link-2-off-DpDOMsa-.js +0 -2
  279. package/dist/admin/assets/link-2-off-DpDOMsa-.js.map +0 -1
  280. package/dist/admin/assets/media-DjK3xGFN.js +0 -2
  281. package/dist/admin/assets/network-CAJ76ILr.js +0 -2
  282. package/dist/admin/assets/network-CAJ76ILr.js.map +0 -1
  283. package/dist/admin/assets/publishing-vCVjaSGF.js +0 -2
  284. package/dist/admin/assets/schema-KXXSPVJn.js +0 -2
  285. package/dist/admin/assets/schema-KXXSPVJn.js.map +0 -1
  286. package/dist/admin/assets/searchSchema-BGrRDBL_.js +0 -2
  287. package/dist/admin/assets/searchSchemas-ChN9hhuQ.js +0 -2
  288. package/dist/admin/assets/searchSchemas-ChN9hhuQ.js.map +0 -1
  289. package/dist/admin/assets/settings-68BAxrPM.js +0 -2
  290. package/dist/admin/assets/settings-68BAxrPM.js.map +0 -1
  291. package/dist/admin/assets/skeleton-DUvclBAr.js +0 -42
  292. package/dist/admin/assets/skeleton-DUvclBAr.js.map +0 -1
  293. package/dist/admin/assets/snapshots-CKjK-vkL.js +0 -2
  294. package/dist/admin/assets/src-D6DABe5J.js +0 -4
  295. package/dist/admin/assets/src-D6DABe5J.js.map +0 -1
  296. package/dist/admin/assets/tabs-YThoOiE3.js +0 -5
  297. package/dist/admin/assets/titles-DyCBLXue.js +0 -2
  298. package/dist/admin/assets/useConfirmTarget-9JmfiY2X.js +0 -2
  299. package/dist/admin/assets/useConfirmTarget-9JmfiY2X.js.map +0 -1
  300. package/dist/admin/assets/usePlaces-D1raoQ2s.js +0 -2
  301. package/dist/admin/assets/usePlaces-D1raoQ2s.js.map +0 -1
  302. 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 parseLockFile, C as BEFORE_EVENTS, D as listForAssets, E as buildEditorManifest, F as RESERVED_FIELD_API_KEYS, G as validateSchema, H as serializeLockFile, I as RESERVED_TYPE_NAMES, J as createStableId, K as RICHTEXT_FORMAT, L as foldApiKey, M as describeError, O as listLiveByDriver, P as RESERVED_DEFINITION_API_KEYS, R as hashDefinition, S as AFTER_EVENTS, T as EXTENSION_ROUTE_ROOT, U as suggestPlural, V as serializeDefinition, W as toTypeName, X as createDb, Y as validateRichText, Z as sqliteDriverOf, _ as createLogger, a as loadConfig, b as SITE_KEY_PATTERN, c as getPendingMigrations, d as createMediaStorage, f as createSite, g as generateToken, h as SYSTEM_CLI_ACTOR, i as assertDatabaseReachable, j as STORAGE_DRIVERS, k as lockById, l as migrateToLatest, m as createAdminDirect, n as startServer, o as loadEnvFileIfPresent, p as listAllSites, q as SAFE_LINK_PATTERN, r as copyObjectVerified, t as startDedicatedWorker, u as SHAPIO_VERSION, v as createUrlBuilder, w as EXTENSION_JOB_PREFIX, x as loadProjectConfig, y as loopbackUrl, z as parseDefinition } from "./worker-C-MSYr4l.js";
2
+ import { $ as validateSchema, A as update, B as hashDefinition, C as BEFORE_EVENTS, D as listForAssets, E as buildEditorManifest, F as RESERVED_FIELD_API_KEYS, G as parseLockFile, H as lockEntriesForSite, I as RESERVED_TYPE_NAMES, J as serializeDefinition, K as schemaFilePath, L as SCHEMA_KIND_DIRECTORIES, M as describeError, O as listLiveByDriver, P as RESERVED_DEFINITION_API_KEYS, Q as upgradeLockFile, R as SITES_DIRECTORY, S as AFTER_EVENTS, T as EXTENSION_ROUTE_ROOT, U as mergePulledLock, V as lockCoversSite, W as parseDefinition, X as suggestPlural, Y as serializeLockFile, Z as toTypeName, _ as createLogger, a as loadConfig, at as createDb, b as SITE_KEY_PATTERN, c as getPendingMigrations, d as createMediaStorage, et as withSiteCovered, f as createSite, g as generateToken, h as SYSTEM_CLI_ACTOR, i as assertDatabaseReachable, it as validateRichText, j as STORAGE_DRIVERS, k as lockById, l as migrateToLatest, m as createAdminDirect, n as startServer, nt as SAFE_LINK_PATTERN, o as loadEnvFileIfPresent, ot as sqliteDriverOf, p as listAllSites, q as scopeDirectory, r as copyObjectVerified, rt as createStableId, t as startDedicatedWorker, tt as RICHTEXT_FORMAT, u as SHAPIO_VERSION, v as createUrlBuilder, w as EXTENSION_JOB_PREFIX, x as loadProjectConfig, y as loopbackUrl, z as foldApiKey } from "./worker-BLXy8aRW.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";
@@ -472,7 +472,8 @@ const DEFINITION_PATHS = {
472
472
  const createDefinitionApi = (request, category) => {
473
473
  const base = DEFINITION_PATHS[category];
474
474
  return {
475
- list: async () => (await request(base)).items,
475
+ /** The site's view (shared definitions and its own); `{ scope: 'network' }` lists the shared ones. */
476
+ list: async (options = {}) => (await request(`${base}${toQueryString(options)}`)).items,
476
477
  get: (id) => request(withId(base, id)),
477
478
  /** Creates and activates a definition (`pending` when prerequisites must run first). */
478
479
  create: (body) => request(base, {
@@ -480,9 +481,17 @@ const createDefinitionApi = (request, category) => {
480
481
  body
481
482
  }),
482
483
  /** Plan preview of a create: classification and impact, nothing written. */
483
- planCreate: (definition) => request(`${base}/plan`, {
484
+ planCreate: (definition, scope) => request(`${base}/plan`, {
484
485
  method: "POST",
485
- body: { definition }
486
+ body: scope ? {
487
+ definition,
488
+ scope
489
+ } : { definition }
490
+ }),
491
+ /** Shares a definition with all sites, or keeps it on one (needs schema.create on every site). */
492
+ changeScope: (id, body) => request(`${withId(base, id)}/scope`, {
493
+ method: "PUT",
494
+ body
486
495
  }),
487
496
  /** Changes a definition; 409 SCHEMA_VERSION_CONFLICT when `expectedVersion` is stale. */
488
497
  update: (id, body) => request(withId(base, id), {
@@ -508,7 +517,7 @@ const createSchemaApi = (request) => ({
508
517
  summary: () => request(ADMIN_PATHS.schema),
509
518
  change: (changeId) => request(withId(ADMIN_PATHS.schemaChanges, changeId)),
510
519
  /** Every definition in the pull format, with the schema version they come from (`shapio schema pull`). */
511
- export: () => request(ADMIN_PATHS.schemaExport),
520
+ export: (options = {}) => request(`${ADMIN_PATHS.schemaExport}${toQueryString(options)}`),
512
521
  /** Applies definition files live through the change planner; refuses when the base moved (409). */
513
522
  apply: (body) => request(ADMIN_PATHS.schemaApply, {
514
523
  method: "POST",
@@ -2802,41 +2811,48 @@ const writeOrder = (entries) => {
2802
2811
  deferred
2803
2812
  };
2804
2813
  };
2805
- /** Schema files: one canonical JSON file per definition, `models/<apiKey>.json` or `components/<apiKey>.json`. */
2806
- const KIND_DIRECTORIES = ["models", "components"];
2807
- const definitionPath = (dir, definition) => join(dir, definition.kind === "component" ? "components" : "models", `${definition.apiKey}.json`);
2814
+ /** The file of a definition in a scope (`site`: its site key, null when shared). */
2815
+ const definitionPath = (dir, definition, site) => join(dir, ...schemaFilePath(definition, site).split("/"));
2808
2816
  const isMissing = (error) => error.code === "ENOENT";
2809
- /** Every schema file under `dir`, with its ID and canonical hash when it parses. */
2810
- const readLocalFiles = async (dir) => {
2811
- const files = [];
2812
- for (const kindDir of KIND_DIRECTORIES) {
2813
- let names;
2814
- try {
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
- }
2817
+ const listNames = async (path, filter) => {
2818
+ try {
2819
+ return (await readdir(path, { withFileTypes: true })).filter(filter).map((entry) => entry.name).sort();
2820
+ } catch (error) {
2821
+ if (isMissing(error)) return [];
2822
+ throw error;
2839
2823
  }
2824
+ };
2825
+ const readLocalFile = async (path, site) => {
2826
+ let raw;
2827
+ try {
2828
+ raw = JSON.parse(await readFile(path, "utf8"));
2829
+ } catch (error) {
2830
+ throw new Error(`${path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
2831
+ }
2832
+ const parsed = parseDefinition(raw);
2833
+ return parsed.ok ? {
2834
+ path,
2835
+ raw,
2836
+ site,
2837
+ id: parsed.definition.id,
2838
+ hash: await hashDefinition(parsed.definition)
2839
+ } : {
2840
+ path,
2841
+ raw,
2842
+ site
2843
+ };
2844
+ };
2845
+ /** The files of one scope's folder (`models/` and `components/` under it). */
2846
+ const readScopeFiles = async (dir, site) => {
2847
+ const base = join(dir, ...scopeDirectory(site).split("/").filter(Boolean));
2848
+ const files = [];
2849
+ 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));
2850
+ return files;
2851
+ };
2852
+ /** Every schema file under `dir`: the shared folders first, then each site folder by key. */
2853
+ const readLocalFiles = async (dir) => {
2854
+ const files = await readScopeFiles(dir, null);
2855
+ for (const siteKey of await listNames(join(dir, SITES_DIRECTORY), (entry) => entry.isDirectory() && !entry.name.startsWith("."))) files.push(...await readScopeFiles(dir, siteKey));
2840
2856
  return files;
2841
2857
  };
2842
2858
  const readLockFile = async (path) => {
@@ -2851,8 +2867,8 @@ const writeLockFile = async (path, lock) => {
2851
2867
  await mkdir(dirname(path), { recursive: true });
2852
2868
  await writeFile(path, serializeLockFile(lock));
2853
2869
  };
2854
- const writeDefinitionFile = async (dir, definition) => {
2855
- const path = definitionPath(dir, definition);
2870
+ const writeDefinitionFile = async (dir, definition, site) => {
2871
+ const path = definitionPath(dir, definition, site);
2856
2872
  await mkdir(dirname(path), { recursive: true });
2857
2873
  await writeFile(path, serializeDefinition(definition));
2858
2874
  return path;
@@ -2905,23 +2921,103 @@ const formatItem = (item, names) => {
2905
2921
  ].filter(Boolean);
2906
2922
  return [`${item.kind === "component" ? "component" : "model"} ${item.apiKey}: ${describeDecision(item)}${flags.length ? ` [${flags.join(", ")}]` : ""}`, ...item.changes.map((change) => ` ${formatChange(change, names)}`)].join("\n");
2907
2923
  };
2908
- /** Explains a refused apply: conflicts with diffs, invalid files, missing acknowledgements. */
2924
+ const FOLDER_OF = {
2925
+ network: "the shared folders",
2926
+ site: "the site’s folder"
2927
+ };
2928
+ const PLACE_OF = {
2929
+ network: "shared",
2930
+ site: "on one site"
2931
+ };
2932
+ const SCOPE_OF = {
2933
+ network: "shared",
2934
+ site: "site"
2935
+ };
2936
+ /** One line per item of a scope refusal (`SCOPE_MISMATCH`, `FORBIDDEN_SCOPE`). */
2937
+ const scopeItemLines = (code, items) => items.map((item) => {
2938
+ const name = item.apiKey ?? item.definitionId ?? "?";
2939
+ 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"}`;
2940
+ });
2941
+ /** What to do next, per refusal. */
2942
+ const HINTS = {
2943
+ SCHEMA_CHANGE_NOT_ACKNOWLEDGED: "Review the changes with `shapio schema diff`, then re-run with --allow-breaking and/or --allow-destructive.",
2944
+ SCHEMA_SYNC_CONFLICT: "Commit your files, run `shapio schema pull --force`, reconcile with git, then apply again.",
2945
+ 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`.",
2946
+ FORBIDDEN_SCOPE: "Shared definitions (in models/ and components/) need schema permission on every site: apply with a network token, or leave those files unchanged."
2947
+ };
2948
+ const issueWhere = (found, filePaths) => {
2949
+ const match = /^\/definitions\/(\d+)(.*)$/.exec(found.path ?? "");
2950
+ const where = match ? `${filePaths[Number(match[1])] ?? `definition ${match[1]}`} ${match[2] || "/"}` : found.path ?? "";
2951
+ return found.siteKey ? `${where} (site ${found.siteKey})` : where;
2952
+ };
2953
+ 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"}`] : []);
2954
+ /** Explains a refused request: conflicts with diffs, invalid files, scope refusals, missing acknowledgements. */
2909
2955
  const formatApiError = (error, filePaths, names) => {
2910
2956
  const details = error.details ?? {};
2911
2957
  const lines = [`${error.code}: ${error.message}`];
2912
2958
  for (const conflict of details.conflicts ?? []) lines.push(formatItem(conflict, names));
2913
- for (const found of details.issues ?? []) {
2914
- const match = /^\/definitions\/(\d+)(.*)$/.exec(found.path ?? "");
2915
- const where = match ? `${filePaths[Number(match[1])] ?? `definition ${match[1]}`} ${match[2] || "/"}` : found.path ?? "";
2916
- lines.push(` ${where}: ${found.message ?? ""}`);
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.");
2959
+ for (const found of details.issues ?? []) lines.push(` ${issueWhere(found, filePaths)}: ${found.message ?? ""}`);
2960
+ lines.push(...scopeItemLines(error.code, details.items ?? []));
2961
+ if (error.code === "SCOPE_IN_USE") lines.push(...siteCountLines(details.sites));
2962
+ const hint = HINTS[error.code];
2963
+ if (hint) lines.push(hint);
2920
2964
  return `${lines.join("\n")}\n`;
2921
2965
  };
2966
+ /**
2967
+ * Which part of a schema tree one site's sync is about (plan site-schema): the shared folders and that site's
2968
+ * own folder. One tree may hold several sites (`sites/<key>/`); a pull or apply for one never reads, sends,
2969
+ * rewrites or removes another site's files or lock entries.
2970
+ */
2971
+ const inView = (site, siteKey) => site === null || site === siteKey;
2972
+ /** The files an apply for `siteKey` sends: the shared ones and that site's. */
2973
+ const filesForSite = (files, siteKey) => files.filter((file) => inView(file.site, siteKey));
2974
+ /** Each sent file's scope, parallel to the files: its folder decides (only creates use it). */
2975
+ const scopesOf = (files) => files.map((file) => file.site === null ? "network" : "site");
2976
+ /**
2977
+ * The base an apply for `siteKey` sends: the lock in format 2 with only the shared entries and that site's,
2978
+ * so neither the three-way decision nor `--prune` can ever see another site's IDs. `sites` is kept: the
2979
+ * server refuses a tree that covers other sites only.
2980
+ */
2981
+ const baseForSite = (lock, siteKey) => {
2982
+ const upgraded = upgradeLockFile(lock);
2983
+ return {
2984
+ ...upgraded,
2985
+ definitions: lockEntriesForSite(upgraded, siteKey)
2986
+ };
2987
+ };
2988
+ /** Files in the site's view that differ from what was last pulled (or were never pulled). */
2989
+ const editedFilesForSite = (files, lock, siteKey) => {
2990
+ const base = lockEntriesForSite(lock, siteKey);
2991
+ if (Object.keys(base).length === 0) return [];
2992
+ return filesForSite(files, siteKey).filter((file) => {
2993
+ const entry = file.id ? base[file.id] : void 0;
2994
+ return !entry || file.hash !== entry.hash;
2995
+ });
2996
+ };
2997
+ /**
2998
+ * Files a pull of `siteKey` leaves stale: in the pulled scopes and not written (deleted or renamed on the
2999
+ * instance), plus any other file of a definition that was just written elsewhere (its scope changed on the
3000
+ * instance: it is the same definition, and two files with one ID would make the next apply fail).
3001
+ */
3002
+ const staleFilesAfterPull = (files, siteKey, written) => {
3003
+ const writtenPaths = new Set(written.values());
3004
+ return files.filter((file) => {
3005
+ if (writtenPaths.has(file.path)) return false;
3006
+ return inView(file.site, siteKey) || file.id !== void 0 && written.has(file.id);
3007
+ });
3008
+ };
3009
+ /**
3010
+ * The lock after an apply for `siteKey` recorded its entries: the tree covers the site once it holds one of
3011
+ * the site's own definitions. A shared-only tree stays uncovered, so it still applies to every site.
3012
+ */
3013
+ const coverSiteIfHeld = (lock, siteKey) => Object.values(lock.definitions).some((entry) => entry.site === siteKey) ? {
3014
+ ...lock,
3015
+ sites: withSiteCovered(lock.sites, siteKey)
3016
+ } : lock;
2922
3017
  const EMPTY_LOCK = {
2923
- formatVersion: 1,
3018
+ formatVersion: 2,
2924
3019
  schemaVersion: 0,
3020
+ sites: [],
2925
3021
  definitions: {}
2926
3022
  };
2927
3023
  /** Field and definition names by ID from the local files, for readable diffs. */
@@ -2942,17 +3038,45 @@ const readLocalState = async (options) => {
2942
3038
  names: collectNames(files)
2943
3039
  };
2944
3040
  };
2945
- /** The schema sync endpoints (`export`, `apply`, `change`) of @shapio/client. */
2946
- const apiFor = (options) => createClient({
3041
+ /** The admin client for the command's site (`--site`, else the token's site, else the primary). */
3042
+ const clientFor = (options) => createClient({
2947
3043
  baseUrl: options.baseUrl,
2948
- token: options.token
2949
- }).admin.schema;
2950
- /** Sends the local files with their lock-file bases; the server decides per definition (three-way). */
3044
+ token: options.token,
3045
+ ...options.site ? { site: options.site } : {}
3046
+ });
3047
+ /** The schema sync endpoints (`export`, `apply`, `change`) of @shapio/client. */
3048
+ const apiFor = (options) => clientFor(options).admin.schema;
3049
+ /** The key of the site the command works on, as the instance resolved it (an unnamed site is the default). */
3050
+ const resolveSiteKey = async (options) => {
3051
+ if (options.site) return options.site;
3052
+ const { site } = await apiFor(options).export({ scope: "network" });
3053
+ if (!site) throw new Error("The instance did not say which site this token works on; pass --site <key>");
3054
+ return site.key;
3055
+ };
3056
+ /**
3057
+ * The local tree narrowed to one site (shared files and `sites/<key>/`), or undefined (after explaining)
3058
+ * when the tree was pulled for other sites only.
3059
+ */
3060
+ const readSiteState = async (options, io) => {
3061
+ const siteKey = await resolveSiteKey(options);
3062
+ const state = await readLocalState(options);
3063
+ if (!lockCoversSite(state.lock, siteKey)) {
3064
+ 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`);
3065
+ return;
3066
+ }
3067
+ return {
3068
+ ...state,
3069
+ siteKey,
3070
+ sent: filesForSite(state.files, siteKey)
3071
+ };
3072
+ };
3073
+ /** Sends the site's files with their lock-file bases; the server decides per definition (three-way). */
2951
3074
  const sendApply = async (options, state, dryRun, io) => {
2952
3075
  try {
2953
3076
  return await apiFor(options).apply({
2954
- definitions: state.files.map((file) => file.raw),
2955
- base: state.lock,
3077
+ definitions: state.sent.map((file) => file.raw),
3078
+ scopes: scopesOf(state.sent),
3079
+ base: baseForSite(state.lock, state.siteKey),
2956
3080
  prune: options.prune,
2957
3081
  dryRun,
2958
3082
  acknowledgeBreaking: options.allowBreaking,
@@ -2960,7 +3084,7 @@ const sendApply = async (options, state, dryRun, io) => {
2960
3084
  });
2961
3085
  } catch (error) {
2962
3086
  if (error instanceof ShapioApiError) {
2963
- io.stderr(formatApiError(error, state.files.map((file) => file.path), state.names));
3087
+ io.stderr(formatApiError(error, state.sent.map((file) => displayPath(file.path)), state.names));
2964
3088
  return;
2965
3089
  }
2966
3090
  throw error;
@@ -3048,11 +3172,12 @@ const checkPlanDirectory = async (dir, force) => {
3048
3172
  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
3173
  if (!force) throw new Error(`${dir} already holds a plan: pass --force to replace it (new IDs), or use another directory`);
3050
3174
  };
3051
- const plannedMap = (source, info, ids) => ({
3175
+ const plannedMap = (source, info, ids, site) => ({
3052
3176
  format: IMPORT_MAP_FORMAT,
3053
3177
  formatVersion: 1,
3054
3178
  source: info,
3055
3179
  plannedAt: (/* @__PURE__ */ new Date()).toISOString(),
3180
+ site: site ?? null,
3056
3181
  definitions: ids,
3057
3182
  media: Object.fromEntries(source.media.map((media) => [media.sourceId, {
3058
3183
  filename: media.filename,
@@ -3070,9 +3195,11 @@ const plannedMap = (source, info, ids) => ({
3070
3195
  /**
3071
3196
  * `--plan`: writes one pull-format schema file per planned definition under `<dir>/schema`, an empty lock file
3072
3197
  * 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.
3198
+ * `import-map.json`. Nothing is sent to an instance. With `site` (`--site` / SHAPIO_SITE) the files go under
3199
+ * `schema/sites/<site>/`, so `schema apply --site <site>` creates them on that site; without one they go in
3200
+ * the shared folders and are created shared with all sites.
3074
3201
  */
3075
- const writePlan = async (dir, source, info, { force = false } = {}) => {
3202
+ const writePlan = async (dir, source, info, { force = false, site } = {}) => {
3076
3203
  await checkPlanDirectory(dir, force);
3077
3204
  const ids = assignIds(source.definitions);
3078
3205
  const definitions = buildDefinitions(source, ids);
@@ -3081,9 +3208,9 @@ const writePlan = async (dir, source, info, { force = false } = {}) => {
3081
3208
  recursive: true,
3082
3209
  force: true
3083
3210
  });
3084
- for (const definition of definitions) await writeDefinitionFile(schemaDir, definition);
3211
+ for (const definition of definitions) await writeDefinitionFile(schemaDir, definition, site ?? null);
3085
3212
  await writeLockFile(join(dir, PLAN_LOCK_FILE), EMPTY_LOCK);
3086
- const map = plannedMap(source, info, ids);
3213
+ const map = plannedMap(source, info, ids, site);
3087
3214
  await writeImportMap(dir, map);
3088
3215
  return {
3089
3216
  definitions,
@@ -3198,17 +3325,27 @@ var ImportRefusedError = class extends Error {
3198
3325
  this.name = "ImportRefusedError";
3199
3326
  }
3200
3327
  };
3328
+ /**
3329
+ * The site `--map` writes to: `--site` (or SHAPIO_SITE), else the site the plan was made for. A requested site
3330
+ * that disagrees with the planned one is refused: the planned models exist on that site only.
3331
+ */
3332
+ const resolveMapSite = (map, requested) => {
3333
+ const planned = map.site ?? void 0;
3334
+ 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}`);
3335
+ return requested ?? planned;
3336
+ };
3201
3337
  const messageOf = (error) => error instanceof Error ? error.message : String(error);
3202
- const checkSourceAndSite = async (map, options) => {
3338
+ const checkSourceAndSite = async (map, site) => {
3203
3339
  if (await sha256File(map.source.path).catch((error) => {
3204
3340
  throw new ImportRefusedError(`The source ${map.source.path} cannot be read: ${messageOf(error)}`);
3205
3341
  }) !== 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) !== (options.site ?? null)) throw new ImportRefusedError(`This import was started on site ${map.state.site ?? "(default)"}: re-run it with the same --site`);
3342
+ 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
3343
  };
3208
- const loadLiveSchema = async (client, map, dir) => {
3344
+ /** The target site's view (the client names the site): its own definitions and the shared ones. */
3345
+ const loadLiveSchema = async (client, map, dir, site) => {
3209
3346
  const exported = await client.admin.schema.export();
3210
3347
  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)}`);
3348
+ 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
3349
  return live;
3213
3350
  };
3214
3351
  const checkLocales = async (client, source) => {
@@ -3362,17 +3499,18 @@ const printSummary = (run, source, changeSetFailures) => {
3362
3499
  };
3363
3500
  const runMap = async (options, io) => {
3364
3501
  const map = await readImportMap(options.dir);
3365
- await checkSourceAndSite(map, options);
3502
+ const site = resolveMapSite(map, options.site);
3503
+ await checkSourceAndSite(map, site);
3366
3504
  const client = createClient({
3367
3505
  baseUrl: options.baseUrl,
3368
3506
  token: options.token,
3369
3507
  fetch: createRateLimitedFetch(options.fetch),
3370
- ...options.site ? { site: options.site } : {}
3508
+ ...site ? { site } : {}
3371
3509
  });
3372
- const live = await loadLiveSchema(client, map, options.dir);
3510
+ const live = await loadLiveSchema(client, map, options.dir, site);
3373
3511
  const source = await options.load(map);
3374
3512
  await checkLocales(client, source);
3375
- map.state.site = options.site ?? null;
3513
+ map.state.site = site ?? null;
3376
3514
  map.state.baseUrl = options.baseUrl;
3377
3515
  const run = {
3378
3516
  options,
@@ -3396,7 +3534,7 @@ const display = (path) => {
3396
3534
  return !shown ? "." : shown.startsWith("..") || isAbsolute(shown) ? path : shown;
3397
3535
  };
3398
3536
  /** What `--plan` wrote and what to do next. */
3399
- const formatPlanSummary = (source, definitions, dir, command) => {
3537
+ const formatPlanSummary = (source, definitions, dir, command, site) => {
3400
3538
  const keyOf = new Map(source.definitions.map((definition) => [definition.apiKey, definition.key]));
3401
3539
  const lines = [`Planned ${definitions.length} definition(s) in ${display(`${dir}/${PLAN_SCHEMA_DIR}`)}:`];
3402
3540
  for (const definition of definitions) {
@@ -3407,7 +3545,9 @@ const formatPlanSummary = (source, definitions, dir, command) => {
3407
3545
  lines.push(`Media: ${source.media.length} file(s) to import.`);
3408
3546
  lines.push("Every model has draft and publish on: nothing goes live until you ship the change set(s) the import opens.");
3409
3547
  lines.push(...source.notes);
3410
- 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}`)}`, ` 3. shapio import ${command} --map ${display(dir)}`);
3548
+ 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.");
3549
+ const siteFlag = site ? ` --site ${site}` : "";
3550
+ 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
3551
  return `${lines.join("\n")}\n`;
3412
3552
  };
3413
3553
  const parse = (args, spec) => {
@@ -3436,24 +3576,30 @@ const parse = (args, spec) => {
3436
3576
  file: positionals[0]
3437
3577
  };
3438
3578
  };
3579
+ /** `--site`, else SHAPIO_SITE: the site the plan's schema belongs to and the map step writes to. */
3580
+ const siteOf = (values, io) => (values.site ?? io.env.SHAPIO_SITE)?.trim() || void 0;
3439
3581
  const runPlan = async (spec, file, values, io) => {
3440
3582
  const dir = resolve(String(values.plan));
3441
3583
  const path = resolve(file);
3442
3584
  await checkPlanDirectory(dir, values.force === true);
3443
3585
  const source = await spec.plan(path, dir, values);
3586
+ const site = siteOf(values, io);
3444
3587
  const { definitions } = await writePlan(dir, source, {
3445
3588
  kind: source.kind,
3446
3589
  path,
3447
3590
  sha256: await sha256File(path)
3448
- }, { force: values.force === true });
3449
- io.stdout(formatPlanSummary(source, definitions, dir, spec.name));
3591
+ }, {
3592
+ force: values.force === true,
3593
+ site
3594
+ });
3595
+ io.stdout(formatPlanSummary(source, definitions, dir, spec.name, site));
3450
3596
  return 0;
3451
3597
  };
3452
3598
  const runMapStep = (spec, values, io) => {
3453
3599
  const token = values.token ?? io.env.SHAPIO_TOKEN;
3454
3600
  if (!token) throw new UsageError$1("An admin API token is required: pass --token or set SHAPIO_TOKEN");
3455
3601
  const dir = resolve(String(values.map));
3456
- const site = values.site ?? io.env.SHAPIO_SITE;
3602
+ const site = siteOf(values, io);
3457
3603
  return runMap({
3458
3604
  dir,
3459
3605
  baseUrl: values.url ?? io.env.SHAPIO_URL ?? "http://localhost:4300",
@@ -3492,7 +3638,7 @@ const importStrapiCommand = createImporterCommand({
3492
3638
  name: "strapi",
3493
3639
  label: "Strapi",
3494
3640
  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: it uploads the\n media, creates every document as a draft and opens change sets with the published ones. Strapi 5 only.",
3641
+ 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
3642
  options: ["key"],
3497
3643
  plan: async (file, dir, values) => {
3498
3644
  await extractStrapiExport(file, sourceDir(dir), typeof values.key === "string" ? values.key : void 0);
@@ -4086,7 +4232,7 @@ const importWordPressCommand = createImporterCommand({
4086
4232
  name: "wordpress",
4087
4233
  label: "WordPress",
4088
4234
  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.",
4235
+ 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
4236
  options: ["media-dir"],
4091
4237
  plan: async (file) => wordpressSource(await readWxr(file)),
4092
4238
  load: async (map, _dir, values) => wordpressSource(await readWxr(map.source.path), mediaDirOf(values))
@@ -4268,13 +4414,28 @@ var UsageError = class extends Error {
4268
4414
  this.name = "UsageError";
4269
4415
  }
4270
4416
  };
4271
- /** Shared flags of `shapio schema pull|diff|apply`. URL and token also come from SHAPIO_URL / SHAPIO_TOKEN. */
4417
+ const CONNECTION_FLAGS = {
4418
+ url: { type: "string" },
4419
+ token: { type: "string" },
4420
+ site: { type: "string" }
4421
+ };
4422
+ /** URL, token and site from the flags, else SHAPIO_URL / SHAPIO_TOKEN / SHAPIO_SITE. */
4423
+ const resolveConnection = (values, io) => {
4424
+ const token = values.token ?? io.env.SHAPIO_TOKEN;
4425
+ if (!token) throw new UsageError("An admin API token is required: pass --token or set SHAPIO_TOKEN");
4426
+ const site = (values.site ?? io.env.SHAPIO_SITE)?.trim() || void 0;
4427
+ return {
4428
+ baseUrl: values.url ?? io.env.SHAPIO_URL ?? "http://localhost:4300",
4429
+ token,
4430
+ ...site ? { site } : {}
4431
+ };
4432
+ };
4433
+ /** Shared flags of `shapio schema pull|diff|apply`. */
4272
4434
  const parseSchemaOptions = (args, io) => {
4273
4435
  const { values } = parseArgs({
4274
4436
  args: [...args],
4275
4437
  options: {
4276
- url: { type: "string" },
4277
- token: { type: "string" },
4438
+ ...CONNECTION_FLAGS,
4278
4439
  dir: { type: "string" },
4279
4440
  lock: { type: "string" },
4280
4441
  prune: {
@@ -4301,13 +4462,11 @@ const parseSchemaOptions = (args, io) => {
4301
4462
  },
4302
4463
  allowPositionals: false
4303
4464
  });
4304
- const token = values.token ?? io.env.SHAPIO_TOKEN;
4305
- if (!token) throw new UsageError("An admin API token is required: pass --token or set SHAPIO_TOKEN");
4465
+ const connection = resolveConnection(values, io);
4306
4466
  const waitTimeoutSeconds = Number(values["wait-timeout"] ?? 600);
4307
4467
  if (!Number.isFinite(waitTimeoutSeconds) || waitTimeoutSeconds <= 0) throw new UsageError("--wait-timeout must be a positive number of seconds");
4308
4468
  return {
4309
- baseUrl: values.url ?? io.env.SHAPIO_URL ?? "http://localhost:4300",
4310
- token,
4469
+ ...connection,
4311
4470
  dir: resolve(values.dir ?? "schema"),
4312
4471
  lockPath: resolve(values.lock ?? ".shapio/schema-lock.json"),
4313
4472
  prune: values.prune,
@@ -4318,7 +4477,7 @@ const parseSchemaOptions = (args, io) => {
4318
4477
  waitTimeoutMs: waitTimeoutSeconds * 1e3
4319
4478
  };
4320
4479
  };
4321
- const COMMON_USAGE = "[--url <origin>] [--token <admin token>] [--dir schema] [--lock .shapio/schema-lock.json]";
4480
+ const COMMON_USAGE = `[--url <origin>] [--token <admin token>] [--site <key>] [--dir schema] [--lock .shapio/schema-lock.json]`;
4322
4481
  const POLL_INTERVAL_MS = 500;
4323
4482
  /** Waits for planned changes (prerequisite jobs) to activate or fail. Returns the failed ones. */
4324
4483
  const waitForChanges = async (options, items, io) => {
@@ -4350,16 +4509,18 @@ const waitForChanges = async (options, items, io) => {
4350
4509
  };
4351
4510
  /**
4352
4511
  * 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 unchanged locally keep
4354
- * their old base, so a later apply still treats them as untouched.
4512
+ * (e.g. with server-assigned field IDs), in their scope's folder. Definitions skipped because they were
4513
+ * unchanged locally keep their old base, so a later apply still treats them as untouched. Other sites'
4514
+ * entries are kept as they are.
4355
4515
  */
4356
4516
  const updateLocalState = async (options, state, response) => {
4357
4517
  const exported = await apiFor(options).export();
4358
4518
  const remote = new Map(exported.definitions.map((entry) => [entry.definition.id, entry]));
4519
+ const upgraded = upgradeLockFile(state.lock);
4359
4520
  const lock = {
4360
- ...state.lock,
4521
+ ...upgraded,
4361
4522
  schemaVersion: exported.schemaVersion,
4362
- definitions: { ...state.lock.definitions }
4523
+ definitions: { ...upgraded.definitions }
4363
4524
  };
4364
4525
  for (const item of response.results) {
4365
4526
  if (!(item.outcome === "activated" || item.decision.action === "skip" && item.decision.reason === "alreadyApplied")) continue;
@@ -4368,97 +4529,152 @@ const updateLocalState = async (options, state, response) => {
4368
4529
  delete lock.definitions[item.definitionId];
4369
4530
  continue;
4370
4531
  }
4371
- const previous = state.files.find((file) => file.id === item.definitionId);
4372
- const path = await writeDefinitionFile(options.dir, entry.definition);
4532
+ const previous = state.sent.find((file) => file.id === item.definitionId);
4533
+ const path = await writeDefinitionFile(options.dir, entry.definition, entry.site);
4373
4534
  if (previous && previous.path !== path) await removeFile(previous.path);
4374
4535
  lock.definitions[item.definitionId] = {
4375
4536
  kind: entry.definition.kind,
4376
4537
  apiKey: entry.definition.apiKey,
4377
4538
  version: entry.version,
4378
- hash: entry.hash
4539
+ hash: entry.hash,
4540
+ site: entry.site
4379
4541
  };
4380
4542
  }
4381
- await writeLockFile(options.lockPath, lock);
4543
+ await writeLockFile(options.lockPath, coverSiteIfHeld(lock, state.siteKey));
4544
+ };
4545
+ /**
4546
+ * `shapio schema apply`: applies local schema files to a running instance live, through the same planner as
4547
+ * the admin UI. Three-way per definition against the lock file; refuses (and changes nothing) on conflicts.
4548
+ * It sends the shared files and the site's own (`sites/<key>/`), never another site's.
4549
+ */
4550
+ const schemaApplyCommand = {
4551
+ summary: "Apply local schema files to the instance live (three-way, per model; refuses on conflicts)",
4552
+ usage: `shapio schema apply ${COMMON_USAGE} [--prune] [--allow-breaking] [--allow-destructive] [--no-wait] [--wait-timeout <s>]`,
4553
+ run: async (args, io) => {
4554
+ const options = parseSchemaOptions(args, io);
4555
+ const state = await readSiteState(options, io);
4556
+ if (!state) return 1;
4557
+ const response = await sendApply(options, state, false, io);
4558
+ if (!response) return 1;
4559
+ printResults(response, state, io, false);
4560
+ const pending = response.results.filter((item) => item.outcome === "pending");
4561
+ const failed = options.wait ? await waitForChanges(options, pending, io) : [];
4562
+ await updateLocalState(options, state, response);
4563
+ const applied = response.results.filter((item) => item.outcome === "activated").length;
4564
+ io.stdout(`Applied ${applied} definition(s) at schema version ${response.schemaVersion}.\n`);
4565
+ return failed.length > 0 ? 1 : 0;
4566
+ }
4567
+ };
4568
+ /** `shapio schema diff`: what `apply` would do, per definition, without changing anything. */
4569
+ const schemaDiffCommand = {
4570
+ summary: "Show what `shapio schema apply` would change on the instance (dry run)",
4571
+ usage: `shapio schema diff ${COMMON_USAGE} [--prune]`,
4572
+ run: async (args, io) => {
4573
+ const options = parseSchemaOptions(args, io);
4574
+ const state = await readSiteState(options, io);
4575
+ if (!state) return 1;
4576
+ const response = await sendApply(options, state, true, io);
4577
+ if (!response) return 1;
4578
+ printResults(response, state, io, false);
4579
+ const pending = response.results.filter((item) => [
4580
+ "create",
4581
+ "update",
4582
+ "delete"
4583
+ ].includes(item.decision.action));
4584
+ io.stdout(pending.length === 0 ? "Nothing to apply.\n" : `${pending.length} definition(s) would change.\n`);
4585
+ return 0;
4586
+ }
4587
+ };
4588
+ /**
4589
+ * `shapio schema pull`: writes one site's view of the instance (the shared definitions in `models/` and
4590
+ * `components/`, the site's own under `sites/<key>/`) as canonical JSON, one file per model or component,
4591
+ * and records what was pulled in the lock file. Another site's folder and lock entries are left alone, so one
4592
+ * tree can hold several sites. Refuses to overwrite local edits in the pulled folders unless --force.
4593
+ */
4594
+ const schemaPullCommand = {
4595
+ summary: "Write a site’s models and components (shared and its own) to schema files and the lock file",
4596
+ usage: `shapio schema pull ${COMMON_USAGE} [--force]`,
4597
+ run: async (args, io) => {
4598
+ const options = parseSchemaOptions(args, io);
4599
+ const exported = await apiFor(options).export();
4600
+ if (!exported.site) throw new Error("The instance did not say which site this view is; pass --site <key>");
4601
+ const siteKey = exported.site.key;
4602
+ const state = await readLocalState(options);
4603
+ const edited = editedFilesForSite(state.files, state.lock, siteKey);
4604
+ if (edited.length > 0 && !options.force) {
4605
+ 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.
4606
+ `);
4607
+ return 1;
4608
+ }
4609
+ const written = /* @__PURE__ */ new Map();
4610
+ for (const { definition, site } of exported.definitions) written.set(definition.id, await writeDefinitionFile(options.dir, definition, site));
4611
+ for (const file of staleFilesAfterPull(state.files, siteKey, written)) await removeFile(file.path);
4612
+ await writeLockFile(options.lockPath, mergePulledLock(state.lock, {
4613
+ schemaVersion: exported.schemaVersion,
4614
+ siteKey,
4615
+ definitions: exported.definitions
4616
+ }));
4617
+ io.stdout(`Pulled ${exported.definitions.length} definition(s) of site "${siteKey}" at schema version ${exported.schemaVersion} into ${displayPath(options.dir)}\n`);
4618
+ return 0;
4619
+ }
4620
+ };
4621
+ const parseScopeOptions = (args, io) => {
4622
+ const { values, positionals } = parseArgs({
4623
+ args: [...args],
4624
+ options: {
4625
+ ...CONNECTION_FLAGS,
4626
+ shared: {
4627
+ type: "boolean",
4628
+ default: false
4629
+ },
4630
+ version: { type: "string" }
4631
+ },
4632
+ allowPositionals: true
4633
+ });
4634
+ const [apiKey, ...extra] = positionals;
4635
+ if (!apiKey || extra.length > 0) throw new UsageError("Name one model or component by its API ID");
4636
+ 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");
4637
+ const version = values.version === void 0 ? void 0 : Number(values.version);
4638
+ if (version !== void 0 && (!Number.isInteger(version) || version < 1)) throw new UsageError("--version must be a positive integer");
4639
+ return {
4640
+ ...resolveConnection(values, io),
4641
+ apiKey,
4642
+ shared: values.shared,
4643
+ version
4644
+ };
4382
4645
  };
4383
4646
  const SUBCOMMANDS$1 = {
4384
- pull: {
4385
- summary: "Write the instance’s models and components to schema files and the lock file",
4386
- usage: `shapio schema pull ${COMMON_USAGE} [--force]`,
4647
+ pull: schemaPullCommand,
4648
+ diff: schemaDiffCommand,
4649
+ apply: schemaApplyCommand,
4650
+ scope: {
4651
+ summary: "Keep a shared model or component on one site (--site), or share a site’s with all sites (--shared)",
4652
+ usage: "shapio schema scope <apiKey> (--site <key> | --shared [--site <key>]) [--url <origin>] [--token <admin token>] [--version <n>]",
4387
4653
  run: async (args, io) => {
4388
- const options = parseSchemaOptions(args, io);
4389
- const state = await readLocalState(options);
4390
- const edited = state.files.filter((file) => {
4391
- const base = file.id ? state.lock.definitions[file.id] : void 0;
4392
- return !base || file.hash !== base.hash;
4393
- });
4394
- if (edited.length > 0 && !options.force && Object.keys(state.lock.definitions).length > 0) {
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
- `);
4654
+ const options = parseScopeOptions(args, io);
4655
+ const client = clientFor(options);
4656
+ const exported = await client.admin.schema.export();
4657
+ const siteKey = options.site ?? exported.site?.key ?? "(default)";
4658
+ const found = exported.definitions.find(({ definition }) => definition.apiKey === options.apiKey);
4659
+ if (!found) {
4660
+ io.stderr(`Site "${siteKey}" has no model or component with API ID "${options.apiKey}".\n`);
4397
4661
  return 1;
4398
4662
  }
4399
- const exported = await apiFor(options).export();
4400
- const lock = {
4401
- formatVersion: 1,
4402
- schemaVersion: exported.schemaVersion,
4403
- definitions: {}
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`);
4663
+ const outcome = await (found.definition.kind === "component" ? client.admin.components : client.admin.models).changeScope(found.definition.id, {
4664
+ scope: options.shared ? "network" : "site",
4665
+ version: options.version ?? found.version
4666
+ });
4667
+ const where = outcome.scope === "network" ? "shared with all sites" : `on site "${siteKey}" only`;
4668
+ 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
4669
  return 0;
4437
4670
  }
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
4671
  }
4456
4672
  };
4457
4673
  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. */
4674
+ /** `shapio schema <pull|diff|apply|scope>`: git-like schema sync over HTTP with an admin API token. */
4459
4675
  const schemaCommand = {
4460
- summary: "Sync models and components with schema files: pull | diff | apply",
4461
- usage: "shapio schema <pull|diff|apply> [options]",
4676
+ summary: "Sync models and components with schema files: pull | diff | apply | scope",
4677
+ usage: "shapio schema <pull|diff|apply|scope> [options]",
4462
4678
  run: async (args, io) => {
4463
4679
  const [name, ...rest] = args;
4464
4680
  const command = name ? SUBCOMMANDS$1[name] : void 0;
@@ -4473,6 +4689,10 @@ const schemaCommand = {
4473
4689
  io.stderr(`shapio schema ${name}: ${error.message}\n`);
4474
4690
  return 1;
4475
4691
  }
4692
+ if (error instanceof ShapioApiError) {
4693
+ io.stderr(formatApiError(error, [], /* @__PURE__ */ new Map()));
4694
+ return 1;
4695
+ }
4476
4696
  throw error;
4477
4697
  }
4478
4698
  }
@@ -5050,7 +5270,7 @@ const adminCommand = {
5050
5270
  }
5051
5271
  }
5052
5272
  };
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 schema, admins and roles; each has its\n own content, media, tokens and snapshots). Keys are lower case and fixed once created.";
5273
+ 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
5274
  const SITE_KEY_RE = new RegExp(SITE_KEY_PATTERN);
5055
5275
  const runSites = async (subcommand, options, io) => {
5056
5276
  const config = loadConfig();