@shapio/cms 0.4.1 → 0.5.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 (260) hide show
  1. package/dist/admin/assets/{AssetTile-CoNClUYS.js → AssetTile-zOf0K0nN.js} +2 -2
  2. package/dist/admin/assets/{AssetTile-CoNClUYS.js.map → AssetTile-zOf0K0nN.js.map} +1 -1
  3. package/dist/admin/assets/{AssistError-BoejyShH.js → AssistError-D_vY2CO0.js} +2 -2
  4. package/dist/admin/assets/{AssistError-BoejyShH.js.map → AssistError-D_vY2CO0.js.map} +1 -1
  5. package/dist/admin/assets/{BlockMenuItems-B2Jugisa.js → BlockMenuItems-BEnSpClj.js} +2 -2
  6. package/dist/admin/assets/{BlockMenuItems-B2Jugisa.js.map → BlockMenuItems-BEnSpClj.js.map} +1 -1
  7. package/dist/admin/assets/{Body-D3GwLGaB.js → Body-B_IVN5wp.js} +2 -2
  8. package/dist/admin/assets/{Body-D3GwLGaB.js.map → Body-B_IVN5wp.js.map} +1 -1
  9. package/dist/admin/assets/{Builder-CmT5-8hI.js → Builder-CnEtRudv.js} +2 -2
  10. package/dist/admin/assets/{Builder-CmT5-8hI.js.map → Builder-CnEtRudv.js.map} +1 -1
  11. package/dist/admin/assets/{ConfirmDialog-HS8Gc2t3.js → ConfirmDialog-BW-l3Whg.js} +2 -2
  12. package/dist/admin/assets/{ConfirmDialog-HS8Gc2t3.js.map → ConfirmDialog-BW-l3Whg.js.map} +1 -1
  13. package/dist/admin/assets/{CopyButton-DS-bAA23.js → CopyButton-CyoUMStc.js} +2 -2
  14. package/dist/admin/assets/{CopyButton-DS-bAA23.js.map → CopyButton-CyoUMStc.js.map} +1 -1
  15. package/dist/admin/assets/{CursorPager-C6PyD3wE.js → CursorPager-BW2Dl_hn.js} +2 -2
  16. package/dist/admin/assets/{CursorPager-C6PyD3wE.js.map → CursorPager-BW2Dl_hn.js.map} +1 -1
  17. package/dist/admin/assets/{ExternalLink-KdO72pTj.js → ExternalLink-BCI0q9ib.js} +2 -2
  18. package/dist/admin/assets/{ExternalLink-KdO72pTj.js.map → ExternalLink-BCI0q9ib.js.map} +1 -1
  19. package/dist/admin/assets/{FocalPoint-C62fRhmM.js → FocalPoint-CxicvlJO.js} +2 -2
  20. package/dist/admin/assets/{FocalPoint-C62fRhmM.js.map → FocalPoint-CxicvlJO.js.map} +1 -1
  21. package/dist/admin/assets/{Form-CLEr5Csd.js → Form-DB03drS9.js} +2 -2
  22. package/dist/admin/assets/{Form-CLEr5Csd.js.map → Form-DB03drS9.js.map} +1 -1
  23. package/dist/admin/assets/{FormCheckboxGroup-BrXY6qBg.js → FormCheckboxGroup-JP1xzReQ.js} +2 -2
  24. package/dist/admin/assets/{FormCheckboxGroup-BrXY6qBg.js.map → FormCheckboxGroup-JP1xzReQ.js.map} +1 -1
  25. package/dist/admin/assets/{FormRadioGroup-BjE1Jhar.js → FormRadioGroup-BvSC8SRm.js} +2 -2
  26. package/dist/admin/assets/{FormRadioGroup-BjE1Jhar.js.map → FormRadioGroup-BvSC8SRm.js.map} +1 -1
  27. package/dist/admin/assets/{FormSelectField-g4zrUlhv.js → FormSelectField-BDIVhZh6.js} +2 -2
  28. package/dist/admin/assets/{FormSelectField-g4zrUlhv.js.map → FormSelectField-BDIVhZh6.js.map} +1 -1
  29. package/dist/admin/assets/{FormSwitchField-gciThNGV.js → FormSwitchField-OM4F8ln4.js} +2 -2
  30. package/dist/admin/assets/{FormSwitchField-gciThNGV.js.map → FormSwitchField-OM4F8ln4.js.map} +1 -1
  31. package/dist/admin/assets/{FormTextareaField-tsS-UXym.js → FormTextareaField-Bw7EkC4H.js} +2 -2
  32. package/dist/admin/assets/{FormTextareaField-tsS-UXym.js.map → FormTextareaField-Bw7EkC4H.js.map} +1 -1
  33. package/dist/admin/assets/{HintedLabel-CVe-PIyv.js → HintedLabel-BGidukgy.js} +2 -2
  34. package/dist/admin/assets/{HintedLabel-CVe-PIyv.js.map → HintedLabel-BGidukgy.js.map} +1 -1
  35. package/dist/admin/assets/{InfoHint-DtaHU1Cg.js → InfoHint-D3uwei-E.js} +2 -2
  36. package/dist/admin/assets/{InfoHint-DtaHU1Cg.js.map → InfoHint-D3uwei-E.js.map} +1 -1
  37. package/dist/admin/assets/{InsertMenuItem-DlzaD9zr.js → InsertMenuItem-DAihGvbw.js} +2 -2
  38. package/dist/admin/assets/{InsertMenuItem-DlzaD9zr.js.map → InsertMenuItem-DAihGvbw.js.map} +1 -1
  39. package/dist/admin/assets/{LinkTabs-stM3j4aH.js → LinkTabs-DBXbfzME.js} +2 -2
  40. package/dist/admin/assets/{LinkTabs-stM3j4aH.js.map → LinkTabs-DBXbfzME.js.map} +1 -1
  41. package/dist/admin/assets/{Match-ErAxnNAY.js → Match-CgfZTzqs.js} +2 -2
  42. package/dist/admin/assets/{Match-ErAxnNAY.js.map → Match-CgfZTzqs.js.map} +1 -1
  43. package/dist/admin/assets/{Panel-ClY9b33D.js → Panel-DDky5_x9.js} +2 -2
  44. package/dist/admin/assets/{Panel-ClY9b33D.js.map → Panel-DDky5_x9.js.map} +1 -1
  45. package/dist/admin/assets/{QueryView-C630REML.js → QueryView-C2d392ly.js} +2 -2
  46. package/dist/admin/assets/{QueryView-C630REML.js.map → QueryView-C2d392ly.js.map} +1 -1
  47. package/dist/admin/assets/{RadioTile-CD4mel7U.js → RadioTile-CcP78eb_.js} +2 -2
  48. package/dist/admin/assets/{RadioTile-CD4mel7U.js.map → RadioTile-CcP78eb_.js.map} +1 -1
  49. package/dist/admin/assets/{RichTextField-CxeVX6C-.js → RichTextField-CeztaFET.js} +2 -2
  50. package/dist/admin/assets/{RichTextField-CxeVX6C-.js.map → RichTextField-CeztaFET.js.map} +1 -1
  51. package/dist/admin/assets/{RowTitle-B-7RptyN.js → RowTitle-mkKp4yfl.js} +2 -2
  52. package/dist/admin/assets/{RowTitle-B-7RptyN.js.map → RowTitle-mkKp4yfl.js.map} +1 -1
  53. package/dist/admin/assets/{SecretReveal-Dhw_dYxe.js → SecretReveal-mYCzxF96.js} +2 -2
  54. package/dist/admin/assets/{SecretReveal-Dhw_dYxe.js.map → SecretReveal-mYCzxF96.js.map} +1 -1
  55. package/dist/admin/assets/{SharedGlyph-97RaO6Bh.js → SharedGlyph-CuDT7OUU.js} +2 -2
  56. package/dist/admin/assets/{SharedGlyph-97RaO6Bh.js.map → SharedGlyph-CuDT7OUU.js.map} +1 -1
  57. package/dist/admin/assets/{StepList--0Fiqsq-.js → StepList-BDO0Q3Wc.js} +2 -2
  58. package/dist/admin/assets/{StepList--0Fiqsq-.js.map → StepList-BDO0Q3Wc.js.map} +1 -1
  59. package/dist/admin/assets/Structure-CL_0hVci.js +2 -0
  60. package/dist/admin/assets/{Structure-CoO3iah3.js.map → Structure-CL_0hVci.js.map} +1 -1
  61. package/dist/admin/assets/{SubNavLink-YjjaDN40.js → SubNavLink-C4eSybWd.js} +2 -2
  62. package/dist/admin/assets/{SubNavLink-YjjaDN40.js.map → SubNavLink-C4eSybWd.js.map} +1 -1
  63. package/dist/admin/assets/{SuggestAltButton-Dlyxoypu.js → SuggestAltButton-CxYlhwyd.js} +2 -2
  64. package/dist/admin/assets/{SuggestAltButton-Dlyxoypu.js.map → SuggestAltButton-CxYlhwyd.js.map} +1 -1
  65. package/dist/admin/assets/{TextLink-DIqr5zkJ.js → TextLink-DzNaAft6.js} +2 -2
  66. package/dist/admin/assets/{TextLink-DIqr5zkJ.js.map → TextLink-DzNaAft6.js.map} +1 -1
  67. package/dist/admin/assets/{Thumbnail-BUYOr4XJ.js → Thumbnail-DSPArZ9h.js} +2 -2
  68. package/dist/admin/assets/{Thumbnail-BUYOr4XJ.js.map → Thumbnail-DSPArZ9h.js.map} +1 -1
  69. package/dist/admin/assets/{UnsavedChangesGuard-BfzHgUkF.js → UnsavedChangesGuard-BPOCBD5T.js} +2 -2
  70. package/dist/admin/assets/{UnsavedChangesGuard-BfzHgUkF.js.map → UnsavedChangesGuard-BPOCBD5T.js.map} +1 -1
  71. package/dist/admin/assets/{UserAvatar-DNOWhSWS.js → UserAvatar-BHNWHDVb.js} +2 -2
  72. package/dist/admin/assets/{UserAvatar-DNOWhSWS.js.map → UserAvatar-BHNWHDVb.js.map} +1 -1
  73. package/dist/admin/assets/{auth-YVfK3SxF.js → auth-BzCgJtgr.js} +2 -2
  74. package/dist/admin/assets/{auth-YVfK3SxF.js.map → auth-BzCgJtgr.js.map} +1 -1
  75. package/dist/admin/assets/{badge-lJ4Ih_Zn.js → badge-DBoaS0nQ.js} +2 -2
  76. package/dist/admin/assets/{badge-lJ4Ih_Zn.js.map → badge-DBoaS0nQ.js.map} +1 -1
  77. package/dist/admin/assets/{boxes-DRvd25_u.js → boxes-DhN0ctLo.js} +2 -2
  78. package/dist/admin/assets/{boxes-DRvd25_u.js.map → boxes-DhN0ctLo.js.map} +1 -1
  79. package/dist/admin/assets/{calendar-clock-VENf4s1R.js → calendar-clock-BEA3tG17.js} +2 -2
  80. package/dist/admin/assets/{calendar-clock-VENf4s1R.js.map → calendar-clock-BEA3tG17.js.map} +1 -1
  81. package/dist/admin/assets/{changeSets-JmSyu3tu.js → changeSets-C54Ku-X9.js} +2 -2
  82. package/dist/admin/assets/{changeSets-JmSyu3tu.js.map → changeSets-C54Ku-X9.js.map} +1 -1
  83. package/dist/admin/assets/chevron-left-BeJdUVAX.js +2 -0
  84. package/dist/admin/assets/{chevron-left-D2DnQyeV.js.map → chevron-left-BeJdUVAX.js.map} +1 -1
  85. package/dist/admin/assets/{circle-check-jmRQz1Jq.js → circle-check-ktACw37r.js} +2 -2
  86. package/dist/admin/assets/{circle-check-jmRQz1Jq.js.map → circle-check-ktACw37r.js.map} +1 -1
  87. package/dist/admin/assets/{clock-DrSTMGjp.js → clock-BKNNASKp.js} +2 -2
  88. package/dist/admin/assets/{clock-DrSTMGjp.js.map → clock-BKNNASKp.js.map} +1 -1
  89. package/dist/admin/assets/cn-h5ORT3LK.js +2 -0
  90. package/dist/admin/assets/cn-h5ORT3LK.js.map +1 -0
  91. package/dist/admin/assets/coerce-DDQ1vGLx.js +2 -0
  92. package/dist/admin/assets/{coerce-B4MJTGg1.js.map → coerce-DDQ1vGLx.js.map} +1 -1
  93. package/dist/admin/assets/{constants-ahk0YSYl.js → constants-DH7oqfO_.js} +2 -2
  94. package/dist/admin/assets/{constants-ahk0YSYl.js.map → constants-DH7oqfO_.js.map} +1 -1
  95. package/dist/admin/assets/{content-DcvNzKGe.js → content-BptqQO8c.js} +3 -3
  96. package/dist/admin/assets/{content-DcvNzKGe.js.map → content-BptqQO8c.js.map} +1 -1
  97. package/dist/admin/assets/{contentCounts-DzIvdeXR.js → contentCounts-BpGlmyeZ.js} +2 -2
  98. package/dist/admin/assets/{contentCounts-DzIvdeXR.js.map → contentCounts-BpGlmyeZ.js.map} +1 -1
  99. package/dist/admin/assets/{contentTypes-YAd-AzOF.js → contentTypes-B_mAlIcH.js} +2 -2
  100. package/dist/admin/assets/{contentTypes-YAd-AzOF.js.map → contentTypes-B_mAlIcH.js.map} +1 -1
  101. package/dist/admin/assets/{context-4Jl20mYc.js → context-DyyMW7Md.js} +2 -2
  102. package/dist/admin/assets/{context-4Jl20mYc.js.map → context-DyyMW7Md.js.map} +1 -1
  103. package/dist/admin/assets/{copy-t6kAFw-O.js → copy-DjzlFY9u.js} +2 -2
  104. package/dist/admin/assets/{copy-t6kAFw-O.js.map → copy-DjzlFY9u.js.map} +1 -1
  105. package/dist/admin/assets/{dateTimeLocal-IDUOKulz.js → dateTimeLocal-DFXY779D.js} +2 -2
  106. package/dist/admin/assets/{dateTimeLocal-IDUOKulz.js.map → dateTimeLocal-DFXY779D.js.map} +1 -1
  107. package/dist/admin/assets/{develop-DYwiTd6R.js → develop-DJap_z-1.js} +2 -2
  108. package/dist/admin/assets/{develop-DYwiTd6R.js.map → develop-DJap_z-1.js.map} +1 -1
  109. package/dist/admin/assets/{dropdown-menu-DiYSm-g3.js → dropdown-menu-DGSMTf23.js} +2 -2
  110. package/dist/admin/assets/{dropdown-menu-DiYSm-g3.js.map → dropdown-menu-DGSMTf23.js.map} +1 -1
  111. package/dist/admin/assets/{ellipsis-DEHUaCkn.js → ellipsis-BMP-j3yO.js} +2 -2
  112. package/dist/admin/assets/{ellipsis-DEHUaCkn.js.map → ellipsis-BMP-j3yO.js.map} +1 -1
  113. package/dist/admin/assets/{external-link-B8UFREQ_.js → external-link-DzOkt97S.js} +2 -2
  114. package/dist/admin/assets/{external-link-B8UFREQ_.js.map → external-link-DzOkt97S.js.map} +1 -1
  115. package/dist/admin/assets/{fileRoute-CVQpW8cj.js → fileRoute-CG0Dufvt.js} +2 -2
  116. package/dist/admin/assets/{fileRoute-CVQpW8cj.js.map → fileRoute-CG0Dufvt.js.map} +1 -1
  117. package/dist/admin/assets/{folder-plus-iDg1hhew.js → folder-plus-C-LhI4a2.js} +2 -2
  118. package/dist/admin/assets/{folder-plus-iDg1hhew.js.map → folder-plus-C-LhI4a2.js.map} +1 -1
  119. package/dist/admin/assets/{followPlural-BdqfEe48.js → followPlural-D-8kzFOe.js} +2 -2
  120. package/dist/admin/assets/{followPlural-BdqfEe48.js.map → followPlural-D-8kzFOe.js.map} +1 -1
  121. package/dist/admin/assets/{globe-TooNSp9V.js → globe-DOoGBy23.js} +2 -2
  122. package/dist/admin/assets/{globe-TooNSp9V.js.map → globe-DOoGBy23.js.map} +1 -1
  123. package/dist/admin/assets/{image-CXZXXH58.js → image-D9p_gGE2.js} +2 -2
  124. package/dist/admin/assets/{image-CXZXXH58.js.map → image-D9p_gGE2.js.map} +1 -1
  125. package/dist/admin/assets/{image-off-cxva5G-Y.js → image-off-G0M2Nh1I.js} +2 -2
  126. package/dist/admin/assets/{image-off-cxva5G-Y.js.map → image-off-G0M2Nh1I.js.map} +1 -1
  127. package/dist/admin/assets/{inbox-B8c9Kj_z.js → inbox-CMzYVMZo.js} +2 -2
  128. package/dist/admin/assets/{inbox-B8c9Kj_z.js.map → inbox-CMzYVMZo.js.map} +1 -1
  129. package/dist/admin/assets/{inbox-CnzNfqt9.js → inbox-XaM-nvsM.js} +2 -2
  130. package/dist/admin/assets/{inbox-CnzNfqt9.js.map → inbox-XaM-nvsM.js.map} +1 -1
  131. package/dist/admin/assets/{index-D9KL7arJ.js → index-DHmfMpqz.js} +4 -4
  132. package/dist/admin/assets/{index-D9KL7arJ.js.map → index-DHmfMpqz.js.map} +1 -1
  133. package/dist/admin/assets/{key-round-CFJ61iCd.js → key-round-HBvMrmMo.js} +2 -2
  134. package/dist/admin/assets/{key-round-CFJ61iCd.js.map → key-round-HBvMrmMo.js.map} +1 -1
  135. package/dist/admin/assets/{languages-eh7U4anI.js → languages-BOv0jYhe.js} +2 -2
  136. package/dist/admin/assets/{languages-eh7U4anI.js.map → languages-BOv0jYhe.js.map} +1 -1
  137. package/dist/admin/assets/{layers-mGfmMsKE.js → layers-CkZyCaeb.js} +2 -2
  138. package/dist/admin/assets/{layers-mGfmMsKE.js.map → layers-CkZyCaeb.js.map} +1 -1
  139. package/dist/admin/assets/{link-2-off-DbZzRN_w.js → link-2-off-UUkSmPIL.js} +2 -2
  140. package/dist/admin/assets/{link-2-off-DbZzRN_w.js.map → link-2-off-UUkSmPIL.js.map} +1 -1
  141. package/dist/admin/assets/{link-b2pFaN-A.js → link-Bvqcg7db.js} +2 -2
  142. package/dist/admin/assets/{link-b2pFaN-A.js.map → link-Bvqcg7db.js.map} +1 -1
  143. package/dist/admin/assets/{locales-oBD0q1pN.js → locales-DbrcdhZi.js} +2 -2
  144. package/dist/admin/assets/{locales-oBD0q1pN.js.map → locales-DbrcdhZi.js.map} +1 -1
  145. package/dist/admin/assets/{media-Cs9LCQcz.js → media-CO0HSS3b.js} +2 -2
  146. package/dist/admin/assets/{media-Cs9LCQcz.js.map → media-CO0HSS3b.js.map} +1 -1
  147. package/dist/admin/assets/{media-5ks_0Rre.js → media-ugEyLgwN.js} +2 -2
  148. package/dist/admin/assets/{media-5ks_0Rre.js.map → media-ugEyLgwN.js.map} +1 -1
  149. package/dist/admin/assets/{network-DS8MW3AZ.js → network-BfDaUb7D.js} +2 -2
  150. package/dist/admin/assets/{network-DS8MW3AZ.js.map → network-BfDaUb7D.js.map} +1 -1
  151. package/dist/admin/assets/{pencil-C7eD5mlZ.js → pencil-BLNGNJmL.js} +2 -2
  152. package/dist/admin/assets/{pencil-C7eD5mlZ.js.map → pencil-BLNGNJmL.js.map} +1 -1
  153. package/dist/admin/assets/{plus-BDZHQBlU.js → plus-BXMBYPoB.js} +2 -2
  154. package/dist/admin/assets/{plus-BDZHQBlU.js.map → plus-BXMBYPoB.js.map} +1 -1
  155. package/dist/admin/assets/{preload-helper-CJNJ4S3k.js → preload-helper-DEbJbZwX.js} +2 -2
  156. package/dist/admin/assets/{preload-helper-CJNJ4S3k.js.map → preload-helper-DEbJbZwX.js.map} +1 -1
  157. package/dist/admin/assets/{publishing-C4YQ4XKt.js → publishing-DKrATBaj.js} +2 -2
  158. package/dist/admin/assets/{publishing-C4YQ4XKt.js.map → publishing-DKrATBaj.js.map} +1 -1
  159. package/dist/admin/assets/{radio-group-zOsGAYRP.js → radio-group-BIhT_uEK.js} +2 -2
  160. package/dist/admin/assets/{radio-group-zOsGAYRP.js.map → radio-group-BIhT_uEK.js.map} +1 -1
  161. package/dist/admin/assets/{rotate-ccw-DCWwb4Ga.js → rotate-ccw-DqbffhiC.js} +2 -2
  162. package/dist/admin/assets/{rotate-ccw-DCWwb4Ga.js.map → rotate-ccw-DqbffhiC.js.map} +1 -1
  163. package/dist/admin/assets/{schedules-DXrBSypK.js → schedules-CETl2eLe.js} +2 -2
  164. package/dist/admin/assets/{schedules-DXrBSypK.js.map → schedules-CETl2eLe.js.map} +1 -1
  165. package/dist/admin/assets/{schema-B7L9OJrX.js → schema-CWHuez0q.js} +2 -2
  166. package/dist/admin/assets/{schema-B7L9OJrX.js.map → schema-CWHuez0q.js.map} +1 -1
  167. package/dist/admin/assets/{scroll-text-BLThe4Ki.js → scroll-text-DlcDEi2k.js} +2 -2
  168. package/dist/admin/assets/{scroll-text-BLThe4Ki.js.map → scroll-text-DlcDEi2k.js.map} +1 -1
  169. package/dist/admin/assets/{search-CcJQqtRw.js → search-Dy2dtwyR.js} +2 -2
  170. package/dist/admin/assets/{search-CcJQqtRw.js.map → search-Dy2dtwyR.js.map} +1 -1
  171. package/dist/admin/assets/{searchSchemas-BCRZ1jyL.js → searchSchemas-Cz4P1iku.js} +2 -2
  172. package/dist/admin/assets/{searchSchemas-BCRZ1jyL.js.map → searchSchemas-Cz4P1iku.js.map} +1 -1
  173. package/dist/admin/assets/{sections-v5Flaor9.js → sections-BfG6DTFr.js} +2 -2
  174. package/dist/admin/assets/{sections-v5Flaor9.js.map → sections-BfG6DTFr.js.map} +1 -1
  175. package/dist/admin/assets/{sections-TIN_Nl1b.js → sections-EgXyXN3-.js} +2 -2
  176. package/dist/admin/assets/{sections-TIN_Nl1b.js.map → sections-EgXyXN3-.js.map} +1 -1
  177. package/dist/admin/assets/{select-CpHp3YOH.js → select--mePTdz8.js} +2 -2
  178. package/dist/admin/assets/{select-CpHp3YOH.js.map → select--mePTdz8.js.map} +1 -1
  179. package/dist/admin/assets/{send-CbP0Rc9_.js → send-DVgq5Iwg.js} +2 -2
  180. package/dist/admin/assets/{send-CbP0Rc9_.js.map → send-DVgq5Iwg.js.map} +1 -1
  181. package/dist/admin/assets/{seo-1n9s7YuX.js → seo-Zhb62Ojz.js} +2 -2
  182. package/dist/admin/assets/{seo-1n9s7YuX.js.map → seo-Zhb62Ojz.js.map} +1 -1
  183. package/dist/admin/assets/{settings-CuEO5l07.js → settings-Mscp6tN5.js} +2 -2
  184. package/dist/admin/assets/{settings-CuEO5l07.js.map → settings-Mscp6tN5.js.map} +1 -1
  185. package/dist/admin/assets/{skeleton-DElTumgk.js → skeleton-DJVukcn6.js} +2 -2
  186. package/dist/admin/assets/{skeleton-DElTumgk.js.map → skeleton-DJVukcn6.js.map} +1 -1
  187. package/dist/admin/assets/{snapshots-DkyyrWX3.js → snapshots-BpLhILlt.js} +2 -2
  188. package/dist/admin/assets/{snapshots-DkyyrWX3.js.map → snapshots-BpLhILlt.js.map} +1 -1
  189. package/dist/admin/assets/{sparkles-D88gf3vX.js → sparkles-Dd6S_roX.js} +2 -2
  190. package/dist/admin/assets/{sparkles-D88gf3vX.js.map → sparkles-Dd6S_roX.js.map} +1 -1
  191. package/dist/admin/assets/{stableId-DlNKh3HM.js → stableId-DFVM13gJ.js} +2 -2
  192. package/dist/admin/assets/{stableId-DlNKh3HM.js.map → stableId-DFVM13gJ.js.map} +1 -1
  193. package/dist/admin/assets/{store-jUWyOXDG.js → store-BP-xStwU.js} +2 -2
  194. package/dist/admin/assets/{store-jUWyOXDG.js.map → store-BP-xStwU.js.map} +1 -1
  195. package/dist/admin/assets/{switch-CijNGYkG.js → switch-skVypg5O.js} +2 -2
  196. package/dist/admin/assets/{switch-CijNGYkG.js.map → switch-skVypg5O.js.map} +1 -1
  197. package/dist/admin/assets/{table-DQ5ILlSh.js → table-BMbbRUK9.js} +2 -2
  198. package/dist/admin/assets/{table-DQ5ILlSh.js.map → table-BMbbRUK9.js.map} +1 -1
  199. package/dist/admin/assets/{tabs-CSqGW9Du.js → tabs-C1lgOg1B.js} +3 -3
  200. package/dist/admin/assets/{tabs-CSqGW9Du.js.map → tabs-C1lgOg1B.js.map} +1 -1
  201. package/dist/admin/assets/{textarea-FwSvwmyT.js → textarea-CffBBxoQ.js} +2 -2
  202. package/dist/admin/assets/{textarea-FwSvwmyT.js.map → textarea-CffBBxoQ.js.map} +1 -1
  203. package/dist/admin/assets/{theme-CHHVK8Ps.js → theme-RHTVnR9j.js} +2 -2
  204. package/dist/admin/assets/{theme-CHHVK8Ps.js.map → theme-RHTVnR9j.js.map} +1 -1
  205. package/dist/admin/assets/{titles-CQYc9nDQ.js → titles-DWiPmNvn.js} +2 -2
  206. package/dist/admin/assets/{titles-CQYc9nDQ.js.map → titles-DWiPmNvn.js.map} +1 -1
  207. package/dist/admin/assets/{tokens-C-20gLAT.js → tokens-BqyeN1D-.js} +2 -2
  208. package/dist/admin/assets/{tokens-C-20gLAT.js.map → tokens-BqyeN1D-.js.map} +1 -1
  209. package/dist/admin/assets/{tooltip-BXqFQv9N.js → tooltip-BfSF6EyR.js} +2 -2
  210. package/dist/admin/assets/{tooltip-BXqFQv9N.js.map → tooltip-BfSF6EyR.js.map} +1 -1
  211. package/dist/admin/assets/{undo-2-CLy2zklL.js → undo-2-p4YlNhBp.js} +2 -2
  212. package/dist/admin/assets/{undo-2-CLy2zklL.js.map → undo-2-p4YlNhBp.js.map} +1 -1
  213. package/dist/admin/assets/{useAuditLabels-BJF7M5RY.js → useAuditLabels-DV5gcaaG.js} +2 -2
  214. package/dist/admin/assets/{useAuditLabels-BJF7M5RY.js.map → useAuditLabels-DV5gcaaG.js.map} +1 -1
  215. package/dist/admin/assets/{useConfirmTarget-BUvlrGSW.js → useConfirmTarget-DCvnxNVg.js} +2 -2
  216. package/dist/admin/assets/{useConfirmTarget-BUvlrGSW.js.map → useConfirmTarget-DCvnxNVg.js.map} +1 -1
  217. package/dist/admin/assets/{useContentSchema-Cvtes34f.js → useContentSchema-z6tNif86.js} +2 -2
  218. package/dist/admin/assets/{useContentSchema-Cvtes34f.js.map → useContentSchema-z6tNif86.js.map} +1 -1
  219. package/dist/admin/assets/{useCursorPager-CzB6NDOB.js → useCursorPager-CQGX41_L.js} +2 -2
  220. package/dist/admin/assets/{useCursorPager-CzB6NDOB.js.map → useCursorPager-CQGX41_L.js.map} +1 -1
  221. package/dist/admin/assets/{useDiscardGuard-BAEGkGK9.js → useDiscardGuard-Dhbif7VL.js} +2 -2
  222. package/dist/admin/assets/{useDiscardGuard-BAEGkGK9.js.map → useDiscardGuard-Dhbif7VL.js.map} +1 -1
  223. package/dist/admin/assets/{useMediaPickerDialog-CiN_pias.js → useMediaPickerDialog-D-5uKsMG.js} +2 -2
  224. package/dist/admin/assets/{useMediaPickerDialog-CiN_pias.js.map → useMediaPickerDialog-D-5uKsMG.js.map} +1 -1
  225. package/dist/admin/assets/{useMediaUploader-CapEHBIt.js → useMediaUploader-BtLoimmQ.js} +2 -2
  226. package/dist/admin/assets/{useMediaUploader-CapEHBIt.js.map → useMediaUploader-BtLoimmQ.js.map} +1 -1
  227. package/dist/admin/assets/{usePaletteItems-wpn3ms6o.js → usePaletteItems-D3PZsW0Q.js} +2 -2
  228. package/dist/admin/assets/{usePaletteItems-wpn3ms6o.js.map → usePaletteItems-D3PZsW0Q.js.map} +1 -1
  229. package/dist/admin/assets/{usePlaces-6iTN15Xy.js → usePlaces-Cj5PljTy.js} +2 -2
  230. package/dist/admin/assets/{usePlaces-6iTN15Xy.js.map → usePlaces-Cj5PljTy.js.map} +1 -1
  231. package/dist/admin/assets/{useQueries-CYfcxI0Q.js → useQueries-CeDzli4Z.js} +2 -2
  232. package/dist/admin/assets/{useQueries-CYfcxI0Q.js.map → useQueries-CeDzli4Z.js.map} +1 -1
  233. package/dist/admin/assets/{useResolvedScheme-i1EwGHuF.js → useResolvedScheme-DHD-zisP.js} +2 -2
  234. package/dist/admin/assets/{useResolvedScheme-i1EwGHuF.js.map → useResolvedScheme-DHD-zisP.js.map} +1 -1
  235. package/dist/admin/assets/{useSchemaScopeAccess-WQKmj8_3.js → useSchemaScopeAccess-BNeVslcv.js} +2 -2
  236. package/dist/admin/assets/{useSchemaScopeAccess-WQKmj8_3.js.map → useSchemaScopeAccess-BNeVslcv.js.map} +1 -1
  237. package/dist/admin/assets/{users-BgTNDPXx.js → users-BRefbzuS.js} +2 -2
  238. package/dist/admin/assets/{users-BgTNDPXx.js.map → users-BRefbzuS.js.map} +1 -1
  239. package/dist/admin/assets/{users-B01EH47o.js → users-CppI8ctO.js} +2 -2
  240. package/dist/admin/assets/{users-B01EH47o.js.map → users-CppI8ctO.js.map} +1 -1
  241. package/dist/admin/assets/{zod-79xzBIrW.js → zod-Dcwk-m8X.js} +2 -2
  242. package/dist/admin/assets/{zod-79xzBIrW.js.map → zod-Dcwk-m8X.js.map} +1 -1
  243. package/dist/admin/index.html +55 -55
  244. package/dist/cli.js +110 -18
  245. package/dist/cli.js.map +1 -1
  246. package/dist/config.d.ts +1 -1
  247. package/dist/index.d.ts +1 -1
  248. package/dist/index.js +1 -1
  249. package/dist/{public-CTK82y3t.d.ts → public-DYR4mtVQ.d.ts} +2 -1
  250. package/dist/s3Adapter-C8AgFVYw.js +125 -0
  251. package/dist/s3Adapter-C8AgFVYw.js.map +1 -0
  252. package/dist/{worker-BDYDbaea.js → worker-jz_0yAmS.js} +388 -358
  253. package/dist/worker-jz_0yAmS.js.map +1 -0
  254. package/package.json +8 -5
  255. package/dist/admin/assets/Structure-CoO3iah3.js +0 -2
  256. package/dist/admin/assets/chevron-left-D2DnQyeV.js +0 -2
  257. package/dist/admin/assets/cn-CL1YHRrS.js +0 -2
  258. package/dist/admin/assets/cn-CL1YHRrS.js.map +0 -1
  259. package/dist/admin/assets/coerce-B4MJTGg1.js +0 -2
  260. package/dist/worker-BDYDbaea.js.map +0 -1
@@ -7,7 +7,6 @@ import Fastify, { LogController } from "fastify";
7
7
  import { AliasNode, AndNode, BinaryOperationNode, ColumnNode, ColumnUpdateNode, IdentifierNode, InsertQueryNode, Kysely, ListNode, MysqlAdapter, MysqlIntrospector, MysqlQueryCompiler, OnDuplicateKeyNode, OperationNodeTransformer, OperatorNode, OrNode, OrderByItemNode, ParensNode, PostgresDialect, PrimitiveValueListNode, RawNode, ReferenceNode, SelectAllNode, SelectModifierNode, SelectQueryNode, SelectionNode, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, TableNode, UpdateQueryNode, ValueListNode, ValueNode, ValuesNode, WhereNode, sql } from "kysely";
8
8
  import pg from "pg";
9
9
  import { AsyncLocalStorage } from "node:async_hooks";
10
- import mysql from "mysql2";
11
10
  import { EventEmitter } from "node:events";
12
11
  import { DatabaseSync } from "node:sqlite";
13
12
  import { Type } from "typebox";
@@ -15,9 +14,6 @@ import { Value } from "typebox/value";
15
14
  import { isDeepStrictEqual } from "node:util";
16
15
  import { createJiti } from "jiti";
17
16
  import { pino } from "pino";
18
- import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
19
- import { createPresignedPost } from "@aws-sdk/s3-presigned-post";
20
- import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
21
17
  import fp from "fastify-plugin";
22
18
  import { hash, verify } from "@node-rs/argon2";
23
19
  import cookie from "@fastify/cookie";
@@ -177,6 +173,20 @@ const sqliteLocationOfUrl = (url) => {
177
173
  path
178
174
  };
179
175
  };
176
+ /** `host:port/database` (PostgreSQL, MySQL) or the file path (SQLite) from DATABASE_URL, never the credentials. */
177
+ const describeDatabaseTarget = (connectionString) => {
178
+ if (dialectOfUrl(connectionString) === "sqlite") {
179
+ const location = sqliteLocationOfUrl(connectionString);
180
+ return location.kind === "memory" ? "an in-memory database" : location.path;
181
+ }
182
+ try {
183
+ const url = new URL(connectionString);
184
+ const defaultPort = dialectOfUrl(connectionString) === "mysql" ? "3306" : "5432";
185
+ return `${url.hostname}:${url.port || defaultPort}${url.pathname}`;
186
+ } catch {
187
+ return "the configured database";
188
+ }
189
+ };
180
190
  let current;
181
191
  /** The dialect queries are built for. PostgreSQL until a database handle says otherwise. */
182
192
  const currentDialect = () => current ?? "postgres";
@@ -1899,9 +1909,10 @@ const MYSQL_TABLES = {
1899
1909
  "id",
1900
1910
  "schema_version",
1901
1911
  "permissions_version",
1902
- "updated_at"
1912
+ "updated_at",
1913
+ "release"
1903
1914
  ],
1904
- nullable: [],
1915
+ nullable: ["release"],
1905
1916
  timestamps: ["updated_at"],
1906
1917
  uniqueKeys: [{
1907
1918
  name: "system_versions_pkey",
@@ -2684,6 +2695,13 @@ const typeCast = (field, next) => {
2684
2695
  return next();
2685
2696
  };
2686
2697
  //#endregion
2698
+ //#region src/db/mysql/module.ts
2699
+ /**
2700
+ * mysql2, loaded on first use. A process on PostgreSQL never loads it, and a bundle of the read path (the
2701
+ * in-process delivery runtime) can leave it out as an optional dependency.
2702
+ */
2703
+ const loadMysql = async () => (await import("mysql2")).default;
2704
+ //#endregion
2687
2705
  //#region src/db/mysql/parameters.ts
2688
2706
  /**
2689
2707
  * Parameters the MySQL connection resolves itself just before a statement runs (ADR 0001, "MySQL").
@@ -2885,7 +2903,7 @@ var MysqlDriver = class {
2885
2903
  this.databaseKey = mysqlDatabaseKey(options.url);
2886
2904
  }
2887
2905
  async init() {
2888
- const pool = mysql.createPool(mysqlPoolOptions(this.#options.url, this.#options.poolMax, this.#options.applicationName));
2906
+ const pool = (await loadMysql()).createPool(mysqlPoolOptions(this.#options.url, this.#options.poolMax, this.#options.applicationName));
2889
2907
  pool.on("connection", (connection) => {
2890
2908
  connection.query(SESSION_SETUP, (error) => {
2891
2909
  if (error) {
@@ -9435,6 +9453,10 @@ const bumpSchemaVersion = async (trx, now) => {
9435
9453
  updated_at: now
9436
9454
  })).returning("schema_version").executeTakeFirstOrThrow()).schema_version;
9437
9455
  };
9456
+ /** Records the Shapio release this database is served by (in-process readers compare their own with it). */
9457
+ const setRelease = async (release, executor = db) => {
9458
+ await executor.updateTable("system_versions").set({ release }).execute();
9459
+ };
9438
9460
  //#endregion
9439
9461
  //#region src/schema/snapshot.ts
9440
9462
  /**
@@ -12132,13 +12154,20 @@ const groupBindings = (rows) => {
12132
12154
  return bindings;
12133
12155
  };
12134
12156
  const KNOWN_ACTIONS = /* @__PURE__ */ new Set([...CONTENT_ACTIONS, ...GLOBAL_ACTIONS]);
12157
+ const KNOWN_CONDITIONS = /* @__PURE__ */ new Set(["ownedByPrincipal"]);
12158
+ /**
12159
+ * A stored grant as this build understands it. Rows written by a newer Shapio may name an action or a row
12160
+ * condition this build does not know; they grant nothing here (fail closed). An unknown condition must never
12161
+ * read as "no condition", which would widen the grant to every row.
12162
+ */
12135
12163
  const toGrant$1 = (row) => {
12136
12164
  if (!KNOWN_ACTIONS.has(row.action)) return;
12165
+ if (row.condition !== null && !KNOWN_CONDITIONS.has(row.condition)) return;
12137
12166
  return {
12138
12167
  roleId: row.role_id,
12139
12168
  action: row.action,
12140
12169
  modelId: row.model_id,
12141
- condition: row.condition === "ownedByPrincipal" ? "ownedByPrincipal" : null,
12170
+ condition: row.condition,
12142
12171
  fieldIds: row.field_ids
12143
12172
  };
12144
12173
  };
@@ -13400,6 +13429,20 @@ const SITE_QUERY_PARAMETER = "site";
13400
13429
  * and the header that selects the site. CDNs that key on the URL only need `?site=` instead of the header.
13401
13430
  */
13402
13431
  const DELIVERY_VARY = "Authorization, Cookie, Shapio-Site";
13432
+ //#endregion
13433
+ //#region src/services/contentContext.ts
13434
+ /**
13435
+ * The content service context for one call, the same however the call arrived: the site's view of the pinned
13436
+ * schema (plan site-schema: shared definitions and the site's own), scoped to the site.
13437
+ */
13438
+ const buildContentContext = ({ network, hooks, media, requestId, ip, ...rest }) => ({
13439
+ ...rest,
13440
+ snapshot: network.forSite(rest.site.id),
13441
+ hooks: hooks ?? createContentHooks(),
13442
+ ...media ? { media } : {},
13443
+ ...requestId !== void 0 ? { requestId } : {},
13444
+ ...ip !== void 0 ? { ip } : {}
13445
+ });
13403
13446
  const NUMERIC_TYPES = /* @__PURE__ */ new Set([
13404
13447
  "number",
13405
13448
  "integer",
@@ -14967,13 +15010,12 @@ const SYSTEM_PRINCIPAL = {
14967
15010
  kind: "system",
14968
15011
  component: EXTENSION_SYSTEM_COMPONENT
14969
15012
  };
14970
- const contentContext = async (environment, site, principal) => ({
15013
+ const contentContext = async (environment, site, principal) => buildContentContext({
14971
15014
  db: environment.db,
14972
- snapshot: (await environment.registry.getSnapshot()).forSite(site.id),
15015
+ network: await environment.registry.getSnapshot(),
14973
15016
  permissions: environment.permissions,
14974
15017
  actor: principal ?? SYSTEM_PRINCIPAL,
14975
- site,
14976
- hooks: createContentHooks()
15018
+ site
14977
15019
  });
14978
15020
  const toReadEntry = (view) => ({
14979
15021
  id: view.id,
@@ -15258,157 +15300,6 @@ const createLoggerOptions = (config) => ({
15258
15300
  });
15259
15301
  const createLogger = (config) => pino(createLoggerOptions(config));
15260
15302
  //#endregion
15261
- //#region src/media/contentDisposition.ts
15262
- /**
15263
- * A Content-Disposition value that names the file safely: an ASCII fallback plus the RFC 5987 UTF-8 form,
15264
- * so quotes, semicolons or line breaks in a filename can never inject header parameters.
15265
- */
15266
- const contentDisposition = (filename, type = "inline") => {
15267
- return `${type}; filename="${filename.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_")}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
15268
- };
15269
- /** Types a browser only ever displays passively: opening one directly cannot run script. */
15270
- const PASSIVE_INLINE_TYPE = /^(image\/(png|jpeg|gif|webp|avif|bmp|tiff|x-icon|vnd\.microsoft\.icon)|video\/.+|audio\/.+)$/;
15271
- /**
15272
- * Content-Disposition to store on an object in a bucket. Public objects can be served straight from the
15273
- * bucket or a CDN, where Shapio's sandboxing headers are absent; anything that could run as a page there
15274
- * (SVG, HTML, XML, PDF...) is stored as an attachment, so opening it downloads it. `<img>` tags still show
15275
- * an SVG, since embedding ignores Content-Disposition.
15276
- */
15277
- const bucketContentDisposition = (contentType) => PASSIVE_INLINE_TYPE.test(contentType.toLowerCase()) ? void 0 : "attachment";
15278
- //#endregion
15279
- //#region src/media/errors.ts
15280
- /** Thrown by adapters when a key has no object, so callers can tell "missing" from "storage is down". */
15281
- var ObjectNotFoundError = class extends Error {
15282
- constructor(key, options) {
15283
- super(`No stored object at ${key}`, options);
15284
- this.name = "ObjectNotFoundError";
15285
- }
15286
- };
15287
- var InvalidStorageKeyError = class extends Error {
15288
- constructor(key) {
15289
- super(`Not a valid media storage key: ${JSON.stringify(key)}`);
15290
- this.name = "InvalidStorageKeyError";
15291
- }
15292
- };
15293
- //#endregion
15294
- //#region src/media/s3Adapter.ts
15295
- /** R2 accepts any region name ("auto" by convention); AWS needs the bucket's real region. */
15296
- const DEFAULT_REGION = "us-east-1";
15297
- const isNotFound$2 = (error) => error instanceof S3ServiceException && (error.$metadata.httpStatusCode === 404 || error.name === "NotFound" || error.name === "NoSuchKey");
15298
- const createS3Client = (config) => new S3Client({
15299
- region: config.region ?? DEFAULT_REGION,
15300
- ...config.endpoint ? { endpoint: config.endpoint } : {},
15301
- forcePathStyle: config.forcePathStyle,
15302
- ...config.accessKeyId && config.secretAccessKey ? { credentials: {
15303
- accessKeyId: config.accessKeyId,
15304
- secretAccessKey: config.secretAccessKey
15305
- } } : {},
15306
- requestChecksumCalculation: "WHEN_REQUIRED",
15307
- responseChecksumValidation: "WHEN_REQUIRED"
15308
- });
15309
- /**
15310
- * Any S3-compatible service (AWS S3, Cloudflare R2, B2, Spaces, Wasabi, versitygw). Private objects are read
15311
- * through presigned GET URLs; browsers upload straight to the bucket with a presigned POST whose policy
15312
- * limits size and content type, so Shapio never streams upload bytes.
15313
- */
15314
- const createS3Adapter = ({ config, publicBaseUrl, client }) => {
15315
- const s3 = client ?? createS3Client(config);
15316
- const Bucket = config.bucket;
15317
- const checkKey = (key) => {
15318
- if (!isValidStorageKey(key)) throw new InvalidStorageKeyError(key);
15319
- return key;
15320
- };
15321
- const head = async (key) => {
15322
- try {
15323
- const result = await s3.send(new HeadObjectCommand({
15324
- Bucket,
15325
- Key: checkKey(key)
15326
- }));
15327
- return {
15328
- size: result.ContentLength ?? 0,
15329
- contentType: result.ContentType
15330
- };
15331
- } catch (error) {
15332
- if (isNotFound$2(error)) return;
15333
- throw error;
15334
- }
15335
- };
15336
- return {
15337
- driver: "s3",
15338
- put: async (key, body, { contentType, contentLength }) => {
15339
- await s3.send(new PutObjectCommand({
15340
- Bucket,
15341
- Key: checkKey(key),
15342
- Body: body,
15343
- ContentType: contentType,
15344
- ContentLength: Buffer.isBuffer(body) ? body.length : contentLength,
15345
- ContentDisposition: bucketContentDisposition(contentType)
15346
- }));
15347
- },
15348
- getStream: async (key, range) => {
15349
- try {
15350
- const result = await s3.send(new GetObjectCommand({
15351
- Bucket,
15352
- Key: checkKey(key),
15353
- ...range ? { Range: `bytes=${range.start}-${range.end}` } : {}
15354
- }));
15355
- if (!result.Body) throw new ObjectNotFoundError(key);
15356
- return result.Body;
15357
- } catch (error) {
15358
- throw isNotFound$2(error) ? new ObjectNotFoundError(key, { cause: error }) : error;
15359
- }
15360
- },
15361
- head,
15362
- exists: async (key) => await head(key) !== void 0,
15363
- delete: async (key) => {
15364
- await s3.send(new DeleteObjectCommand({
15365
- Bucket,
15366
- Key: checkKey(key)
15367
- }));
15368
- },
15369
- signedGetUrl: async (key, { expiresInSeconds, filename, contentType }) => getSignedUrl(s3, new GetObjectCommand({
15370
- Bucket,
15371
- Key: checkKey(key),
15372
- ResponseContentType: contentType,
15373
- ResponseContentDisposition: contentDisposition(filename)
15374
- }), { expiresIn: expiresInSeconds }),
15375
- publicUrl: (key) => publicBaseUrl ? `${publicBaseUrl}/${encodeKeyPath(checkKey(key))}` : void 0,
15376
- presignedUpload: async ({ key, contentType, maxSizeBytes, expiresInSeconds }) => {
15377
- const disposition = bucketContentDisposition(contentType);
15378
- const { url, fields } = await createPresignedPost(s3, {
15379
- Bucket,
15380
- Key: checkKey(key),
15381
- Conditions: [
15382
- [
15383
- "content-length-range",
15384
- 1,
15385
- maxSizeBytes
15386
- ],
15387
- [
15388
- "eq",
15389
- "$Content-Type",
15390
- contentType
15391
- ],
15392
- ...disposition ? [[
15393
- "eq",
15394
- "$Content-Disposition",
15395
- disposition
15396
- ]] : []
15397
- ],
15398
- Fields: {
15399
- "Content-Type": contentType,
15400
- ...disposition ? { "Content-Disposition": disposition } : {}
15401
- },
15402
- Expires: expiresInSeconds
15403
- });
15404
- return {
15405
- url,
15406
- fields
15407
- };
15408
- }
15409
- };
15410
- };
15411
- //#endregion
15412
15303
  //#region src/media/origins.ts
15413
15304
  const SAMPLE_KEY = "public/00000000-0000-0000-0000-000000000000/sample/origin-probe";
15414
15305
  const originOf = (url) => {
@@ -15429,6 +15320,7 @@ const resolveMediaOrigins = async (storage) => {
15429
15320
  const base = originOf(storage.publicBaseUrl);
15430
15321
  if (base) origins.add(base);
15431
15322
  if (storage.s3) {
15323
+ const { createS3Adapter } = await import("./s3Adapter-C8AgFVYw.js");
15432
15324
  const adapter = createS3Adapter({
15433
15325
  config: storage.s3,
15434
15326
  publicBaseUrl: void 0
@@ -16086,7 +15978,42 @@ const memoizePermissions = (base, principal) => {
16086
15978
  };
16087
15979
  };
16088
15980
  //#endregion
15981
+ //#region src/permissions/scoped.ts
15982
+ const isLazy = (versions) => "read" in versions;
15983
+ /**
15984
+ * The evaluator at the scope's versions. Inside a transaction before a lazy scope read its versions, the
15985
+ * unbound evaluator checks the versions itself through the transaction, so no pooled connection is asked
15986
+ * for while the transaction holds one.
15987
+ */
15988
+ const boundEvaluator = async (base, versions, executor) => {
15989
+ if (!isLazy(versions)) return base.atVersions?.(versions) ?? base;
15990
+ if (executor && !versions.isRead()) return base;
15991
+ return base.atVersions?.(await versions.read()) ?? base;
15992
+ };
15993
+ /**
15994
+ * One scope's evaluator (an HTTP request, an in-process delivery call): checked against the scope's durable
15995
+ * versions (ADR 0005: a revoke applies from the next scope, not mid-scope) and memoized per (principal, action,
15996
+ * model) for the scope (`memo.ts`).
15997
+ */
15998
+ const scopePermissions = (base, principal, versions) => memoizePermissions({
15999
+ evaluate: async (caller, policyRequest, executor) => (await boundEvaluator(base, versions, executor)).evaluate(caller, policyRequest, executor),
16000
+ canPerform: async (caller, action, executor) => (await boundEvaluator(base, versions, executor)).canPerform(caller, action, executor),
16001
+ canPerformOnSite: async (caller, action, siteId, executor) => (await boundEvaluator(base, versions, executor)).canPerformOnSite(caller, action, siteId, executor)
16002
+ }, principal);
16003
+ //#endregion
16004
+ //#region src/constants/delivery.ts
16005
+ /** `system_settings` key of the delivery descriptor the server writes at start (services/deliveryDescriptor.ts). */
16006
+ const DELIVERY_DESCRIPTOR_SETTING = "delivery_descriptor";
16007
+ //#endregion
16089
16008
  //#region src/repositories/requestState.ts
16009
+ /** The site a lookup names, joined to the one `system_versions` row (at most one row matches). */
16010
+ const siteJoin = (join, lookup) => {
16011
+ switch (lookup.by) {
16012
+ case "id": return join.on("sites.id", "=", lookup.id);
16013
+ case "key": return join.on("sites.key", "=", lookup.key);
16014
+ case "primary": return join.on("sites.is_primary", "=", true);
16015
+ }
16016
+ };
16090
16017
  /**
16091
16018
  * Everything a request checks before its work, in one statement: the durable schema and permissions versions
16092
16019
  * (`system_versions`, one row) and, when asked, the request's site. Read once per request on the pool, before
@@ -16101,13 +16028,7 @@ const read = async (lookup, trx = db) => {
16101
16028
  site: void 0
16102
16029
  };
16103
16030
  }
16104
- const row = await trx.selectFrom("system_versions").leftJoin("sites", (join) => {
16105
- switch (lookup.by) {
16106
- case "id": return join.on("sites.id", "=", lookup.id);
16107
- case "key": return join.on("sites.key", "=", lookup.key);
16108
- case "primary": return join.on("sites.is_primary", "=", true);
16109
- }
16110
- }).select([
16031
+ const row = await trx.selectFrom("system_versions").leftJoin("sites", (join) => siteJoin(join, lookup)).select([
16111
16032
  "system_versions.schema_version",
16112
16033
  "system_versions.permissions_version",
16113
16034
  "sites.id as site_id",
@@ -16150,27 +16071,15 @@ const getRequestState = (request, lookup) => {
16150
16071
  /** Reads the request's site with its state, or on its own when the state was already read without it. */
16151
16072
  const readRequestSite = async (request, lookup) => request.requestState ? findSite(lookup) : (await getRequestState(request, lookup)).site;
16152
16073
  /**
16153
- * The server's evaluator at the request's versions. Inside a transaction before the state was read (no route
16154
- * does that today), the unbound evaluator checks the versions itself through the transaction, so no pooled
16155
- * connection is asked for while the transaction holds one.
16156
- */
16157
- const boundPermissions = async (request, executor) => {
16158
- const base = request.server.permissions;
16159
- if (executor && !request.requestState) return base;
16160
- const { versions } = await getRequestState(request);
16161
- return base.atVersions?.(versions) ?? base;
16162
- };
16163
- /**
16164
16074
  * The request's evaluator: checked against the request's versions, and memoized per (principal, action,
16165
16075
  * model) for the request (`permissions/memo.ts`), for REST as for GraphQL. Create it after site resolution,
16166
16076
  * which settles the principal.
16167
16077
  */
16168
16078
  const getRequestPermissions = (request) => {
16169
- request.requestPermissions ??= memoizePermissions({
16170
- evaluate: async (principal, policyRequest, executor) => (await boundPermissions(request, executor)).evaluate(principal, policyRequest, executor),
16171
- canPerform: async (principal, action, executor) => (await boundPermissions(request, executor)).canPerform(principal, action, executor),
16172
- canPerformOnSite: async (principal, action, siteId, executor) => (await boundPermissions(request, executor)).canPerformOnSite(principal, action, siteId, executor)
16173
- }, request.principal);
16079
+ request.requestPermissions ??= scopePermissions(request.server.permissions, request.principal, {
16080
+ read: async () => (await getRequestState(request)).versions,
16081
+ isRead: () => request.requestState !== null
16082
+ });
16174
16083
  return request.requestPermissions;
16175
16084
  };
16176
16085
  //#endregion
@@ -16413,12 +16322,14 @@ const revokeApiToken = async (context, permissions, id) => {
16413
16322
  });
16414
16323
  });
16415
16324
  };
16325
+ /** Whether a token with this expiry has expired at `now`. */
16326
+ const isTokenExpired = (expiresAt, now) => expiresAt !== null && expiresAt.getTime() <= now.getTime();
16416
16327
  /** The principal for a bearer API token, or undefined when unknown, revoked or expired. */
16417
- const resolveApiToken = async (value, now = /* @__PURE__ */ new Date()) => {
16418
- const row = await findLiveByHash$1(hashToken(value));
16419
- if (!row || row.expires_at !== null && row.expires_at.getTime() <= now.getTime()) return;
16328
+ const resolveApiToken = async (value, now = /* @__PURE__ */ new Date(), { recordUse = true, executor = db } = {}) => {
16329
+ const row = await findLiveByHash$1(hashToken(value), executor);
16330
+ if (!row || isTokenExpired(row.expires_at, now)) return;
16420
16331
  const staleBefore = /* @__PURE__ */ new Date(now.getTime() - LAST_SEEN_WRITE_INTERVAL_MS);
16421
- if (row.last_used_at === null || row.last_used_at.getTime() < staleBefore.getTime()) await touchLastUsed$1(row.id, now, staleBefore);
16332
+ if (recordUse && (row.last_used_at === null || row.last_used_at.getTime() < staleBefore.getTime())) await touchLastUsed$1(row.id, now, staleBefore, executor);
16422
16333
  return {
16423
16334
  kind: "token",
16424
16335
  tokenId: row.id,
@@ -17590,7 +17501,7 @@ const editorManifestPlugin = fp(async (app, { manifest }) => {
17590
17501
  app.decorate("editorManifest", manifest);
17591
17502
  }, { name: "shapio-editor-manifest" });
17592
17503
  //#endregion
17593
- //#region src/plugins/errorHandler.ts
17504
+ //#region src/helpers/errorResponse.ts
17594
17505
  const STATUS_CODES = {
17595
17506
  400: "BAD_REQUEST",
17596
17507
  401: "UNAUTHORIZED",
@@ -17607,6 +17518,10 @@ const STATUS_CODES = {
17607
17518
  };
17608
17519
  /** Errors raised by @fastify/csrf-protection (plugins/csrf.ts). */
17609
17520
  const CSRF_ERROR_CODES$1 = /* @__PURE__ */ new Set(["FST_CSRF_INVALID_TOKEN", "FST_CSRF_MISSING_SECRET"]);
17521
+ /**
17522
+ * The one error shape every endpoint returns, `{ error: { code, message, details? } }`, and its status.
17523
+ * Server errors (5xx) never carry their internal text; the caller logs the original error.
17524
+ */
17610
17525
  const toErrorResponse = (error) => {
17611
17526
  if (error instanceof AppError) {
17612
17527
  const body = { error: {
@@ -17619,7 +17534,7 @@ const toErrorResponse = (error) => {
17619
17534
  body
17620
17535
  };
17621
17536
  }
17622
- if (CSRF_ERROR_CODES$1.has(error.code)) return {
17537
+ if (error.code !== void 0 && CSRF_ERROR_CODES$1.has(error.code)) return {
17623
17538
  statusCode: 403,
17624
17539
  body: { error: {
17625
17540
  code: "CSRF_INVALID",
@@ -17654,6 +17569,8 @@ const toErrorResponse = (error) => {
17654
17569
  } }
17655
17570
  };
17656
17571
  };
17572
+ //#endregion
17573
+ //#region src/plugins/errorHandler.ts
17657
17574
  /** The single error handler: consistent `{ error: { code, message, details? } }` bodies and logging. */
17658
17575
  const errorHandlerPlugin = fp(async (app) => {
17659
17576
  app.decorateRequest("errorCode", void 0);
@@ -18223,6 +18140,14 @@ const createMysqlNotificationListener = (options) => {
18223
18140
  });
18224
18141
  };
18225
18142
  const connect = async () => {
18143
+ if (closed) return;
18144
+ let mysql;
18145
+ try {
18146
+ mysql = await loadMysql();
18147
+ } catch (error) {
18148
+ log.error({ err: error }, "mysql2 could not be loaded; notifications are off");
18149
+ return;
18150
+ }
18226
18151
  if (closed) return;
18227
18152
  const current = mysql.createConnection(mysqlPoolOptions(connectionString, 1, options.applicationName ?? "shapio-listen"));
18228
18153
  connection = current;
@@ -18980,6 +18905,45 @@ const createLoaders = (siteId, content) => {
18980
18905
  };
18981
18906
  };
18982
18907
  //#endregion
18908
+ //#region src/schema/codegen/graphql/requestContext.ts
18909
+ /**
18910
+ * The context every resolver of one operation receives, with its own DataLoaders. Carries no HTTP objects, so
18911
+ * an operation can run outside a request.
18912
+ */
18913
+ const createGraphqlContext = ({ site, permissions, isAdmin, content }) => {
18914
+ let base;
18915
+ const contentAt = async (snapshot) => {
18916
+ base ??= content(snapshot);
18917
+ return {
18918
+ ...await base,
18919
+ snapshot,
18920
+ permissions
18921
+ };
18922
+ };
18923
+ return {
18924
+ permissions,
18925
+ isAdmin,
18926
+ site,
18927
+ loaders: createLoaders(() => site.id, contentAt),
18928
+ content: contentAt
18929
+ };
18930
+ };
18931
+ //#endregion
18932
+ //#region src/helpers/siteQuery.ts
18933
+ const isSiteSegment = (segment) => {
18934
+ const key = segment.split("=", 1)[0] ?? "";
18935
+ try {
18936
+ return decodeURIComponent(key.replace(/\+/g, " ")) === SITE_QUERY_PARAMETER;
18937
+ } catch {
18938
+ return false;
18939
+ }
18940
+ };
18941
+ /**
18942
+ * A raw querystring (without `?`) without its `site` parameter: site resolution consumes it, and the content
18943
+ * compiler refuses unknown keys. The rest is left exactly as sent (bracket syntax is the compiler's to parse).
18944
+ */
18945
+ const withoutSiteParameter = (rawQuery) => rawQuery.split("&").filter((segment) => !isSiteSegment(segment)).join("&");
18946
+ //#endregion
18983
18947
  //#region src/controllers/contentContext.ts
18984
18948
  /**
18985
18949
  * The service context for a content request: the pinned snapshot, the principal and dependencies.
@@ -18987,9 +18951,9 @@ const createLoaders = (siteId, content) => {
18987
18951
  * memoized per (principal, action, model), so the read, relation visibility, populate and usage recording
18988
18952
  * evaluate each model's policy once.
18989
18953
  */
18990
- const contentContextFor = async (request) => ({
18954
+ const contentContextFor = async (request) => buildContentContext({
18991
18955
  db: request.server.db,
18992
- snapshot: await getRequestSchema(request),
18956
+ network: await getRequestNetworkSchema(request),
18993
18957
  permissions: getRequestPermissions(request),
18994
18958
  actor: request.principal,
18995
18959
  site: getRequestSite(request),
@@ -19001,14 +18965,6 @@ const contentContextFor = async (request) => ({
19001
18965
  requestId: request.id,
19002
18966
  ip: request.ip
19003
18967
  });
19004
- const isSiteSegment = (segment) => {
19005
- const key = segment.split("=", 1)[0] ?? "";
19006
- try {
19007
- return decodeURIComponent(key.replace(/\+/g, " ")) === SITE_QUERY_PARAMETER;
19008
- } catch {
19009
- return false;
19010
- }
19011
- };
19012
18968
  /**
19013
18969
  * The raw querystring (bracket syntax is parsed by the content compiler, not Fastify's flat parser), without
19014
18970
  * `?site=`: site resolution (plugins/siteResolution.ts) consumed it, and the compiler refuses unknown keys.
@@ -19016,34 +18972,19 @@ const isSiteSegment = (segment) => {
19016
18972
  const rawQueryOf = (request) => {
19017
18973
  const index = request.url.indexOf("?");
19018
18974
  if (index === -1) return "";
19019
- return request.url.slice(index + 1).split("&").filter((segment) => !isSiteSegment(segment)).join("&");
18975
+ return withoutSiteParameter(request.url.slice(index + 1));
19020
18976
  };
19021
18977
  //#endregion
19022
18978
  //#region src/controllers/graphqlContext.ts
19023
18979
  /** Admin users and admin-scope API tokens: may read drafts. */
19024
18980
  const isAdminPrincipal = (principal) => principal.kind === "admin" || principal.kind === "token" && principal.scope === "admin";
19025
18981
  /** The context every resolver of one `/api/graphql` request receives (built after site resolution). */
19026
- const createGraphqlRequestContext = (request) => {
19027
- const site = getRequestSite(request);
19028
- const permissions = getRequestPermissions(request);
19029
- let base;
19030
- const content = async (snapshot) => {
19031
- base ??= contentContextFor(request);
19032
- return {
19033
- ...await base,
19034
- snapshot,
19035
- permissions
19036
- };
19037
- };
19038
- return {
19039
- request,
19040
- permissions,
19041
- isAdmin: isAdminPrincipal(request.principal),
19042
- site,
19043
- loaders: createLoaders(() => site.id, content),
19044
- content
19045
- };
19046
- };
18982
+ const createGraphqlRequestContext = (request) => createGraphqlContext({
18983
+ site: getRequestSite(request),
18984
+ permissions: getRequestPermissions(request),
18985
+ isAdmin: isAdminPrincipal(request.principal),
18986
+ content: () => contentContextFor(request)
18987
+ });
19047
18988
  //#endregion
19048
18989
  //#region src/controllers/graphql.ts
19049
18990
  const introspectionDisabled = () => new AppError(403, "INTROSPECTION_DISABLED", "Schema introspection needs an admin session or an API token (or GRAPHQL_PUBLIC_INTROSPECTION=true)");
@@ -21548,6 +21489,21 @@ const graphqlPlugin = fp(async (app, { config, urls }) => {
21548
21489
  ]
21549
21490
  });
21550
21491
  //#endregion
21492
+ //#region src/media/errors.ts
21493
+ /** Thrown by adapters when a key has no object, so callers can tell "missing" from "storage is down". */
21494
+ var ObjectNotFoundError = class extends Error {
21495
+ constructor(key, options) {
21496
+ super(`No stored object at ${key}`, options);
21497
+ this.name = "ObjectNotFoundError";
21498
+ }
21499
+ };
21500
+ var InvalidStorageKeyError = class extends Error {
21501
+ constructor(key) {
21502
+ super(`Not a valid media storage key: ${JSON.stringify(key)}`);
21503
+ this.name = "InvalidStorageKeyError";
21504
+ }
21505
+ };
21506
+ //#endregion
21551
21507
  //#region src/media/signing.ts
21552
21508
  const sign = (secret, purpose, subject, expiresAt) => createHmac("sha256", secret).update(`${purpose}\n${subject}\n${expiresAt}`).digest("base64url");
21553
21509
  const safeEqual = (a, b) => {
@@ -21665,9 +21621,10 @@ var StorageNotConfiguredError = class extends Error {
21665
21621
  };
21666
21622
  /**
21667
21623
  * Every adapter this instance can reach. Local disk is always available; S3 whenever its bucket is set,
21668
- * whatever STORAGE_DRIVER says, so assets keep being served while `shapio media migrate` moves them.
21624
+ * whatever STORAGE_DRIVER says, so assets keep being served while `shapio media migrate` moves them. The S3
21625
+ * adapter (and the AWS SDK) is loaded only then.
21669
21626
  */
21670
- const createMediaStorage = (config, deps) => {
21627
+ const createMediaStorage = async (config, deps) => {
21671
21628
  const adapters = /* @__PURE__ */ new Map();
21672
21629
  adapters.set("local", createLocalAdapter({
21673
21630
  root: config.mediaPath,
@@ -21675,10 +21632,13 @@ const createMediaStorage = (config, deps) => {
21675
21632
  signingSecret: deps.signingSecret,
21676
21633
  publicBaseUrl: config.publicBaseUrl
21677
21634
  }));
21678
- if (config.s3) adapters.set("s3", createS3Adapter({
21679
- config: config.s3,
21680
- publicBaseUrl: config.publicBaseUrl
21681
- }));
21635
+ if (config.s3) {
21636
+ const { createS3Adapter } = await import("./s3Adapter-C8AgFVYw.js");
21637
+ adapters.set("s3", createS3Adapter({
21638
+ config: config.s3,
21639
+ publicBaseUrl: config.publicBaseUrl
21640
+ }));
21641
+ }
21682
21642
  const get = (driver) => {
21683
21643
  const adapter = adapters.get(driver);
21684
21644
  if (!adapter) throw new StorageNotConfiguredError(driver);
@@ -21694,7 +21654,7 @@ const createMediaStorage = (config, deps) => {
21694
21654
  //#region src/plugins/mediaStorage.ts
21695
21655
  /** Exposes the media storage adapters to routes. Registered after the signing secret is resolved. */
21696
21656
  const mediaStoragePlugin = fp(async (app, { storage, urls, signingSecret }) => {
21697
- app.decorate("mediaStorage", createMediaStorage(storage, {
21657
+ app.decorate("mediaStorage", await createMediaStorage(storage, {
21698
21658
  urls,
21699
21659
  signingSecret
21700
21660
  }));
@@ -38999,7 +38959,7 @@ const openStoredBundle = (storage, stored) => storage.get(stored.driver).getStre
38999
38959
  const removeStoredBundle = (storage, stored) => storage.get(stored.driver).delete(stored.key);
39000
38960
  //#endregion
39001
38961
  //#region src/constants/version.ts
39002
- const SHAPIO_VERSION = "0.4.1";
38962
+ const SHAPIO_VERSION = "0.5.0";
39003
38963
  //#endregion
39004
38964
  //#region src/repositories/transferExport.ts
39005
38965
  /**
@@ -39791,21 +39751,21 @@ const importContent = async (run, initial) => {
39791
39751
  });
39792
39752
  };
39793
39753
  /** Deletes live target entries of the bundle's models that the bundle does not have. */
39794
- const prune = async (run, snapshot) => {
39754
+ const prune = async (run, network) => {
39795
39755
  const keep = /* @__PURE__ */ new Set();
39796
39756
  const models = /* @__PURE__ */ new Set();
39797
39757
  for await (const { record } of readBundle(await open$1(run))) if (record.type === "entry") {
39798
39758
  keep.add(record.id);
39799
39759
  models.add(record.modelId);
39800
39760
  }
39801
- const context = {
39761
+ const context = buildContentContext({
39802
39762
  db: run.deps.db,
39803
- snapshot,
39763
+ network,
39804
39764
  permissions: SYSTEM_PERMISSIONS,
39805
39765
  actor: SYSTEM_ACTOR,
39806
- site: await getSiteRef(run.payload.siteId, run.deps.db),
39807
- hooks: createContentHooks()
39808
- };
39766
+ site: await getSiteRef(run.payload.siteId, run.deps.db)
39767
+ });
39768
+ const { snapshot } = context;
39809
39769
  let failed = [];
39810
39770
  for (let round = 0; round < PRUNE_ROUNDS; round += 1) {
39811
39771
  failed = [];
@@ -39841,7 +39801,7 @@ const runPhase = async (run, phase) => {
39841
39801
  case "media": return importMedia(run);
39842
39802
  case "entries": return importEntryRows(run, await loadSiteSnapshot(run));
39843
39803
  case "content": return importContent(run, await loadSiteSnapshot(run));
39844
- case "prune": return run.payload.prune ? prune(run, await loadSiteSnapshot(run)) : void 0;
39804
+ case "prune": return run.payload.prune ? prune(run, await loadSnapshot(run.deps.db)) : void 0;
39845
39805
  case "cleanup": return removeStoredBundle(run.deps.storage, run.payload.bundle).catch((error) => run.job.log.warn({
39846
39806
  err: describeError(error),
39847
39807
  key: run.payload.bundle.key
@@ -42293,14 +42253,14 @@ const docsRoutes = async (app) => {
42293
42253
  //#endregion
42294
42254
  //#region src/db/migrations/20261001120000_create_platform_tables.ts
42295
42255
  var _20261001120000_create_platform_tables_exports = /* @__PURE__ */ __exportAll({
42296
- down: () => down$33,
42297
- up: () => up$33
42256
+ down: () => down$34,
42257
+ up: () => up$34
42298
42258
  });
42299
42259
  /**
42300
42260
  * Foundation tables shared by every package: durable versions, the job queue, the transactional outbox
42301
42261
  * and the audit log. Statuses are text with CHECK constraints so new states need only a constraint change.
42302
42262
  */
42303
- const up$33 = async (db) => {
42263
+ const up$34 = async (db) => {
42304
42264
  await db.schema.createTable("system_versions").addColumn("id", "boolean", (col) => col.primaryKey().defaultTo(true).check(sql`id`)).addColumn("schema_version", "integer", (col) => col.notNull().defaultTo(0)).addColumn("permissions_version", "integer", (col) => col.notNull().defaultTo(0)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).execute();
42305
42265
  await sql`insert into system_versions default values`.execute(db);
42306
42266
  await db.schema.createTable("jobs").addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`)).addColumn("type", "text", (col) => col.notNull()).addColumn("payload", "jsonb", (col) => col.notNull().defaultTo(sql`'{}'::jsonb`)).addColumn("status", "text", (col) => col.notNull().defaultTo("pending").check(sql`status in ('pending', 'running', 'succeeded', 'dead')`)).addColumn("priority", "integer", (col) => col.notNull().defaultTo(0)).addColumn("run_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("attempts", "integer", (col) => col.notNull().defaultTo(0)).addColumn("max_attempts", "integer", (col) => col.notNull().defaultTo(10).check(sql`max_attempts > 0`)).addColumn("idempotency_key", "text").addColumn("locked_by", "text").addColumn("locked_until", "timestamptz").addColumn("checkpoint", "jsonb").addColumn("result", "jsonb").addColumn("last_error", "text").addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("finished_at", "timestamptz").execute();
@@ -42322,7 +42282,7 @@ const up$33 = async (db) => {
42322
42282
  "occurred_at desc"
42323
42283
  ]).execute();
42324
42284
  };
42325
- const down$33 = async (db) => {
42285
+ const down$34 = async (db) => {
42326
42286
  await db.schema.dropTable("audit_events").execute();
42327
42287
  await db.schema.dropTable("outbox_events").execute();
42328
42288
  await db.schema.dropTable("jobs").execute();
@@ -42331,8 +42291,8 @@ const down$33 = async (db) => {
42331
42291
  //#endregion
42332
42292
  //#region src/db/migrations/20261001130000_create_admin_identity.ts
42333
42293
  var _20261001130000_create_admin_identity_exports = /* @__PURE__ */ __exportAll({
42334
- down: () => down$32,
42335
- up: () => up$32
42294
+ down: () => down$33,
42295
+ up: () => up$33
42336
42296
  });
42337
42297
  /**
42338
42298
  * Admin identity (package B, ADR 0005): admin users, roles and their permission grants, server-side
@@ -42341,7 +42301,7 @@ var _20261001130000_create_admin_identity_exports = /* @__PURE__ */ __exportAll(
42341
42301
  */
42342
42302
  const withTimestamps$3 = (table) => table.addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`));
42343
42303
  const uuidPrimaryKey$3 = (table) => table.addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`));
42344
- const up$32 = async (db) => {
42304
+ const up$33 = async (db) => {
42345
42305
  await withTimestamps$3(uuidPrimaryKey$3(db.schema.createTable("admin_users")).addColumn("email", "text", (col) => col.notNull().unique()).addColumn("name", "text", (col) => col.notNull().defaultTo("")).addColumn("password_hash", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo("active").check(sql`status in ('active', 'disabled')`)).addColumn("last_login_at", "timestamptz").addColumn("password_changed_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`))).execute();
42346
42306
  await withTimestamps$3(uuidPrimaryKey$3(db.schema.createTable("admin_roles")).addColumn("key", "text", (col) => col.notNull().unique().check(sql`key ~ '^[a-z][a-z0-9-]{0,62}$'`)).addColumn("name", "text", (col) => col.notNull()).addColumn("description", "text", (col) => col.notNull().defaultTo("")).addColumn("kind", "text", (col) => col.notNull().defaultTo("admin").check(sql`kind in ('admin', 'delivery')`)).addColumn("is_system", "boolean", (col) => col.notNull().defaultTo(false)).addColumn("version", "integer", (col) => col.notNull().defaultTo(1))).execute();
42347
42307
  await withTimestamps$3(uuidPrimaryKey$3(db.schema.createTable("admin_role_permissions")).addColumn("role_id", "uuid", (col) => col.notNull().references("admin_roles.id").onDelete("cascade")).addColumn("action", "text", (col) => col.notNull()).addColumn("model_id", "text").addColumn("condition", "text", (col) => col.check(sql`condition in ('ownedByPrincipal')`)).addColumn("field_ids", sql`text[]`).addUniqueConstraint("admin_role_permissions_grant_uq", [
@@ -42361,7 +42321,7 @@ const up$32 = async (db) => {
42361
42321
  await withTimestamps$3(uuidPrimaryKey$3(db.schema.createTable("api_tokens")).addColumn("name", "text", (col) => col.notNull()).addColumn("token_hash", "text", (col) => col.notNull().unique()).addColumn("token_prefix", "text", (col) => col.notNull()).addColumn("role_id", "uuid", (col) => col.notNull().references("admin_roles.id").onDelete("restrict")).addColumn("created_by", "uuid", (col) => col.references("admin_users.id").onDelete("set null")).addColumn("expires_at", "timestamptz").addColumn("last_used_at", "timestamptz").addColumn("revoked_at", "timestamptz")).execute();
42362
42322
  await db.schema.createIndex("api_tokens_role_idx").on("api_tokens").column("role_id").execute();
42363
42323
  };
42364
- const down$32 = async (db) => {
42324
+ const down$33 = async (db) => {
42365
42325
  await db.schema.dropTable("api_tokens").execute();
42366
42326
  await db.schema.dropTable("setup_tokens").execute();
42367
42327
  await db.schema.dropTable("password_resets").execute();
@@ -42375,31 +42335,31 @@ const down$32 = async (db) => {
42375
42335
  //#endregion
42376
42336
  //#region src/db/migrations/20261001130500_create_system_settings.ts
42377
42337
  var _20261001130500_create_system_settings_exports = /* @__PURE__ */ __exportAll({
42378
- down: () => down$31,
42379
- up: () => up$31
42338
+ down: () => down$32,
42339
+ up: () => up$32
42380
42340
  });
42381
42341
  /**
42382
42342
  * Instance-wide key/value settings that must survive restarts and be shared by every instance, e.g. the
42383
42343
  * generated signing secret. General purpose: later packages add keys, not tables.
42384
42344
  */
42385
- const up$31 = async (db) => {
42345
+ const up$32 = async (db) => {
42386
42346
  await db.schema.createTable("system_settings").addColumn("key", "text", (col) => col.primaryKey()).addColumn("value", "text", (col) => col.notNull()).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).execute();
42387
42347
  };
42388
- const down$31 = async (db) => {
42348
+ const down$32 = async (db) => {
42389
42349
  await db.schema.dropTable("system_settings").execute();
42390
42350
  };
42391
42351
  //#endregion
42392
42352
  //#region src/db/migrations/20261001140000_create_schema_tables.ts
42393
42353
  var _20261001140000_create_schema_tables_exports = /* @__PURE__ */ __exportAll({
42394
- down: () => down$30,
42395
- up: () => up$30
42354
+ down: () => down$31,
42355
+ up: () => up$31
42396
42356
  });
42397
42357
  /**
42398
42358
  * Schema registry (package D, ADR 0002): models and components (one table, `kind` discriminator), immutable
42399
42359
  * schema revisions, the per-model active pointer, schema change jobs, locales and schema settings.
42400
42360
  * Model definitions are data: creating or changing a model never runs DDL (CONTRIBUTING.md rule 1).
42401
42361
  */
42402
- const up$30 = async (db) => {
42362
+ const up$31 = async (db) => {
42403
42363
  await db.schema.createTable("models").addColumn("id", "uuid", (col) => col.primaryKey()).addColumn("kind", "text", (col) => col.notNull().check(sql`kind in ('collection', 'singleton', 'component')`)).addColumn("api_key", "text", (col) => col.notNull()).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("deleted_at", "timestamptz").execute();
42404
42364
  await sql`create unique index models_api_key_uq on models (lower(api_key)) where deleted_at is null`.execute(db);
42405
42365
  await db.schema.createTable("schema_revisions").addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`)).addColumn("model_id", "uuid", (col) => col.notNull().references("models.id").onDelete("restrict")).addColumn("version", "integer", (col) => col.notNull().check(sql`version > 0`)).addColumn("definition", "jsonb", (col) => col.notNull()).addColumn("hash", "text", (col) => col.notNull()).addColumn("parent_revision_id", "uuid", (col) => col.references("schema_revisions.id")).addColumn("created_by_type", "text", (col) => col.notNull()).addColumn("created_by_id", "text").addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addUniqueConstraint("schema_revisions_model_version_uq", ["model_id", "version"]).execute();
@@ -42427,7 +42387,7 @@ const up$30 = async (db) => {
42427
42387
  await db.schema.createTable("schema_settings").addColumn("id", "boolean", (col) => col.primaryKey().defaultTo(true).check(sql`id`)).addColumn("read_only", "boolean", (col) => col.notNull().defaultTo(false)).addColumn("read_only_reason", "text").addColumn("updated_by_type", "text").addColumn("updated_by_id", "text").addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).execute();
42428
42388
  await sql`insert into schema_settings default values`.execute(db);
42429
42389
  };
42430
- const down$30 = async (db) => {
42390
+ const down$31 = async (db) => {
42431
42391
  await db.schema.dropTable("schema_settings").execute();
42432
42392
  await db.schema.dropTable("locales").execute();
42433
42393
  await db.schema.dropTable("schema_change_jobs").execute();
@@ -42439,8 +42399,8 @@ const down$30 = async (db) => {
42439
42399
  //#endregion
42440
42400
  //#region src/db/migrations/20261001150000_create_content_tables.ts
42441
42401
  var _20261001150000_create_content_tables_exports = /* @__PURE__ */ __exportAll({
42442
- down: () => down$29,
42443
- up: () => up$29
42402
+ down: () => down$30,
42403
+ up: () => up$30
42444
42404
  });
42445
42405
  /**
42446
42406
  * Content storage (package E, ADR 0001): entries, immutable content revisions, mutable heads (one per
@@ -42448,7 +42408,7 @@ var _20261001150000_create_content_tables_exports = /* @__PURE__ */ __exportAll(
42448
42408
  * and the publication log behind `?snapshot=N`. User content models never get tables of their own
42449
42409
  * (CONTRIBUTING.md rule 1): their values are JSONB keyed by stable field IDs.
42450
42410
  */
42451
- const up$29 = async (db) => {
42411
+ const up$30 = async (db) => {
42452
42412
  await db.schema.createTable("entries").addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`)).addColumn("model_id", "uuid", (col) => col.notNull().references("models.id").onDelete("restrict")).addColumn("owner_app_user_id", "uuid").addColumn("created_by_admin_id", "uuid", (col) => col.references("admin_users.id").onDelete("set null")).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("deleted_at", "timestamptz").execute();
42453
42413
  await sql`create index entries_model_idx on entries (model_id, created_at) where deleted_at is null`.execute(db);
42454
42414
  await sql`create index entries_owner_idx on entries (owner_app_user_id) where owner_app_user_id is not null`.execute(db);
@@ -42512,7 +42472,7 @@ const up$29 = async (db) => {
42512
42472
  await sql`create unique index publication_log_open_uq on publication_log (entry_id, locale) where to_seq is null`.execute(db);
42513
42473
  await db.schema.createIndex("publication_log_model_seq_idx").on("publication_log").columns(["model_id", "from_seq"]).execute();
42514
42474
  };
42515
- const down$29 = async (db) => {
42475
+ const down$30 = async (db) => {
42516
42476
  await db.schema.dropTable("publication_log").execute();
42517
42477
  await db.schema.dropTable("publication_state").execute();
42518
42478
  await db.schema.dropTable("unique_values").execute();
@@ -42525,8 +42485,8 @@ const down$29 = async (db) => {
42525
42485
  //#endregion
42526
42486
  //#region src/db/migrations/20261001160000_create_media_tables.ts
42527
42487
  var _20261001160000_create_media_tables_exports = /* @__PURE__ */ __exportAll({
42528
- down: () => down$28,
42529
- up: () => up$28
42488
+ down: () => down$29,
42489
+ up: () => up$29
42530
42490
  });
42531
42491
  /**
42532
42492
  * Media library (package G): folders, assets, image variants, references from content and upload grants.
@@ -42537,7 +42497,7 @@ const withTimestamps$2 = (table) => table.addColumn("created_at", "timestamptz",
42537
42497
  const uuidPrimaryKey$2 = (table) => table.addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`));
42538
42498
  const STORAGE_DRIVER_CHECK = sql`storage_driver in ('local', 's3')`;
42539
42499
  const VISIBILITY_CHECK = sql`visibility in ('public', 'private')`;
42540
- const up$28 = async (db) => {
42500
+ const up$29 = async (db) => {
42541
42501
  await withTimestamps$2(uuidPrimaryKey$2(db.schema.createTable("media_folders")).addColumn("parent_id", "uuid", (col) => col.references("media_folders.id").onDelete("restrict")).addColumn("name", "text", (col) => col.notNull().check(sql`length(name) between 1 and 255`)).addColumn("created_by", "uuid", (col) => col.references("admin_users.id").onDelete("set null")).addColumn("version", "integer", (col) => col.notNull().defaultTo(1))).execute();
42542
42502
  await sql`create unique index media_folders_sibling_name_uq on media_folders
42543
42503
  (coalesce(parent_id, '00000000-0000-0000-0000-000000000000'::uuid), lower(name))`.execute(db);
@@ -42566,7 +42526,7 @@ const up$28 = async (db) => {
42566
42526
  ]).execute();
42567
42527
  await db.schema.createTable("media_upload_grants").addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`)).addColumn("asset_id", "uuid", (col) => col.notNull()).addColumn("kind", "text", (col) => col.notNull().check(sql`kind in ('create', 'replace')`)).addColumn("storage_driver", "text", (col) => col.notNull().check(STORAGE_DRIVER_CHECK)).addColumn("storage_key", "text", (col) => col.notNull().unique()).addColumn("original_filename", "text", (col) => col.notNull()).addColumn("declared_mime_type", "text", (col) => col.notNull()).addColumn("max_size_bytes", "bigint", (col) => col.notNull()).addColumn("folder_id", "uuid").addColumn("visibility", "text", (col) => col.notNull().check(VISIBILITY_CHECK)).addColumn("created_by", "uuid", (col) => col.references("admin_users.id").onDelete("set null")).addColumn("created_by_principal", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo("pending").check(sql`status in ('pending', 'consumed', 'rejected', 'expired')`)).addColumn("expires_at", "timestamptz", (col) => col.notNull()).addColumn("consumed_at", "timestamptz").addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).execute();
42568
42528
  };
42569
- const down$28 = async (db) => {
42529
+ const down$29 = async (db) => {
42570
42530
  await db.schema.dropTable("media_upload_grants").execute();
42571
42531
  await db.schema.dropTable("media_references").execute();
42572
42532
  await db.schema.dropTable("media_variants").execute();
@@ -42576,8 +42536,8 @@ const down$28 = async (db) => {
42576
42536
  //#endregion
42577
42537
  //#region src/db/migrations/20261001170000_create_publishing_tables.ts
42578
42538
  var _20261001170000_create_publishing_tables_exports = /* @__PURE__ */ __exportAll({
42579
- down: () => down$27,
42580
- up: () => up$27
42539
+ down: () => down$28,
42540
+ up: () => up$28
42581
42541
  });
42582
42542
  /**
42583
42543
  * Publishing (package H): scheduled publications, releases, webhooks and their delivery log, deployment
@@ -42616,13 +42576,13 @@ const createPreviewTables = async (db) => {
42616
42576
  await uuidPrimaryKey$1(db.schema.createTable("preview_tokens")).addColumn("token_hash", "text", (col) => col.notNull().unique()).addColumn("token_prefix", "text", (col) => col.notNull()).addColumn("model_id", "uuid", (col) => col.notNull()).addColumn("entry_id", "uuid", (col) => col.references("entries.id").onDelete("cascade")).addColumn("locale", "text").addColumn("connection_id", "uuid", (col) => col.references("deployment_connections.id").onDelete("set null")).addColumn("delivery_role_id", "uuid", (col) => col.references("admin_roles.id").onDelete("set null")).addColumn("created_by", "uuid", (col) => col.notNull().references("admin_users.id").onDelete("cascade")).addColumn("expires_at", "timestamptz", (col) => col.notNull()).addColumn("revoked_at", "timestamptz").addColumn("last_used_at", "timestamptz").addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).execute();
42617
42577
  await db.schema.createIndex("preview_tokens_entry_idx").on("preview_tokens").columns(["entry_id", "created_at desc"]).execute();
42618
42578
  };
42619
- const up$27 = async (db) => {
42579
+ const up$28 = async (db) => {
42620
42580
  await createSchedulingTables(db);
42621
42581
  await createWebhookTables(db);
42622
42582
  await createDeploymentTables(db);
42623
42583
  await createPreviewTables(db);
42624
42584
  };
42625
- const down$27 = async (db) => {
42585
+ const down$28 = async (db) => {
42626
42586
  await db.schema.dropTable("preview_tokens").execute();
42627
42587
  await db.schema.dropTable("deployment_runs").execute();
42628
42588
  await db.schema.dropTable("deployment_connections").execute();
@@ -42635,8 +42595,8 @@ const down$27 = async (db) => {
42635
42595
  //#endregion
42636
42596
  //#region src/db/migrations/20261001180000_create_app_users.ts
42637
42597
  var _20261001180000_create_app_users_exports = /* @__PURE__ */ __exportAll({
42638
- down: () => down$26,
42639
- up: () => up$26
42598
+ down: () => down$27,
42599
+ up: () => up$27
42640
42600
  });
42641
42601
  /**
42642
42602
  * App users (package I, ADR 0005): the end users of sites and apps built on Shapio, with their own roles
@@ -42687,7 +42647,7 @@ const createCredentials = async (db) => {
42687
42647
  }
42688
42648
  await db.schema.createTable("app_login_codes").addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`)).addColumn("app_user_id", "uuid", (col) => col.notNull().references("app_users.id").onDelete("cascade")).addColumn("code_hash", "text", (col) => col.notNull().unique()).addColumn("expires_at", "timestamptz", (col) => col.notNull()).addColumn("used_at", "timestamptz").addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).execute();
42689
42649
  };
42690
- const up$26 = async (db) => {
42650
+ const up$27 = async (db) => {
42691
42651
  await createUsers(db);
42692
42652
  await createRoles(db);
42693
42653
  await createCredentials(db);
@@ -42697,7 +42657,7 @@ const up$26 = async (db) => {
42697
42657
  foreign key (owner_app_user_id) references app_users (id) on delete set null
42698
42658
  `.execute(db);
42699
42659
  };
42700
- const down$26 = async (db) => {
42660
+ const down$27 = async (db) => {
42701
42661
  await sql`alter table entries drop constraint if exists entries_owner_app_user_fk`.execute(db);
42702
42662
  await db.schema.dropTable("app_login_codes").execute();
42703
42663
  await db.schema.dropTable("app_password_resets").execute();
@@ -42712,8 +42672,8 @@ const down$26 = async (db) => {
42712
42672
  //#endregion
42713
42673
  //#region src/db/migrations/20261002120000_create_extension_hook_runs.ts
42714
42674
  var _20261002120000_create_extension_hook_runs_exports = /* @__PURE__ */ __exportAll({
42715
- down: () => down$25,
42716
- up: () => up$25
42675
+ down: () => down$26,
42676
+ up: () => up$26
42717
42677
  });
42718
42678
  /**
42719
42679
  * Extension points (package K, ADR 0009): the ledger of post-commit lifecycle hooks (`afterCreate`,
@@ -42721,17 +42681,17 @@ var _20261002120000_create_extension_hook_runs_exports = /* @__PURE__ */ __expor
42721
42681
  * as the hook's own database work, so a hook re-run after a worker crash finds its row and is skipped:
42722
42682
  * exactly once for everything the hook does through its transaction.
42723
42683
  */
42724
- const up$25 = async (db) => {
42684
+ const up$26 = async (db) => {
42725
42685
  await db.schema.createTable("extension_hook_runs").addColumn("event_id", "uuid", (col) => col.notNull()).addColumn("hook", "text", (col) => col.notNull()).addColumn("job_id", "uuid").addColumn("completed_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addPrimaryKeyConstraint("extension_hook_runs_pkey", ["event_id", "hook"]).execute();
42726
42686
  };
42727
- const down$25 = async (db) => {
42687
+ const down$26 = async (db) => {
42728
42688
  await db.schema.dropTable("extension_hook_runs").execute();
42729
42689
  };
42730
42690
  //#endregion
42731
42691
  //#region src/db/migrations/20261002130000_app_auth_token_version_and_login_code_pkce.ts
42732
42692
  var _20261002130000_app_auth_token_version_and_login_code_pkce_exports = /* @__PURE__ */ __exportAll({
42733
- down: () => down$24,
42734
- up: () => up$24
42693
+ down: () => down$25,
42694
+ up: () => up$25
42735
42695
  });
42736
42696
  /**
42737
42697
  * App users (ADR 0005):
@@ -42742,20 +42702,20 @@ var _20261002130000_app_auth_token_version_and_login_code_pkce_exports = /* @__P
42742
42702
  * sign-in; the one-time login code is only exchanged with the matching verifier. Codes live a minute, so
42743
42703
  * any pending ones (issued without a challenge) are dropped rather than given a placeholder.
42744
42704
  */
42745
- const up$24 = async (db) => {
42705
+ const up$25 = async (db) => {
42746
42706
  await db.schema.alterTable("app_users").addColumn("token_version", "integer", (col) => col.notNull().defaultTo(0)).execute();
42747
42707
  await sql`delete from app_login_codes`.execute(db);
42748
42708
  await db.schema.alterTable("app_login_codes").addColumn("code_challenge", "text", (col) => col.notNull()).execute();
42749
42709
  };
42750
- const down$24 = async (db) => {
42710
+ const down$25 = async (db) => {
42751
42711
  await db.schema.alterTable("app_login_codes").dropColumn("code_challenge").execute();
42752
42712
  await db.schema.alterTable("app_users").dropColumn("token_version").execute();
42753
42713
  };
42754
42714
  //#endregion
42755
42715
  //#region src/db/migrations/20261002180000_add_publication_log_diff_indexes.ts
42756
42716
  var _20261002180000_add_publication_log_diff_indexes_exports = /* @__PURE__ */ __exportAll({
42757
- down: () => down$23,
42758
- up: () => up$23
42717
+ down: () => down$24,
42718
+ up: () => up$24
42759
42719
  });
42760
42720
  /**
42761
42721
  * The snapshot diff (repositories/snapshotDiff.ts) finds the (entry, locale) pairs whose live period opened
@@ -42764,7 +42724,7 @@ var _20261002180000_add_publication_log_diff_indexes_exports = /* @__PURE__ */ _
42764
42724
  * - `(entry_id, locale, from_seq)`: "the row live at seq N" is the latest row starting at or before N.
42765
42725
  * Periods that opened in the range use the existing `(model_id, from_seq)` index.
42766
42726
  */
42767
- const up$23 = async (db) => {
42727
+ const up$24 = async (db) => {
42768
42728
  await sql`create index publication_log_to_seq_idx on publication_log (to_seq) where to_seq is not null`.execute(db);
42769
42729
  await db.schema.createIndex("publication_log_entry_locale_from_idx").on("publication_log").columns([
42770
42730
  "entry_id",
@@ -42772,15 +42732,15 @@ const up$23 = async (db) => {
42772
42732
  "from_seq"
42773
42733
  ]).execute();
42774
42734
  };
42775
- const down$23 = async (db) => {
42735
+ const down$24 = async (db) => {
42776
42736
  await db.schema.dropIndex("publication_log_entry_locale_from_idx").execute();
42777
42737
  await db.schema.dropIndex("publication_log_to_seq_idx").execute();
42778
42738
  };
42779
42739
  //#endregion
42780
42740
  //#region src/db/migrations/20261002180100_create_usage_tables.ts
42781
42741
  var _20261002180100_create_usage_tables_exports = /* @__PURE__ */ __exportAll({
42782
- down: () => down$22,
42783
- up: () => up$22
42742
+ down: () => down$23,
42743
+ up: () => up$23
42784
42744
  });
42785
42745
  /**
42786
42746
  * Field usage from delivery traffic (plan developer-face §5): daily counters written by the in-memory
@@ -42788,7 +42748,7 @@ var _20261002180100_create_usage_tables_exports = /* @__PURE__ */ __exportAll({
42788
42748
  * payloads. `principal_key` is `token:<id>`, `app_users` or `anonymous`. Pruned by the retention job
42789
42749
  * after USAGE_RETENTION_DAYS.
42790
42750
  */
42791
- const up$22 = async (db) => {
42751
+ const up$23 = async (db) => {
42792
42752
  await db.schema.createTable("field_reads").addColumn("day", "date", (col) => col.notNull()).addColumn("model_id", "uuid", (col) => col.notNull()).addColumn("field_path", "text", (col) => col.notNull()).addColumn("principal_key", "text", (col) => col.notNull()).addColumn("selection", "text", (col) => col.notNull().check(sql`selection in ('explicit', 'implicit')`)).addColumn("reads", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("last_read_at", "timestamptz", (col) => col.notNull()).addPrimaryKeyConstraint("field_reads_pkey", [
42793
42753
  "day",
42794
42754
  "model_id",
@@ -42799,15 +42759,15 @@ const up$22 = async (db) => {
42799
42759
  await db.schema.createIndex("field_reads_model_day_idx").on("field_reads").columns(["model_id", "day"]).execute();
42800
42760
  await db.schema.createTable("token_reads").addColumn("day", "date", (col) => col.notNull()).addColumn("principal_key", "text", (col) => col.notNull()).addColumn("requests", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("last_snapshot", "bigint").addColumn("last_read_at", "timestamptz", (col) => col.notNull()).addPrimaryKeyConstraint("token_reads_pkey", ["day", "principal_key"]).execute();
42801
42761
  };
42802
- const down$22 = async (db) => {
42762
+ const down$23 = async (db) => {
42803
42763
  await db.schema.dropTable("token_reads").execute();
42804
42764
  await db.schema.dropTable("field_reads").execute();
42805
42765
  };
42806
42766
  //#endregion
42807
42767
  //#region src/db/migrations/20261002180200_create_change_sets.ts
42808
42768
  var _20261002180200_create_change_sets_exports = /* @__PURE__ */ __exportAll({
42809
- down: () => down$21,
42810
- up: () => up$21
42769
+ down: () => down$22,
42770
+ up: () => up$22
42811
42771
  });
42812
42772
  /**
42813
42773
  * Change sets (developer-face plan §5, ADR 0010): schema drafts and entry publications that ship as ONE
@@ -42986,7 +42946,7 @@ const migrateReleases = async (db) => {
42986
42946
  await sql`drop table release_items`.execute(db);
42987
42947
  await sql`drop table releases`.execute(db);
42988
42948
  };
42989
- const up$21 = async (db) => {
42949
+ const up$22 = async (db) => {
42990
42950
  await createTables(db);
42991
42951
  await linkExecutionRecords(db);
42992
42952
  await allowConversionRevisions(db);
@@ -43058,7 +43018,7 @@ const restoreReleases = async (db) => {
43058
43018
  await sql`update webhooks set events = array_remove(array_remove(events, 'change_set.shipping'),
43059
43019
  'change_set.discarded')`.execute(db);
43060
43020
  };
43061
- const down$21 = async (db) => {
43021
+ const down$22 = async (db) => {
43062
43022
  await restoreReleases(db);
43063
43023
  await sql`alter table content_revisions drop constraint content_revisions_reason_check`.execute(db);
43064
43024
  await sql`alter table content_revisions add constraint content_revisions_reason_check
@@ -43079,8 +43039,8 @@ const down$21 = async (db) => {
43079
43039
  //#endregion
43080
43040
  //#region src/db/migrations/20261002190000_create_editor_presence_and_content_health.ts
43081
43041
  var _20261002190000_create_editor_presence_and_content_health_exports = /* @__PURE__ */ __exportAll({
43082
- down: () => down$20,
43083
- up: () => up$20
43042
+ down: () => down$21,
43043
+ up: () => up$21
43084
43044
  });
43085
43045
  /**
43086
43046
  * Editor experience (plan editor-experience §9):
@@ -43091,7 +43051,7 @@ var _20261002190000_create_editor_presence_and_content_health_exports = /* @__PU
43091
43051
  * - `content_health_findings`: open and recently resolved findings of the health rules per entry and
43092
43052
  * locale (`*` for the entry as a whole). An evaluation upserts what it found and resolves the rest.
43093
43053
  */
43094
- const up$20 = async (db) => {
43054
+ const up$21 = async (db) => {
43095
43055
  await sql`
43096
43056
  create unlogged table editor_presence (
43097
43057
  entry_id uuid not null,
@@ -43128,15 +43088,15 @@ const up$20 = async (db) => {
43128
43088
  await sql`create index content_health_findings_resolved_idx on content_health_findings (resolved_at)
43129
43089
  where resolved_at is not null`.execute(db);
43130
43090
  };
43131
- const down$20 = async (db) => {
43091
+ const down$21 = async (db) => {
43132
43092
  await sql`drop table content_health_findings`.execute(db);
43133
43093
  await sql`drop table editor_presence`.execute(db);
43134
43094
  };
43135
43095
  //#endregion
43136
43096
  //#region src/db/migrations/20261003120000_add_changes_ship_permission.ts
43137
43097
  var _20261003120000_add_changes_ship_permission_exports = /* @__PURE__ */ __exportAll({
43138
- down: () => down$19,
43139
- up: () => up$19
43098
+ down: () => down$20,
43099
+ up: () => up$20
43140
43100
  });
43141
43101
  /**
43142
43102
  * `changes.ship` (agentic plan §I): shipping a change set (and scheduling a ship) becomes its own global
@@ -43144,7 +43104,7 @@ var _20261003120000_add_changes_ship_permission_exports = /* @__PURE__ */ __expo
43144
43104
  * live. Every role that holds `changes.manage` today keeps what it could do: it gets `changes.ship` too.
43145
43105
  * The permissions version moves only when grants changed, so running instances reload exactly then.
43146
43106
  */
43147
- const up$19 = async (db) => {
43107
+ const up$20 = async (db) => {
43148
43108
  await sql`
43149
43109
  with granted as (
43150
43110
  insert into admin_role_permissions (role_id, action, model_id, condition, field_ids)
@@ -43158,7 +43118,7 @@ const up$19 = async (db) => {
43158
43118
  where exists (select 1 from granted)
43159
43119
  `.execute(db);
43160
43120
  };
43161
- const down$19 = async (db) => {
43121
+ const down$20 = async (db) => {
43162
43122
  await sql`
43163
43123
  with revoked as (
43164
43124
  delete from admin_role_permissions where action = 'changes.ship' returning 1
@@ -43170,8 +43130,8 @@ const down$19 = async (db) => {
43170
43130
  //#endregion
43171
43131
  //#region src/db/migrations/20261003130000_create_sites.ts
43172
43132
  var _20261003130000_create_sites_exports = /* @__PURE__ */ __exportAll({
43173
- down: () => down$18,
43174
- up: () => up$18
43133
+ down: () => down$19,
43134
+ up: () => up$19
43175
43135
  });
43176
43136
  /**
43177
43137
  * Sites (plan agentic-ecosystem §H, ADR 0011): one instance hosts many sites with one schema and one login.
@@ -43359,7 +43319,7 @@ const siteRoleAssignments = async (db) => {
43359
43319
  `.execute(db);
43360
43320
  await sql`alter table admin_invitations drop column role_ids`.execute(db);
43361
43321
  };
43362
- const up$18 = async (db) => {
43322
+ const up$19 = async (db) => {
43363
43323
  await createSites(db);
43364
43324
  await addSiteColumns(db);
43365
43325
  await tieCopiesToEntries(db);
@@ -43372,7 +43332,7 @@ const assertSingleSite = async (db) => {
43372
43332
  const { rows } = await sql`select count(*)::text as count from sites`.execute(db);
43373
43333
  if (Number(rows[0]?.count ?? 0) > 1) throw new Error("Cannot roll back sites while more than one site exists: merging them would merge their snapshot ledgers and uniqueness and widen role assignments. Delete the other sites first.");
43374
43334
  };
43375
- const down$18 = async (db) => {
43335
+ const down$19 = async (db) => {
43376
43336
  await assertSingleSite(db);
43377
43337
  await sql`alter table admin_invitations add column role_ids uuid[] not null default '{}'::uuid[]`.execute(db);
43378
43338
  await sql`
@@ -43443,8 +43403,8 @@ const down$18 = async (db) => {
43443
43403
  //#endregion
43444
43404
  //#region src/db/migrations/20261003140200_enqueue_field_index_layout.ts
43445
43405
  var _20261003140200_enqueue_field_index_layout_exports = /* @__PURE__ */ __exportAll({
43446
- down: () => down$17,
43447
- up: () => up$17
43406
+ down: () => down$18,
43407
+ up: () => up$18
43448
43408
  });
43449
43409
  /**
43450
43410
  * Field index layout v2 (sites plan §H): per-field expression indexes on `entry_heads` lead with `site_id`,
@@ -43456,7 +43416,7 @@ var _20261003140200_enqueue_field_index_layout_exports = /* @__PURE__ */ __expor
43456
43416
  */
43457
43417
  const JOB_TYPE$1 = "schema.fieldIndexLayout";
43458
43418
  const IDEMPOTENCY_KEY$1 = "schema.fieldIndexLayout:v2";
43459
- const up$17 = async (db) => {
43419
+ const up$18 = async (db) => {
43460
43420
  await sql`
43461
43421
  insert into jobs (type, payload, max_attempts, idempotency_key)
43462
43422
  select ${JOB_TYPE$1}, '{}'::jsonb, 5, ${IDEMPOTENCY_KEY$1}
@@ -43469,14 +43429,14 @@ const up$17 = async (db) => {
43469
43429
  `.execute(db);
43470
43430
  };
43471
43431
  /** The layout job is idempotent; rolling back only forgets it if it has not run yet. */
43472
- const down$17 = async (db) => {
43432
+ const down$18 = async (db) => {
43473
43433
  await sql`delete from jobs where idempotency_key = ${IDEMPOTENCY_KEY$1} and status = 'pending'`.execute(db);
43474
43434
  };
43475
43435
  //#endregion
43476
43436
  //#region src/db/migrations/20261003140500_change_set_items_site_and_preview_entry.ts
43477
43437
  var _20261003140500_change_set_items_site_and_preview_entry_exports = /* @__PURE__ */ __exportAll({
43478
- down: () => down$16,
43479
- up: () => up$16
43438
+ down: () => down$17,
43439
+ up: () => up$17
43480
43440
  });
43481
43441
  /**
43482
43442
  * Sites, publishing (plan agentic-ecosystem §H, package G5).
@@ -43487,7 +43447,7 @@ var _20261003140500_change_set_items_site_and_preview_entry_exports = /* @__PURE
43487
43447
  * - `preview_tokens.entry_id` becomes required: a preview token previews one entry of one site. Model-wide
43488
43448
  * tokens (short-lived by design) are deleted; whoever needs one opens the entry's preview again.
43489
43449
  */
43490
- const up$16 = async (db) => {
43450
+ const up$17 = async (db) => {
43491
43451
  await sql`alter table change_sets add constraint change_sets_id_site_uq unique (id, site_id)`.execute(db);
43492
43452
  await sql`alter table change_set_items add column site_id uuid`.execute(db);
43493
43453
  await sql`
@@ -43503,7 +43463,7 @@ const up$16 = async (db) => {
43503
43463
  await sql`delete from preview_tokens where entry_id is null`.execute(db);
43504
43464
  await sql`alter table preview_tokens alter column entry_id set not null`.execute(db);
43505
43465
  };
43506
- const down$16 = async (db) => {
43466
+ const down$17 = async (db) => {
43507
43467
  await sql`alter table preview_tokens alter column entry_id drop not null`.execute(db);
43508
43468
  await sql`alter table change_set_items drop constraint change_set_items_entry_site_fk`.execute(db);
43509
43469
  await sql`alter table change_set_items drop constraint change_set_items_set_site_fk`.execute(db);
@@ -43513,8 +43473,8 @@ const down$16 = async (db) => {
43513
43473
  //#endregion
43514
43474
  //#region src/db/migrations/20261003150000_widen_deployment_providers.ts
43515
43475
  var _20261003150000_widen_deployment_providers_exports = /* @__PURE__ */ __exportAll({
43516
- down: () => down$15,
43517
- up: () => up$15
43476
+ down: () => down$16,
43477
+ up: () => up$16
43518
43478
  });
43519
43479
  /**
43520
43480
  * Deploy adapters (plan agentic-ecosystem §B2): deployment connections may use the Vercel and Netlify
@@ -43529,11 +43489,11 @@ const replaceCheck = async (db, check) => {
43529
43489
  check (${check}) not valid`.execute(db);
43530
43490
  await sql`alter table deployment_connections validate constraint deployment_connections_provider_check`.execute(db);
43531
43491
  };
43532
- const up$15 = async (db) => {
43492
+ const up$16 = async (db) => {
43533
43493
  await replaceCheck(db, WIDENED);
43534
43494
  };
43535
43495
  /** Refuses while Vercel or Netlify connections exist: a down migration never deletes user data. */
43536
- const down$15 = async (db) => {
43496
+ const down$16 = async (db) => {
43537
43497
  const { rows } = await sql`
43538
43498
  select count(*)::text as count from deployment_connections where provider in ('vercel', 'netlify')
43539
43499
  `.execute(db);
@@ -43544,8 +43504,8 @@ const down$15 = async (db) => {
43544
43504
  //#endregion
43545
43505
  //#region src/db/migrations/20261003160000_create_assist_runs.ts
43546
43506
  var _20261003160000_create_assist_runs_exports = /* @__PURE__ */ __exportAll({
43547
- down: () => down$14,
43548
- up: () => up$14
43507
+ down: () => down$15,
43508
+ up: () => up$15
43549
43509
  });
43550
43510
  /**
43551
43511
  * Editor assists (plan agentic-ecosystem §A0, §I): one `assist_runs` row per assist request (who, which
@@ -43561,14 +43521,14 @@ const replaceSourceCheck = async (db, check) => {
43561
43521
  await sql`alter table change_sets add constraint change_sets_source_check check (${check}) not valid`.execute(db);
43562
43522
  await sql`alter table change_sets validate constraint change_sets_source_check`.execute(db);
43563
43523
  };
43564
- const up$14 = async (db) => {
43524
+ const up$15 = async (db) => {
43565
43525
  await db.schema.createTable("assist_runs").addColumn("id", "uuid", (col) => col.primaryKey().defaultTo(sql`gen_random_uuid()`)).addColumn("site_id", "uuid", (col) => col.notNull().references("sites.id").onDelete("cascade")).addColumn("actor_type", "text", (col) => col.notNull()).addColumn("actor_id", "text", (col) => col.notNull()).addColumn("action", "text", (col) => col.notNull().check(ACTIONS)).addColumn("rule", "text").addColumn("provider", "text", (col) => col.notNull()).addColumn("model", "text", (col) => col.notNull()).addColumn("input_tokens", "integer", (col) => col.notNull().defaultTo(0)).addColumn("output_tokens", "integer", (col) => col.notNull().defaultTo(0)).addColumn("duration_ms", "integer", (col) => col.notNull().defaultTo(0)).addColumn("status", "text", (col) => col.notNull().check(sql`status in ('queued', 'running', 'succeeded', 'failed')`)).addColumn("error_code", "text").addColumn("job_id", "uuid", (col) => col.references("jobs.id").onDelete("set null")).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`)).addColumn("finished_at", "timestamptz").execute();
43566
43526
  await db.schema.createIndex("assist_runs_site_created_idx").on("assist_runs").columns(["site_id", "created_at"]).execute();
43567
43527
  await db.schema.createIndex("assist_runs_created_idx").on("assist_runs").column("created_at").execute();
43568
43528
  await replaceSourceCheck(db, SOURCES_WIDENED);
43569
43529
  };
43570
43530
  /** Refuses while change sets opened by assist exist: a down migration never deletes user data. */
43571
- const down$14 = async (db) => {
43531
+ const down$15 = async (db) => {
43572
43532
  const { rows } = await sql`
43573
43533
  select count(*)::text as count from change_sets where source = 'assist'
43574
43534
  `.execute(db);
@@ -43580,8 +43540,8 @@ const down$14 = async (db) => {
43580
43540
  //#endregion
43581
43541
  //#region src/db/migrations/20261003170000_audit_events_seq.ts
43582
43542
  var _20261003170000_audit_events_seq_exports$2 = /* @__PURE__ */ __exportAll({
43583
- down: () => down$13,
43584
- up: () => up$13
43543
+ down: () => down$14,
43544
+ up: () => up$14
43585
43545
  });
43586
43546
  /**
43587
43547
  * `audit_events.seq`: a monotonic tiebreaker after `occurred_at`, so events written in the same instant (one
@@ -43590,17 +43550,17 @@ var _20261003170000_audit_events_seq_exports$2 = /* @__PURE__ */ __exportAll({
43590
43550
  * are numbered in the order the rewrite reads them. No index: the audit queries filter and sort by
43591
43551
  * `occurred_at` first, and `seq` only breaks ties.
43592
43552
  */
43593
- const up$13 = async (db) => {
43553
+ const up$14 = async (db) => {
43594
43554
  await db.schema.alterTable("audit_events").addColumn("seq", "bigint", (col) => col.notNull().generatedAlwaysAsIdentity()).execute();
43595
43555
  };
43596
- const down$13 = async (db) => {
43556
+ const down$14 = async (db) => {
43597
43557
  await db.schema.alterTable("audit_events").dropColumn("seq").execute();
43598
43558
  };
43599
43559
  //#endregion
43600
43560
  //#region src/db/migrations/20261004120000_models_site_scope.ts
43601
43561
  var _20261004120000_models_site_scope_exports$2 = /* @__PURE__ */ __exportAll({
43602
- down: () => down$12,
43603
- up: () => up$12
43562
+ down: () => down$13,
43563
+ up: () => up$13
43604
43564
  });
43605
43565
  /**
43606
43566
  * Per-site schema (plan site-schema): a definition belongs to one site (`models.site_id`) or is shared by every
@@ -43613,7 +43573,7 @@ var _20261004120000_models_site_scope_exports$2 = /* @__PURE__ */ __exportAll({
43613
43573
  * over `lower(api_key)` cannot hold. Share or delete them first; then `down` is lossless.
43614
43574
  */
43615
43575
  const NIL_UUID$1 = "00000000-0000-0000-0000-000000000000";
43616
- const up$12 = async (db) => {
43576
+ const up$13 = async (db) => {
43617
43577
  await sql`alter table models add column site_id uuid references sites (id)`.execute(db);
43618
43578
  await sql`create index models_site_idx on models (site_id)`.execute(db);
43619
43579
  await sql`drop index models_api_key_uq`.execute(db);
@@ -43623,7 +43583,7 @@ const up$12 = async (db) => {
43623
43583
  `.execute(db);
43624
43584
  await sql`alter table schema_drafts add column shared boolean not null default false`.execute(db);
43625
43585
  };
43626
- const down$12 = async (db) => {
43586
+ const down$13 = async (db) => {
43627
43587
  const { rows } = await sql`select count(*)::text as count from models where site_id is not null`.execute(db);
43628
43588
  if (Number(rows[0]?.count ?? 0) > 0) throw new Error("Cannot roll back per-site schema while definitions belong to a site: two sites may share an API ID. Share or delete the site definitions first.");
43629
43589
  await sql`alter table schema_drafts drop column shared`.execute(db);
@@ -43635,8 +43595,8 @@ const down$12 = async (db) => {
43635
43595
  //#endregion
43636
43596
  //#region src/db/migrations/20261004130000_site_seo_settings.ts
43637
43597
  var _20261004130000_site_seo_settings_exports$2 = /* @__PURE__ */ __exportAll({
43638
- down: () => down$11,
43639
- up: () => up$11
43598
+ down: () => down$12,
43599
+ up: () => up$12
43640
43600
  });
43641
43601
  /**
43642
43602
  * SEO defaults per site (plan seo-fields): `sites.seo_defaults` holds the site's default texts per locale, its
@@ -43647,7 +43607,7 @@ var _20261004130000_site_seo_settings_exports$2 = /* @__PURE__ */ __exportAll({
43647
43607
  * about what a site publishes) gets it; the built-in owner and admin roles get it from `ensureSystemRoles`.
43648
43608
  * The permissions version moves only when grants changed, so running instances reload exactly then.
43649
43609
  */
43650
- const up$11 = async (db) => {
43610
+ const up$12 = async (db) => {
43651
43611
  await sql`alter table sites add column seo_defaults jsonb`.execute(db);
43652
43612
  await sql`
43653
43613
  with granted as (
@@ -43662,7 +43622,7 @@ const up$11 = async (db) => {
43662
43622
  where exists (select 1 from granted)
43663
43623
  `.execute(db);
43664
43624
  };
43665
- const down$11 = async (db) => {
43625
+ const down$12 = async (db) => {
43666
43626
  await sql`
43667
43627
  with revoked as (
43668
43628
  delete from admin_role_permissions where action = 'site.settings' returning 1
@@ -43675,8 +43635,8 @@ const down$11 = async (db) => {
43675
43635
  //#endregion
43676
43636
  //#region src/db/migrations/20261005120000_entry_heads_entry_created_at.ts
43677
43637
  var _20261005120000_entry_heads_entry_created_at_exports$2 = /* @__PURE__ */ __exportAll({
43678
- down: () => down$10,
43679
- up: () => up$10
43638
+ down: () => down$11,
43639
+ up: () => up$11
43680
43640
  });
43681
43641
  /**
43682
43642
  * Delivery perf 2 (ADR 0001 amendment): the entry's `created_at` on each head (`entry_created_at`), so a list
@@ -43693,7 +43653,7 @@ const INDEX$2 = "entry_heads_entry_order_idx";
43693
43653
  const JOB_TYPE = "schema.entryOrderIndex";
43694
43654
  const IDEMPOTENCY_KEY = "schema.entryOrderIndex:v1";
43695
43655
  const BATCH$1 = 5e3;
43696
- const up$10 = async (db) => {
43656
+ const up$11 = async (db) => {
43697
43657
  await sql`alter table entry_heads add column entry_created_at timestamptz`.execute(db);
43698
43658
  for (;;) {
43699
43659
  const result = await sql`
@@ -43718,12 +43678,25 @@ const up$10 = async (db) => {
43718
43678
  create index ${sql.id(INDEX$2)} on entry_heads (site_id, model_id, state, entry_created_at desc, entry_id)
43719
43679
  `.execute(db);
43720
43680
  };
43721
- const down$10 = async (db) => {
43681
+ const down$11 = async (db) => {
43722
43682
  await sql`delete from jobs where idempotency_key = ${IDEMPOTENCY_KEY} and status = 'pending'`.execute(db);
43723
43683
  await sql`drop index if exists ${sql.id(INDEX$2)}`.execute(db);
43724
43684
  await sql`alter table entry_heads drop column entry_created_at`.execute(db);
43725
43685
  };
43726
43686
  //#endregion
43687
+ //#region src/db/migrations/20261006120000_system_versions_release.ts
43688
+ var _20261006120000_system_versions_release_exports = /* @__PURE__ */ __exportAll({
43689
+ dialectNeutral: () => true,
43690
+ down: () => down$10,
43691
+ up: () => up$10
43692
+ });
43693
+ const up$10 = async (db) => {
43694
+ await db.schema.alterTable("system_versions").addColumn("release", "text").execute();
43695
+ };
43696
+ const down$10 = async (db) => {
43697
+ await db.schema.alterTable("system_versions").dropColumn("release").execute();
43698
+ };
43699
+ //#endregion
43727
43700
  //#region src/db/migrations/mysql/0001_baseline.ts
43728
43701
  var _0001_baseline_exports$1 = /* @__PURE__ */ __exportAll({
43729
43702
  down: () => down$9,
@@ -45025,7 +44998,8 @@ const MYSQL_MIGRATIONS = {
45025
44998
  "20261003170000_audit_events_seq": _20261003170000_audit_events_seq_exports$1,
45026
44999
  "20261004120000_models_site_scope": _20261004120000_models_site_scope_exports$1,
45027
45000
  "20261004130000_site_seo_settings": _20261004130000_site_seo_settings_exports$1,
45028
- "20261005120000_entry_heads_entry_created_at": _20261005120000_entry_heads_entry_created_at_exports$1
45001
+ "20261005120000_entry_heads_entry_created_at": _20261005120000_entry_heads_entry_created_at_exports$1,
45002
+ "20261006120000_system_versions_release": _20261006120000_system_versions_release_exports
45029
45003
  };
45030
45004
  //#endregion
45031
45005
  //#region src/db/migrations/sqlite/0001_baseline.ts
@@ -46348,7 +46322,8 @@ const SQLITE_MIGRATIONS = {
46348
46322
  "20261003170000_audit_events_seq": _20261003170000_audit_events_seq_exports,
46349
46323
  "20261004120000_models_site_scope": _20261004120000_models_site_scope_exports,
46350
46324
  "20261004130000_site_seo_settings": _20261004130000_site_seo_settings_exports,
46351
- "20261005120000_entry_heads_entry_created_at": _20261005120000_entry_heads_entry_created_at_exports
46325
+ "20261005120000_entry_heads_entry_created_at": _20261005120000_entry_heads_entry_created_at_exports,
46326
+ "20261006120000_system_versions_release": _20261006120000_system_versions_release_exports
46352
46327
  };
46353
46328
  //#endregion
46354
46329
  //#region src/db/migrations/index.ts
@@ -46381,7 +46356,8 @@ const MIGRATIONS = {
46381
46356
  "20261003170000_audit_events_seq": _20261003170000_audit_events_seq_exports$2,
46382
46357
  "20261004120000_models_site_scope": _20261004120000_models_site_scope_exports$2,
46383
46358
  "20261004130000_site_seo_settings": _20261004130000_site_seo_settings_exports$2,
46384
- "20261005120000_entry_heads_entry_created_at": _20261005120000_entry_heads_entry_created_at_exports$2
46359
+ "20261005120000_entry_heads_entry_created_at": _20261005120000_entry_heads_entry_created_at_exports$2,
46360
+ "20261006120000_system_versions_release": _20261006120000_system_versions_release_exports
46385
46361
  };
46386
46362
  const PROVIDERS = {
46387
46363
  postgres: { getMigrations: () => Promise.resolve({ ...MIGRATIONS }) },
@@ -46684,6 +46660,24 @@ const parseByteRange = (header, size) => {
46684
46660
  };
46685
46661
  };
46686
46662
  //#endregion
46663
+ //#region src/media/contentDisposition.ts
46664
+ /**
46665
+ * A Content-Disposition value that names the file safely: an ASCII fallback plus the RFC 5987 UTF-8 form,
46666
+ * so quotes, semicolons or line breaks in a filename can never inject header parameters.
46667
+ */
46668
+ const contentDisposition = (filename, type = "inline") => {
46669
+ return `${type}; filename="${filename.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_")}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
46670
+ };
46671
+ /** Types a browser only ever displays passively: opening one directly cannot run script. */
46672
+ const PASSIVE_INLINE_TYPE = /^(image\/(png|jpeg|gif|webp|avif|bmp|tiff|x-icon|vnd\.microsoft\.icon)|video\/.+|audio\/.+)$/;
46673
+ /**
46674
+ * Content-Disposition to store on an object in a bucket. Public objects can be served straight from the
46675
+ * bucket or a CDN, where Shapio's sandboxing headers are absent; anything that could run as a page there
46676
+ * (SVG, HTML, XML, PDF...) is stored as an attachment, so opening it downloads it. `<img>` tags still show
46677
+ * an SVG, since embedding ignores Content-Disposition.
46678
+ */
46679
+ const bucketContentDisposition = (contentType) => PASSIVE_INLINE_TYPE.test(contentType.toLowerCase()) ? void 0 : "attachment";
46680
+ //#endregion
46687
46681
  //#region src/services/mediaFiles.ts
46688
46682
  const notFound = () => new AppError(404, "NOT_FOUND", "Media file not found");
46689
46683
  const findFile = async (key) => {
@@ -47111,6 +47105,16 @@ const insertIfAbsent = async (key, value, trx = db) => {
47111
47105
  value
47112
47106
  }).onConflict((oc) => oc.column("key").doNothing()).returning("key").executeTakeFirst() !== void 0;
47113
47107
  };
47108
+ /** Creates or replaces a setting. */
47109
+ const upsertValue = async (key, value, trx = db) => {
47110
+ await trx.insertInto("system_settings").values({
47111
+ key,
47112
+ value
47113
+ }).onConflict((oc) => oc.column("key").doUpdateSet({
47114
+ value,
47115
+ updated_at: /* @__PURE__ */ new Date()
47116
+ })).execute();
47117
+ };
47114
47118
  //#endregion
47115
47119
  //#region src/services/signingSecret.ts
47116
47120
  const SIGNING_SECRET_SETTING = "signing_secret";
@@ -47616,20 +47620,6 @@ var StartupCheckError = class extends Error {
47616
47620
  this.name = "StartupCheckError";
47617
47621
  }
47618
47622
  };
47619
- /** `host:port/database` (PostgreSQL, MySQL) or the file path (SQLite) from DATABASE_URL, never the credentials. */
47620
- const describeDatabaseTarget = (connectionString) => {
47621
- if (dialectOfUrl(connectionString) === "sqlite") {
47622
- const location = sqliteLocationOfUrl(connectionString);
47623
- return location.kind === "memory" ? "an in-memory database" : location.path;
47624
- }
47625
- try {
47626
- const url = new URL(connectionString);
47627
- const defaultPort = dialectOfUrl(connectionString) === "mysql" ? "3306" : "5432";
47628
- return `${url.hostname}:${url.port || defaultPort}${url.pathname}`;
47629
- } catch {
47630
- return "the configured database";
47631
- }
47632
- };
47633
47623
  const NETWORK_REASONS = {
47634
47624
  ECONNREFUSED: "connection refused",
47635
47625
  ENOTFOUND: "host not found",
@@ -49127,6 +49117,45 @@ const createPublishingJobHandlers = (environment) => [
49127
49117
  ];
49128
49118
  /** Outbox subscribers: webhook deliveries and deployment triggers follow domain events (ADR 0007). */
49129
49119
  const PUBLISHING_OUTBOX_SUBSCRIBERS = [webhookOutboxSubscriber, deploymentOutboxSubscriber];
49120
+ Type.Object({
49121
+ format: Type.Literal(1),
49122
+ publicUrl: Type.String(),
49123
+ basePath: Type.String(),
49124
+ media: Type.Object({
49125
+ publicBaseUrl: Type.Union([Type.String(), Type.Null()]),
49126
+ s3: Type.Union([Type.Object({
49127
+ bucket: Type.String(),
49128
+ region: Type.Union([Type.String(), Type.Null()]),
49129
+ endpoint: Type.Union([Type.String(), Type.Null()]),
49130
+ forcePathStyle: Type.Boolean()
49131
+ }), Type.Null()])
49132
+ })
49133
+ });
49134
+ const toDeliveryDescriptor = ({ server, storage }) => ({
49135
+ format: 1,
49136
+ publicUrl: server.publicUrl,
49137
+ basePath: server.basePath,
49138
+ media: {
49139
+ publicBaseUrl: storage.publicBaseUrl ?? null,
49140
+ s3: storage.s3 ? {
49141
+ bucket: storage.s3.bucket,
49142
+ region: storage.s3.region ?? null,
49143
+ endpoint: storage.s3.endpoint ?? null,
49144
+ forcePathStyle: storage.s3.forcePathStyle
49145
+ } : null
49146
+ }
49147
+ });
49148
+ /**
49149
+ * Called by the server at every start, after migrating: records its release and its delivery descriptor
49150
+ * (rewritten only when it changed), in one transaction.
49151
+ */
49152
+ const publishServerRelease = async (database, release, source) => {
49153
+ const descriptor = JSON.stringify(toDeliveryDescriptor(source));
49154
+ await database.transaction().execute(async (trx) => {
49155
+ await setRelease(release, trx);
49156
+ if (await findValue("delivery_descriptor", trx) !== descriptor) await upsertValue(DELIVERY_DESCRIPTOR_SETTING, descriptor, trx);
49157
+ });
49158
+ };
49130
49159
  //#endregion
49131
49160
  //#region src/tls/certificates.ts
49132
49161
  /**
@@ -49287,7 +49316,7 @@ const logStartupSummary = (config, logger) => {
49287
49316
  };
49288
49317
  logger.info(summary, `Shapio ${summary.version} running at ${summary.url} (mode ${summary.mode}, storage ${summary.storage}, worker ${summary.worker}, tls ${summary.tls})`);
49289
49318
  };
49290
- const createConfiguredWorker = (config, db, logger, signingSecret, extensions) => {
49319
+ const createConfiguredWorker = async (config, db, logger, signingSecret, extensions) => {
49291
49320
  const publishing = createPublishingJobEnvironment(createPublishingRuntime({
49292
49321
  db,
49293
49322
  signingSecret,
@@ -49300,7 +49329,7 @@ const createConfiguredWorker = (config, db, logger, signingSecret, extensions) =
49300
49329
  urls: createUrlBuilder(config.server),
49301
49330
  transport: createEmailTransport(config.email, logger.child({ component: "email" }))
49302
49331
  };
49303
- const storage = createMediaStorage(config.storage, { urls: createUrlBuilder(config.server) });
49332
+ const storage = await createMediaStorage(config.storage, { urls: createUrlBuilder(config.server) });
49304
49333
  return createWorker({
49305
49334
  db,
49306
49335
  handlers: createJobHandlers([
@@ -49381,6 +49410,7 @@ const startServer = async (config) => {
49381
49410
  log: logger
49382
49411
  });
49383
49412
  const signingSecret = await resolveSigningSecret(db, config.sessionSecret, logger);
49413
+ await publishServerRelease(db, SHAPIO_VERSION, config);
49384
49414
  extensions = await startExtensions(config, db, logger, project);
49385
49415
  const app = await buildApp(config, {
49386
49416
  db,
@@ -49390,7 +49420,7 @@ const startServer = async (config) => {
49390
49420
  schemaListen: config.schema.listen,
49391
49421
  ...tls.initial ? { tls: tls.initial } : {}
49392
49422
  });
49393
- const worker = config.worker.mode === "inline" ? createConfiguredWorker(config, db, logger, signingSecret, extensions) : void 0;
49423
+ const worker = config.worker.mode === "inline" ? await createConfiguredWorker(config, db, logger, signingSecret, extensions) : void 0;
49394
49424
  const tlsRuntime = tls;
49395
49425
  const extensionRuntime = extensions;
49396
49426
  let closing;
@@ -49464,7 +49494,7 @@ const startDedicatedWorker = async (config) => {
49464
49494
  }
49465
49495
  const signingSecret = await resolveSigningSecret(db, config.sessionSecret, logger);
49466
49496
  const extensions = await startExtensions(config, db, logger, project);
49467
- const worker = createConfiguredWorker(config, db, logger, signingSecret, extensions);
49497
+ const worker = await createConfiguredWorker(config, db, logger, signingSecret, extensions);
49468
49498
  await ensureRetentionScheduled(db);
49469
49499
  await ensureHealthSweepScheduled(db);
49470
49500
  worker.start();
@@ -49478,6 +49508,6 @@ const startDedicatedWorker = async (config) => {
49478
49508
  return worker;
49479
49509
  };
49480
49510
  //#endregion
49481
- export { toTypeName as $, update$8 as A, checkThemeContrast as B, BEFORE_EVENTS as C, listForAssets as D, buildEditorManifest as E, RESERVED_FIELD_API_KEYS as F, mergePulledLock as G, hashDefinition as H, RESERVED_TYPE_NAMES as I, schemaFilePath as J, parseDefinition as K, SCHEMA_KIND_DIRECTORIES as L, describeError as M, LOCK_FILE_PATH as N, listLiveByDriver as O, RESERVED_DEFINITION_API_KEYS as P, suggestPlural as Q, SITES_DIRECTORY as R, AFTER_EVENTS as S, EXTENSION_ROUTE_ROOT as T, lockCoversSite as U, foldApiKey as V, lockEntriesForSite as W, serializeDefinition as X, scopeDirectory as Y, serializeLockFile as Z, createLogger as _, loadConfig as a, createStableId as at, SITE_KEY_PATTERN as b, getPendingMigrations as c, sqliteDriverOf as ct, createMediaStorage as d, upgradeLockFile as et, createSite$1 as f, generateToken as g, SYSTEM_CLI_ACTOR as h, assertDatabaseReachable as i, SAFE_LINK_PATTERN as it, STORAGE_DRIVERS$1 as j, lockById$5 as k, migrateToLatest as l, createAdminDirect as m, startServer as n, withSiteCovered as nt, loadEnvFileIfPresent as o, validateRichText as ot, listAllSites as p, parseLockFile as q, copyObjectVerified as r, RICHTEXT_FORMAT as rt, buildApp as s, createDb as st, startDedicatedWorker as t, validateSchema as tt, SHAPIO_VERSION as u, createUrlBuilder as v, EXTENSION_JOB_PREFIX as w, loadProjectConfig as x, loopbackUrl as y, THEME_VARIANTS as z };
49511
+ export { parseLockFile as $, buildEditorManifest as A, RESERVED_DEFINITION_API_KEYS as B, loopbackUrl as C, BEFORE_EVENTS as D, AFTER_EVENTS as E, lockById$5 as F, THEME_VARIANTS as G, RESERVED_TYPE_NAMES as H, update$8 as I, hashDefinition as J, checkThemeContrast as K, STORAGE_DRIVERS$1 as L, isValidStorageKey as M, listForAssets as N, EXTENSION_JOB_PREFIX as O, listLiveByDriver as P, parseDefinition as Q, describeError as R, createUrlBuilder as S, loadProjectConfig as T, SCHEMA_KIND_DIRECTORIES as U, RESERVED_FIELD_API_KEYS as V, SITES_DIRECTORY as W, lockEntriesForSite as X, lockCoversSite as Y, mergePulledLock as Z, listAllSites as _, loadConfig as a, toTypeName as at, generateToken as b, bucketContentDisposition as c, withSiteCovered as ct, migrateToLatest as d, createStableId as dt, schemaFilePath as et, SHAPIO_VERSION as f, validateRichText as ft, createSite$1 as g, ObjectNotFoundError as h, assertDatabaseReachable as i, suggestPlural as it, encodeKeyPath as j, EXTENSION_ROUTE_ROOT as k, contentDisposition as l, RICHTEXT_FORMAT as lt, InvalidStorageKeyError as m, sqliteDriverOf as mt, startServer as n, serializeDefinition as nt, loadEnvFileIfPresent as o, upgradeLockFile as ot, createMediaStorage as p, createDb as pt, foldApiKey as q, copyObjectVerified as r, serializeLockFile as rt, buildApp as s, validateSchema as st, startDedicatedWorker as t, scopeDirectory as tt, getPendingMigrations as u, SAFE_LINK_PATTERN as ut, createAdminDirect as v, SITE_KEY_PATTERN as w, createLogger as x, SYSTEM_CLI_ACTOR as y, LOCK_FILE_PATH as z };
49482
49512
 
49483
- //# sourceMappingURL=worker-BDYDbaea.js.map
49513
+ //# sourceMappingURL=worker-jz_0yAmS.js.map