@xleddyl/nuxt-cms 0.9.3 → 0.10.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 (433) hide show
  1. package/README.md +1 -1
  2. package/dist/module.d.mts +20 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +1037 -148
  5. package/dist/runtime/app/blocks/CmsBlocks.d.vue.ts +1 -1
  6. package/dist/runtime/app/blocks/CmsBlocks.vue +57 -9
  7. package/dist/runtime/app/blocks/CmsBlocks.vue.d.ts +1 -1
  8. package/dist/runtime/app/blocks/CmsField.d.ts +24 -0
  9. package/dist/runtime/app/blocks/CmsField.js +16 -0
  10. package/dist/runtime/app/blocks/node.d.ts +37 -0
  11. package/dist/runtime/app/blocks/node.js +17 -0
  12. package/dist/runtime/app/blocks/resolve.d.ts +18 -0
  13. package/dist/runtime/app/blocks/resolve.js +34 -0
  14. package/dist/runtime/app/components/cms/BlockPicker.d.vue.ts +22 -0
  15. package/dist/runtime/app/components/cms/BlockPicker.vue +87 -0
  16. package/dist/runtime/app/components/cms/BlockPicker.vue.d.ts +22 -0
  17. package/dist/runtime/app/components/cms/BlockPickerPopover.d.vue.ts +38 -0
  18. package/dist/runtime/app/components/cms/BlockPickerPopover.vue +44 -0
  19. package/dist/runtime/app/components/cms/BlockPickerPopover.vue.d.ts +38 -0
  20. package/dist/runtime/app/components/cms/BlockTree.d.vue.ts +29 -0
  21. package/dist/runtime/app/components/cms/BlockTree.vue +322 -0
  22. package/dist/runtime/app/components/cms/BlockTree.vue.d.ts +29 -0
  23. package/dist/runtime/app/components/cms/BlocksField.vue +569 -218
  24. package/dist/runtime/app/components/cms/BrandMark.vue +7 -13
  25. package/dist/runtime/app/components/cms/ConfirmGroups.vue +15 -13
  26. package/dist/runtime/app/components/cms/{BlockPalette.vue.d.ts → EditorHistory.d.vue.ts} +6 -5
  27. package/dist/runtime/app/components/cms/EditorHistory.vue +34 -0
  28. package/dist/runtime/app/components/cms/{BlockPalette.d.vue.ts → EditorHistory.vue.d.ts} +6 -5
  29. package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +11 -5
  30. package/dist/runtime/app/components/cms/EntryDrawer.vue +183 -152
  31. package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +11 -5
  32. package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +16 -4
  33. package/dist/runtime/app/components/cms/EntryForm.vue +106 -146
  34. package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +16 -4
  35. package/dist/runtime/app/components/cms/FieldInput.vue +10 -2
  36. package/dist/runtime/app/components/cms/FormSections.d.vue.ts +22 -0
  37. package/dist/runtime/app/components/cms/FormSections.vue +71 -0
  38. package/dist/runtime/app/components/cms/FormSections.vue.d.ts +22 -0
  39. package/dist/runtime/app/components/cms/ImportModal.vue +46 -58
  40. package/dist/runtime/app/components/cms/LinkField.d.vue.ts +16 -0
  41. package/dist/runtime/app/components/cms/LinkField.vue +250 -0
  42. package/dist/runtime/app/components/cms/LinkField.vue.d.ts +16 -0
  43. package/dist/runtime/app/components/cms/ListFilters.vue +10 -12
  44. package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +2 -1
  45. package/dist/runtime/app/components/cms/LocaleSwitch.vue +29 -5
  46. package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +2 -1
  47. package/dist/runtime/app/components/cms/MediaDetail.d.vue.ts +2 -0
  48. package/dist/runtime/app/components/cms/MediaDetail.vue +112 -83
  49. package/dist/runtime/app/components/cms/MediaDetail.vue.d.ts +2 -0
  50. package/dist/runtime/app/components/cms/MediaField.vue +35 -25
  51. package/dist/runtime/app/components/cms/MediaFolderPicker.vue +26 -71
  52. package/dist/runtime/app/components/cms/MediaGallery.d.vue.ts +1 -0
  53. package/dist/runtime/app/components/cms/MediaGallery.vue +442 -419
  54. package/dist/runtime/app/components/cms/MediaGallery.vue.d.ts +1 -0
  55. package/dist/runtime/app/components/cms/MediaThumb.vue +9 -25
  56. package/dist/runtime/app/components/cms/MediaUpload.vue +1 -1
  57. package/dist/runtime/app/components/cms/{BlockOutline.d.vue.ts → NestedBlockList.d.vue.ts} +5 -5
  58. package/dist/runtime/app/components/cms/NestedBlockList.vue +87 -0
  59. package/dist/runtime/app/components/cms/{BlockOutline.vue.d.ts → NestedBlockList.vue.d.ts} +5 -5
  60. package/dist/runtime/app/components/cms/PoweredBy.vue +12 -5
  61. package/dist/runtime/app/components/cms/PresetModal.d.vue.ts +18 -0
  62. package/dist/runtime/app/components/cms/PresetModal.vue +70 -0
  63. package/dist/runtime/app/components/cms/PresetModal.vue.d.ts +18 -0
  64. package/dist/runtime/app/components/cms/RelationField.vue +2 -8
  65. package/dist/runtime/app/components/cms/SettingsAccount.vue +60 -71
  66. package/dist/runtime/app/components/cms/SettingsColumns.vue +47 -69
  67. package/dist/runtime/app/components/cms/SettingsEmptyAdd.d.vue.ts +13 -0
  68. package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue +21 -0
  69. package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue.d.ts +13 -0
  70. package/dist/runtime/app/components/cms/SettingsEntries.d.vue.ts +13 -0
  71. package/dist/runtime/app/components/cms/SettingsEntries.vue +50 -0
  72. package/dist/runtime/app/components/cms/SettingsEntries.vue.d.ts +13 -0
  73. package/dist/runtime/app/components/cms/SettingsEntry.d.vue.ts +1 -10
  74. package/dist/runtime/app/components/cms/SettingsEntry.vue +11 -95
  75. package/dist/runtime/app/components/cms/SettingsEntry.vue.d.ts +1 -10
  76. package/dist/runtime/app/components/cms/SettingsEntryActions.d.vue.ts +7 -0
  77. package/dist/runtime/app/components/cms/SettingsEntryActions.vue +42 -0
  78. package/dist/runtime/app/components/cms/SettingsEntryActions.vue.d.ts +7 -0
  79. package/dist/runtime/app/components/cms/SettingsEntryTabs.d.vue.ts +7 -0
  80. package/dist/runtime/app/components/cms/SettingsEntryTabs.vue +21 -0
  81. package/dist/runtime/app/components/cms/SettingsEntryTabs.vue.d.ts +7 -0
  82. package/dist/runtime/app/components/cms/SettingsFormAction.d.vue.ts +3 -0
  83. package/dist/runtime/app/components/cms/SettingsFormAction.vue +17 -0
  84. package/dist/runtime/app/components/cms/SettingsFormAction.vue.d.ts +3 -0
  85. package/dist/runtime/app/components/cms/SettingsLayout.vue +18 -14
  86. package/dist/runtime/app/components/cms/SettingsPresetForm.d.vue.ts +11 -0
  87. package/dist/runtime/app/components/cms/SettingsPresetForm.vue +60 -0
  88. package/dist/runtime/app/components/cms/SettingsPresetForm.vue.d.ts +11 -0
  89. package/dist/runtime/app/components/cms/SettingsPresets.d.vue.ts +13 -0
  90. package/dist/runtime/app/components/cms/SettingsPresets.vue +65 -0
  91. package/dist/runtime/app/components/cms/SettingsPresets.vue.d.ts +13 -0
  92. package/dist/runtime/app/components/cms/SettingsRecordActions.d.vue.ts +32 -0
  93. package/dist/runtime/app/components/cms/SettingsRecordActions.vue +45 -0
  94. package/dist/runtime/app/components/cms/SettingsRecordActions.vue.d.ts +32 -0
  95. package/dist/runtime/app/components/cms/SettingsRedirectActions.d.vue.ts +3 -0
  96. package/dist/runtime/app/components/cms/SettingsRedirectActions.vue +59 -0
  97. package/dist/runtime/app/components/cms/SettingsRedirectActions.vue.d.ts +3 -0
  98. package/dist/runtime/app/components/cms/SettingsRedirectForm.d.vue.ts +11 -0
  99. package/dist/runtime/app/components/cms/SettingsRedirectForm.vue +77 -0
  100. package/dist/runtime/app/components/cms/SettingsRedirectForm.vue.d.ts +11 -0
  101. package/dist/runtime/app/components/cms/SettingsRedirects.d.vue.ts +13 -0
  102. package/dist/runtime/app/components/cms/SettingsRedirects.vue +117 -0
  103. package/dist/runtime/app/components/cms/SettingsRedirects.vue.d.ts +13 -0
  104. package/dist/runtime/app/components/cms/SettingsSecretCard.d.vue.ts +17 -0
  105. package/dist/runtime/app/components/cms/SettingsSecretCard.vue +43 -0
  106. package/dist/runtime/app/components/cms/SettingsSecretCard.vue.d.ts +17 -0
  107. package/dist/runtime/app/components/cms/SettingsSite.d.vue.ts +3 -0
  108. package/dist/runtime/app/components/cms/SettingsSite.vue +140 -0
  109. package/dist/runtime/app/components/cms/SettingsSite.vue.d.ts +3 -0
  110. package/dist/runtime/app/components/cms/SettingsUserCreate.d.vue.ts +8 -0
  111. package/dist/runtime/app/components/cms/SettingsUserCreate.vue +82 -0
  112. package/dist/runtime/app/components/cms/SettingsUserCreate.vue.d.ts +8 -0
  113. package/dist/runtime/app/components/cms/SettingsUserDetail.d.vue.ts +11 -0
  114. package/dist/runtime/app/components/cms/SettingsUserDetail.vue +179 -0
  115. package/dist/runtime/app/components/cms/SettingsUserDetail.vue.d.ts +11 -0
  116. package/dist/runtime/app/components/cms/SettingsUsers.d.vue.ts +9 -6
  117. package/dist/runtime/app/components/cms/SettingsUsers.vue +88 -234
  118. package/dist/runtime/app/components/cms/SettingsUsers.vue.d.ts +9 -6
  119. package/dist/runtime/app/components/cms/SharePreview.d.vue.ts +18 -0
  120. package/dist/runtime/app/components/cms/SharePreview.vue +219 -0
  121. package/dist/runtime/app/components/cms/SharePreview.vue.d.ts +18 -0
  122. package/dist/runtime/app/components/cms/SlugField.vue +5 -3
  123. package/dist/runtime/app/components/cms/StatusBadge.vue +9 -8
  124. package/dist/runtime/app/components/cms/TranslationBadges.d.vue.ts +7 -0
  125. package/dist/runtime/app/components/cms/TranslationBadges.vue +45 -0
  126. package/dist/runtime/app/components/cms/TranslationBadges.vue.d.ts +7 -0
  127. package/dist/runtime/app/components/cms/TranslationMenu.d.vue.ts +17 -0
  128. package/dist/runtime/app/components/cms/TranslationMenu.vue +28 -0
  129. package/dist/runtime/app/components/cms/TranslationMenu.vue.d.ts +17 -0
  130. package/dist/runtime/app/components/cms/VisualEditor.d.vue.ts +22 -4
  131. package/dist/runtime/app/components/cms/VisualEditor.vue +1152 -335
  132. package/dist/runtime/app/components/cms/VisualEditor.vue.d.ts +22 -4
  133. package/dist/runtime/app/components/cms/WelcomeModal.vue +32 -50
  134. package/dist/runtime/app/composables/cms-clipboard.d.ts +8 -0
  135. package/dist/runtime/app/composables/cms-clipboard.js +38 -0
  136. package/dist/runtime/app/composables/cms-confirm.d.ts +6 -8
  137. package/dist/runtime/app/composables/cms-confirm.js +5 -7
  138. package/dist/runtime/app/composables/cms-entry-disabled.d.ts +5 -1
  139. package/dist/runtime/app/composables/cms-entry-disabled.js +7 -0
  140. package/dist/runtime/app/composables/cms-entry-form.d.ts +15 -0
  141. package/dist/runtime/app/composables/cms-entry-form.js +40 -0
  142. package/dist/runtime/app/composables/cms-entry-settings.d.ts +30 -0
  143. package/dist/runtime/app/composables/cms-entry-settings.js +122 -0
  144. package/dist/runtime/app/composables/cms-entry.d.ts +5 -1
  145. package/dist/runtime/app/composables/cms-entry.js +36 -11
  146. package/dist/runtime/app/composables/cms-history.d.ts +10 -0
  147. package/dist/runtime/app/composables/cms-history.js +24 -0
  148. package/dist/runtime/app/composables/cms-link-targets.d.ts +10 -0
  149. package/dist/runtime/app/composables/cms-link-targets.js +40 -0
  150. package/dist/runtime/app/composables/cms-link-usage.d.ts +1 -0
  151. package/dist/runtime/app/composables/cms-link-usage.js +45 -0
  152. package/dist/runtime/app/composables/cms-media-library.d.ts +19 -9
  153. package/dist/runtime/app/composables/cms-media-library.js +31 -13
  154. package/dist/runtime/app/composables/cms-media-uploader.js +6 -20
  155. package/dist/runtime/app/composables/cms-permissions.d.ts +18 -0
  156. package/dist/runtime/app/composables/cms-permissions.js +54 -0
  157. package/dist/runtime/app/composables/cms-presets.d.ts +11 -0
  158. package/dist/runtime/app/composables/cms-presets.js +40 -0
  159. package/dist/runtime/app/composables/cms-preview-mode.d.ts +4 -0
  160. package/dist/runtime/app/composables/cms-preview-mode.js +8 -0
  161. package/dist/runtime/app/composables/cms-query.d.ts +3 -0
  162. package/dist/runtime/app/composables/cms-query.js +45 -20
  163. package/dist/runtime/app/composables/cms-record-list.d.ts +31 -0
  164. package/dist/runtime/app/composables/cms-record-list.js +75 -0
  165. package/dist/runtime/app/composables/cms-route.d.ts +6 -0
  166. package/dist/runtime/app/composables/cms-route.js +36 -0
  167. package/dist/runtime/app/composables/cms-runtime.d.ts +1 -0
  168. package/dist/runtime/app/composables/cms-save-handoff.d.ts +4 -0
  169. package/dist/runtime/app/composables/cms-save-handoff.js +19 -0
  170. package/dist/runtime/app/composables/cms-seo.d.ts +12 -0
  171. package/dist/runtime/app/composables/cms-seo.js +69 -0
  172. package/dist/runtime/app/composables/cms-settings-form.d.ts +13 -0
  173. package/dist/runtime/app/composables/cms-settings-form.js +29 -0
  174. package/dist/runtime/app/composables/cms-settings-sections.d.ts +26 -0
  175. package/dist/runtime/app/composables/cms-settings-sections.js +181 -0
  176. package/dist/runtime/app/composables/cms-settings-stores.d.ts +52 -0
  177. package/dist/runtime/app/composables/cms-settings-stores.js +156 -0
  178. package/dist/runtime/app/composables/cms-settings.d.ts +3 -3
  179. package/dist/runtime/app/composables/cms-settings.js +14 -8
  180. package/dist/runtime/app/composables/cms-site-disabled.d.ts +7 -0
  181. package/dist/runtime/app/composables/cms-site-disabled.js +8 -0
  182. package/dist/runtime/app/composables/cms-site.d.ts +7 -0
  183. package/dist/runtime/app/composables/cms-site.js +22 -0
  184. package/dist/runtime/app/composables/cms-toast.d.ts +2 -8
  185. package/dist/runtime/app/composables/cms-toast.js +3 -9
  186. package/dist/runtime/app/composables/cms-translation-actions.d.ts +15 -0
  187. package/dist/runtime/app/composables/cms-translation-actions.js +108 -0
  188. package/dist/runtime/app/css-url.d.ts +4 -0
  189. package/dist/runtime/app/image/CmsImage.d.vue.ts +89 -0
  190. package/dist/runtime/app/image/CmsImage.vue +91 -0
  191. package/dist/runtime/app/image/CmsImage.vue.d.ts +89 -0
  192. package/dist/runtime/app/layouts/cms-admin.d.vue.ts +2 -2
  193. package/dist/runtime/app/layouts/cms-admin.vue +98 -76
  194. package/dist/runtime/app/layouts/cms-admin.vue.d.ts +2 -2
  195. package/dist/runtime/app/layouts/cms-editor.vue +7 -2
  196. package/dist/runtime/app/links/CmsLink.d.vue.ts +18 -0
  197. package/dist/runtime/app/links/CmsLink.vue +18 -0
  198. package/dist/runtime/app/links/CmsLink.vue.d.ts +18 -0
  199. package/dist/runtime/app/middleware/cms-auth.js +13 -1
  200. package/dist/runtime/app/pages/admin-collection.vue +473 -297
  201. package/dist/runtime/app/pages/admin-content.vue +190 -53
  202. package/dist/runtime/app/pages/admin-entry.vue +79 -75
  203. package/dist/runtime/app/pages/admin-index.vue +18 -5
  204. package/dist/runtime/app/pages/admin-login.vue +1 -1
  205. package/dist/runtime/app/pages/admin-logout.vue +1 -1
  206. package/dist/runtime/app/pages/admin-media.vue +3 -3
  207. package/dist/runtime/app/pages/admin-preview.vue +112 -24
  208. package/dist/runtime/app/plugins/cms-field.d.ts +2 -0
  209. package/dist/runtime/app/plugins/cms-field.js +8 -0
  210. package/dist/runtime/app/plugins/cms-preview-banner.client.d.ts +2 -0
  211. package/dist/runtime/app/plugins/cms-preview-banner.client.js +19 -0
  212. package/dist/runtime/app/plugins/cms-preview.d.ts +2 -0
  213. package/dist/runtime/app/plugins/cms-preview.js +27 -0
  214. package/dist/runtime/app/preview/CmsPreviewBanner.d.vue.ts +7 -0
  215. package/dist/runtime/app/preview/CmsPreviewBanner.vue +63 -0
  216. package/dist/runtime/app/preview/CmsPreviewBanner.vue.d.ts +7 -0
  217. package/dist/runtime/app/preview/PreviewOverlay.d.vue.ts +17 -3
  218. package/dist/runtime/app/preview/PreviewOverlay.vue +629 -166
  219. package/dist/runtime/app/preview/PreviewOverlay.vue.d.ts +17 -3
  220. package/dist/runtime/app/preview/field-marker.d.ts +12 -0
  221. package/dist/runtime/app/preview/field-marker.js +20 -0
  222. package/dist/runtime/app/preview/inline-text.d.ts +15 -0
  223. package/dist/runtime/app/preview/inline-text.js +158 -0
  224. package/dist/runtime/app/utils/block-editor.d.ts +22 -0
  225. package/dist/runtime/app/utils/block-editor.js +12 -0
  226. package/dist/runtime/app/utils/keyboard.d.ts +6 -0
  227. package/dist/runtime/app/utils/keyboard.js +52 -0
  228. package/dist/runtime/app/utils/redirects.d.ts +6 -0
  229. package/dist/runtime/app/utils/redirects.js +7 -0
  230. package/dist/runtime/app/utils/ui.d.ts +8 -0
  231. package/dist/runtime/app/utils/ui.js +15 -1
  232. package/dist/runtime/app/utils/users.d.ts +15 -0
  233. package/dist/runtime/app/utils/users.js +14 -0
  234. package/dist/runtime/assets/cms.css +1 -1
  235. package/dist/runtime/generate.js +16 -2
  236. package/dist/runtime/server/api/collection-export.get.js +3 -2
  237. package/dist/runtime/server/api/collection-import.post.js +39 -11
  238. package/dist/runtime/server/api/collection.get.js +29 -10
  239. package/dist/runtime/server/api/collection.post.js +28 -11
  240. package/dist/runtime/server/api/collection.put.js +35 -13
  241. package/dist/runtime/server/api/item.delete.js +19 -7
  242. package/dist/runtime/server/api/item.get.js +3 -2
  243. package/dist/runtime/server/api/item.put.js +52 -18
  244. package/dist/runtime/server/api/link-usage.post.d.ts +5 -0
  245. package/dist/runtime/server/api/link-usage.post.js +21 -0
  246. package/dist/runtime/server/api/media-delete.post.js +4 -2
  247. package/dist/runtime/server/api/media-download.get.js +2 -2
  248. package/dist/runtime/server/api/media-folder.delete.js +8 -2
  249. package/dist/runtime/server/api/media-folder.patch.js +10 -2
  250. package/dist/runtime/server/api/media-folder.post.js +4 -2
  251. package/dist/runtime/server/api/media-move.post.js +7 -4
  252. package/dist/runtime/server/api/media-presign.post.js +2 -2
  253. package/dist/runtime/server/api/media-upload.put.js +2 -2
  254. package/dist/runtime/server/api/media-usage.post.d.ts +2 -4
  255. package/dist/runtime/server/api/media-usage.post.js +13 -9
  256. package/dist/runtime/server/api/media.delete.js +4 -2
  257. package/dist/runtime/server/api/media.get.js +22 -7
  258. package/dist/runtime/server/api/media.post.js +4 -2
  259. package/dist/runtime/server/api/media.put.js +33 -16
  260. package/dist/runtime/server/api/preset.delete.d.ts +4 -0
  261. package/dist/runtime/server/api/preset.delete.js +9 -0
  262. package/dist/runtime/server/api/preset.put.d.ts +3 -0
  263. package/dist/runtime/server/api/preset.put.js +23 -0
  264. package/dist/runtime/server/api/presets-field.get.d.ts +3 -0
  265. package/dist/runtime/server/api/presets-field.get.js +13 -0
  266. package/dist/runtime/server/api/presets-field.post.d.ts +3 -0
  267. package/dist/runtime/server/api/presets-field.post.js +36 -0
  268. package/dist/runtime/server/api/presets.get.d.ts +3 -0
  269. package/dist/runtime/server/api/presets.get.js +7 -0
  270. package/dist/runtime/server/api/preview-link.delete.d.ts +4 -0
  271. package/dist/runtime/server/api/preview-link.delete.js +15 -0
  272. package/dist/runtime/server/api/preview-links.get.d.ts +3 -0
  273. package/dist/runtime/server/api/preview-links.get.js +10 -0
  274. package/dist/runtime/server/api/preview-links.post.d.ts +3 -0
  275. package/dist/runtime/server/api/preview-links.post.js +17 -0
  276. package/dist/runtime/server/api/preview.get.d.ts +3 -0
  277. package/dist/runtime/server/api/preview.get.js +7 -0
  278. package/dist/runtime/server/api/redirect.delete.d.ts +4 -0
  279. package/dist/runtime/server/api/redirect.delete.js +10 -0
  280. package/dist/runtime/server/api/redirect.put.d.ts +3 -0
  281. package/dist/runtime/server/api/redirect.put.js +8 -0
  282. package/dist/runtime/server/api/redirects-import.post.d.ts +3 -0
  283. package/dist/runtime/server/api/redirects-import.post.js +13 -0
  284. package/dist/runtime/server/api/redirects.get.d.ts +3 -0
  285. package/dist/runtime/server/api/redirects.get.js +7 -0
  286. package/dist/runtime/server/api/redirects.post.d.ts +3 -0
  287. package/dist/runtime/server/api/redirects.post.js +7 -0
  288. package/dist/runtime/server/api/search.get.js +8 -3
  289. package/dist/runtime/server/api/settings.delete.js +2 -2
  290. package/dist/runtime/server/api/settings.get.js +4 -2
  291. package/dist/runtime/server/api/settings.put.js +2 -2
  292. package/dist/runtime/server/api/site.get.d.ts +3 -0
  293. package/dist/runtime/server/api/site.get.js +7 -0
  294. package/dist/runtime/server/api/site.put.d.ts +3 -0
  295. package/dist/runtime/server/api/site.put.js +16 -0
  296. package/dist/runtime/server/api/tags.get.js +3 -2
  297. package/dist/runtime/server/api/user.delete.js +8 -2
  298. package/dist/runtime/server/api/user.put.js +14 -4
  299. package/dist/runtime/server/api/users.get.js +2 -2
  300. package/dist/runtime/server/api/users.post.js +8 -4
  301. package/dist/runtime/server/middleware/hidden-site.d.ts +2 -0
  302. package/dist/runtime/server/middleware/hidden-site.js +8 -0
  303. package/dist/runtime/server/middleware/preview-links.d.ts +2 -0
  304. package/dist/runtime/server/middleware/preview-links.js +6 -0
  305. package/dist/runtime/server/middleware/redirects.d.ts +2 -0
  306. package/dist/runtime/server/middleware/redirects.js +18 -0
  307. package/dist/runtime/server/plugins/session-validate.js +4 -1
  308. package/dist/runtime/server/plugins/webhooks.d.ts +3 -0
  309. package/dist/runtime/server/plugins/webhooks.js +27 -0
  310. package/dist/runtime/server/routes/graphql.js +5 -3
  311. package/dist/runtime/server/routes/robots.d.ts +2 -0
  312. package/dist/runtime/server/routes/robots.js +16 -0
  313. package/dist/runtime/server/routes/sitemap.d.ts +2 -0
  314. package/dist/runtime/server/routes/sitemap.js +67 -0
  315. package/dist/runtime/server/utils/cached-value.d.ts +5 -0
  316. package/dist/runtime/server/utils/cached-value.js +24 -0
  317. package/dist/runtime/server/utils/content-routes.d.ts +5 -0
  318. package/dist/runtime/server/utils/content-routes.js +29 -0
  319. package/dist/runtime/server/utils/field-rules.d.ts +19 -0
  320. package/dist/runtime/server/utils/field-rules.js +72 -0
  321. package/dist/runtime/server/utils/graphql.d.ts +3 -1
  322. package/dist/runtime/server/utils/graphql.js +145 -29
  323. package/dist/runtime/server/utils/hooks.d.ts +14 -0
  324. package/dist/runtime/server/utils/hooks.js +71 -0
  325. package/dist/runtime/server/utils/import.d.ts +7 -2
  326. package/dist/runtime/server/utils/import.js +108 -14
  327. package/dist/runtime/server/utils/link-usage.d.ts +4 -0
  328. package/dist/runtime/server/utils/link-usage.js +28 -0
  329. package/dist/runtime/server/utils/media-references.d.ts +6 -1
  330. package/dist/runtime/server/utils/media-references.js +49 -20
  331. package/dist/runtime/server/utils/media-usage.d.ts +2 -0
  332. package/dist/runtime/server/utils/media-usage.js +5 -3
  333. package/dist/runtime/server/utils/page-rows.d.ts +2 -0
  334. package/dist/runtime/server/utils/page-rows.js +42 -29
  335. package/dist/runtime/server/utils/permissions.d.ts +14 -0
  336. package/dist/runtime/server/utils/permissions.js +43 -0
  337. package/dist/runtime/server/utils/presets.d.ts +18 -0
  338. package/dist/runtime/server/utils/presets.js +68 -0
  339. package/dist/runtime/server/utils/preview-links.d.ts +207 -0
  340. package/dist/runtime/server/utils/preview-links.js +161 -0
  341. package/dist/runtime/server/utils/redirects.d.ts +31 -0
  342. package/dist/runtime/server/utils/redirects.js +240 -0
  343. package/dist/runtime/server/utils/registry.js +4 -1
  344. package/dist/runtime/server/utils/relations.d.ts +8 -0
  345. package/dist/runtime/server/utils/relations.js +25 -6
  346. package/dist/runtime/server/utils/require-admin.d.ts +0 -1
  347. package/dist/runtime/server/utils/require-admin.js +4 -8
  348. package/dist/runtime/server/utils/search.d.ts +1 -0
  349. package/dist/runtime/server/utils/search.js +44 -19
  350. package/dist/runtime/server/utils/single-row.d.ts +3 -0
  351. package/dist/runtime/server/utils/single-row.js +4 -0
  352. package/dist/runtime/server/utils/site.d.ts +23 -0
  353. package/dist/runtime/server/utils/site.js +79 -0
  354. package/dist/runtime/server/utils/sitemap.d.ts +12 -0
  355. package/dist/runtime/server/utils/sitemap.js +109 -0
  356. package/dist/runtime/server/utils/user-management.d.ts +4 -0
  357. package/dist/runtime/server/utils/user-management.js +20 -0
  358. package/dist/runtime/server/utils/users.d.ts +1 -1
  359. package/dist/runtime/server/utils/users.js +1 -1
  360. package/dist/runtime/server/utils/visibility.d.ts +1 -0
  361. package/dist/runtime/server/utils/visibility.js +6 -1
  362. package/dist/runtime/server/utils/webhooks.d.ts +24 -0
  363. package/dist/runtime/server/utils/webhooks.js +90 -0
  364. package/dist/runtime/shared/block-paths.d.ts +66 -0
  365. package/dist/runtime/shared/block-paths.js +310 -0
  366. package/dist/runtime/shared/block-picker.d.ts +18 -0
  367. package/dist/runtime/shared/block-picker.js +31 -0
  368. package/dist/runtime/shared/block-signature.d.ts +4 -0
  369. package/dist/runtime/shared/block-signature.js +46 -0
  370. package/dist/runtime/shared/clipboard.d.ts +31 -0
  371. package/dist/runtime/shared/clipboard.js +206 -0
  372. package/dist/runtime/shared/conditions.d.ts +36 -0
  373. package/dist/runtime/shared/conditions.js +100 -0
  374. package/dist/runtime/shared/content-routes.d.ts +42 -0
  375. package/dist/runtime/shared/content-routes.js +237 -0
  376. package/dist/runtime/shared/duplicate.js +2 -1
  377. package/dist/runtime/shared/editor-layout.d.ts +18 -0
  378. package/dist/runtime/shared/editor-layout.js +9 -0
  379. package/dist/runtime/shared/field-options.d.ts +21 -0
  380. package/dist/runtime/shared/field-options.js +90 -0
  381. package/dist/runtime/shared/graphql-sdl.d.ts +1 -0
  382. package/dist/runtime/shared/graphql-sdl.js +50 -26
  383. package/dist/runtime/shared/history.d.ts +27 -0
  384. package/dist/runtime/shared/history.js +123 -0
  385. package/dist/runtime/shared/hooks.d.ts +45 -0
  386. package/dist/runtime/shared/hooks.js +28 -0
  387. package/dist/runtime/shared/index.d.ts +90 -21
  388. package/dist/runtime/shared/index.js +121 -37
  389. package/dist/runtime/shared/inline-text.d.ts +38 -0
  390. package/dist/runtime/shared/inline-text.js +85 -0
  391. package/dist/runtime/shared/layout.d.ts +2 -1
  392. package/dist/runtime/shared/layout.js +3 -0
  393. package/dist/runtime/shared/links.d.ts +48 -0
  394. package/dist/runtime/shared/links.js +155 -0
  395. package/dist/runtime/shared/list-filters.d.ts +2 -0
  396. package/dist/runtime/shared/list-filters.js +2 -0
  397. package/dist/runtime/shared/media-focal.d.ts +30 -0
  398. package/dist/runtime/shared/media-focal.js +52 -0
  399. package/dist/runtime/shared/migrations-dir.d.ts +14 -0
  400. package/dist/runtime/shared/migrations-dir.js +48 -2
  401. package/dist/runtime/shared/presets.d.ts +67 -0
  402. package/dist/runtime/shared/presets.js +127 -0
  403. package/dist/runtime/shared/preview-links.d.ts +64 -0
  404. package/dist/runtime/shared/preview-links.js +54 -0
  405. package/dist/runtime/shared/preview.d.ts +124 -21
  406. package/dist/runtime/shared/preview.js +419 -81
  407. package/dist/runtime/shared/redirects.d.ts +75 -0
  408. package/dist/runtime/shared/redirects.js +230 -0
  409. package/dist/runtime/shared/roles.d.ts +57 -0
  410. package/dist/runtime/shared/roles.js +293 -0
  411. package/dist/runtime/shared/save-state.d.ts +8 -0
  412. package/dist/runtime/shared/save-state.js +15 -0
  413. package/dist/runtime/shared/search.d.ts +16 -1
  414. package/dist/runtime/shared/search.js +41 -7
  415. package/dist/runtime/shared/seo.d.ts +167 -0
  416. package/dist/runtime/shared/seo.js +335 -0
  417. package/dist/runtime/shared/transfer.js +2 -1
  418. package/dist/runtime/shared/translations.d.ts +29 -0
  419. package/dist/runtime/shared/translations.js +123 -0
  420. package/dist/runtime/shared/users.d.ts +1 -1
  421. package/dist/runtime/shared/validation.d.ts +23 -1
  422. package/dist/runtime/shared/validation.js +214 -64
  423. package/dist/runtime/webhooks.d.ts +33 -0
  424. package/dist/runtime/webhooks.js +57 -0
  425. package/dist/types.d.mts +5 -1
  426. package/package.json +6 -2
  427. package/dist/runtime/app/components/cms/BlockOutline.vue +0 -88
  428. package/dist/runtime/app/components/cms/BlockPalette.vue +0 -31
  429. package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +0 -12
  430. package/dist/runtime/app/components/cms/SettingsModal.vue +0 -126
  431. package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +0 -12
  432. package/dist/runtime/app/composables/cms-field-focus.d.ts +0 -4
  433. package/dist/runtime/app/composables/cms-field-focus.js +0 -20
@@ -3,7 +3,7 @@ import { createError } from "h3";
3
3
  import cmsConfig from "#cms-config";
4
4
  import { useDb } from "#cms-db";
5
5
  import * as cmsTables from "#cms-tables";
6
- import { entryFieldsFor } from "../../shared/index.js";
6
+ import { entryFieldsFor, relationTarget } from "../../shared/index.js";
7
7
  import { resolveTable, tableColumns } from "./registry.js";
8
8
  import { joinWriteStatements } from "./write-statements.js";
9
9
  function manyToManyKeys(entry) {
@@ -37,7 +37,7 @@ export async function assertRelationTargets(entry, lists) {
37
37
  for (const [key, ids] of Object.entries(lists)) {
38
38
  if (!ids.length) continue;
39
39
  const field = entryFieldsFor(entry)[key];
40
- const target = resolveTable(field.to);
40
+ const target = resolveTable(relationTarget(field));
41
41
  if (!target) continue;
42
42
  const idCol = tableColumns(target).id;
43
43
  const rows = await db.select({ id: idCol }).from(target).where(inArray(idCol, ids));
@@ -46,7 +46,9 @@ export async function assertRelationTargets(entry, lists) {
46
46
  const missing = ids.filter((id) => !found.has(id));
47
47
  throw createError({
48
48
  statusCode: 422,
49
- statusMessage: `Unknown ${field.to} id(s) for '${key}': ${missing.join(", ")}`
49
+ statusMessage: `Unknown ${relationTarget(field)} id(s) for '${key}': ${missing.join(
50
+ ", "
51
+ )}`
50
52
  });
51
53
  }
52
54
  }
@@ -60,9 +62,10 @@ export async function relationTitles(db, entry, rows) {
60
62
  const config = cmsConfig;
61
63
  const titles = {};
62
64
  for (const [key, field] of Object.entries(entryFieldsFor(entry))) {
63
- if (field.type !== "relation" || !field.to) continue;
64
- const targetEntry = config[field.to];
65
- const target = resolveTable(field.to);
65
+ const to = relationTarget(field);
66
+ if (field.type !== "relation" || !to) continue;
67
+ const targetEntry = config[to];
68
+ const target = resolveTable(to);
66
69
  if (!targetEntry?.titleField || !target) continue;
67
70
  const ids = /* @__PURE__ */ new Set();
68
71
  for (const row of rows) {
@@ -99,3 +102,19 @@ export async function attachManyToMany(db, name, entry, rows) {
99
102
  }
100
103
  return rows;
101
104
  }
105
+ export async function readBackSaved(name, entry, row) {
106
+ try {
107
+ const [attached] = await attachManyToMany(useDb(), name, entry, [row]);
108
+ return { row: attached ?? row, error: null };
109
+ } catch (error) {
110
+ return { row, error };
111
+ }
112
+ }
113
+ export function savedReadBackError(error) {
114
+ return createError({
115
+ statusCode: 500,
116
+ statusMessage: "The changes were saved, but the saved item could not be read back. Reload it.",
117
+ data: { saved: true },
118
+ cause: error
119
+ });
120
+ }
@@ -2,4 +2,3 @@ import type { H3Event } from 'h3';
2
2
  import type { CmsSessionUser } from '../../shared/index.js';
3
3
  export declare function isSuperAdminEmail(event: H3Event, email: string): boolean;
4
4
  export declare function requireAdmin(event: H3Event): Promise<CmsSessionUser>;
5
- export declare function requireSuperAdmin(event: H3Event): Promise<CmsSessionUser>;
@@ -23,19 +23,15 @@ export async function requireAdmin(event) {
23
23
  if (!row || user.passwordStamp !== passwordStamp(row.passwordHash)) {
24
24
  throw createError({ statusCode: 401, statusMessage: "Authentication required" });
25
25
  }
26
+ if (row.role === "superadmin") {
27
+ throw createError({ statusCode: 403, statusMessage: "Not authorized" });
28
+ }
26
29
  return {
27
30
  id: row.id,
28
31
  email: row.email,
29
32
  name: row.name ?? null,
30
- role: "admin",
33
+ role: row.role,
31
34
  firstLogin: !!row.mustChangePassword,
32
35
  passwordStamp: user.passwordStamp
33
36
  };
34
37
  }
35
- export async function requireSuperAdmin(event) {
36
- const user = await requireAdmin(event);
37
- if (user.role !== "superadmin") {
38
- throw createError({ statusCode: 403, statusMessage: "Only the super admin can manage users" });
39
- }
40
- return user;
41
- }
@@ -11,6 +11,7 @@ export interface CmsSearchOptions {
11
11
  tables: Record<string, unknown>;
12
12
  i18n: CmsI18n;
13
13
  query: string;
14
+ media?: boolean;
14
15
  }
15
16
  export declare function searchLikeCondition(dialect: SearchDialect, column: AnySQLiteColumn, pattern: string): SQL;
16
17
  export declare function searchCmsValues(options: CmsSearchOptions): Promise<{
@@ -1,14 +1,19 @@
1
1
  import { desc, inArray, or, sql } from "drizzle-orm";
2
2
  import {
3
3
  decodeTranslatableValue,
4
+ formatBlockPath,
5
+ resolveBlockPath,
6
+ walkBlocks,
4
7
  isMultiSelect,
5
8
  isTranslatableField,
9
+ linkLabelTexts,
6
10
  mediaFilename,
7
11
  pageColumnFields,
8
12
  pageFields,
9
13
  pageFieldsTableName,
10
14
  pageMediaTableName,
11
15
  pageRouteOf,
16
+ readLinkValue,
12
17
  selectOptionLabel,
13
18
  selectValuesMatching
14
19
  } from "../../shared/index.js";
@@ -17,6 +22,8 @@ import {
17
22
  CMS_SEARCH_MIN_LENGTH,
18
23
  CMS_SEARCH_ROWS_PER_ENTRY,
19
24
  CMS_SEARCH_VALUE_LIMIT,
25
+ cmsEntryLink,
26
+ cmsHashLink,
20
27
  cmsSearchLink,
21
28
  normalizeSearchQuery,
22
29
  searchCmsLabels,
@@ -34,6 +41,7 @@ const VALUE_TYPES = /* @__PURE__ */ new Set([
34
41
  "select",
35
42
  "tags",
36
43
  "json",
44
+ "link",
37
45
  "blocks"
38
46
  ]);
39
47
  function isSearchableField(field) {
@@ -66,6 +74,13 @@ function fieldTexts(field, value, defaultLocale) {
66
74
  }).join(", ");
67
75
  return [{ locale: null, text }];
68
76
  }
77
+ if (field.type === "link") {
78
+ const link = readLinkValue(value);
79
+ if (!link) return [];
80
+ const texts = linkLabelTexts(link.label);
81
+ if (link.url) texts.push({ locale: null, text: link.url });
82
+ return texts;
83
+ }
69
84
  if (field.type === "tags" && Array.isArray(value))
70
85
  return value.length ? [{ locale: null, text: value.map(String).join(", ") }] : [];
71
86
  if (typeof value === "string") return [{ locale: null, text: plainText(field, value) }];
@@ -88,22 +103,29 @@ function rowHits(fields, row, term, defaultLocale) {
88
103
  collect(field, value, { field: key, block: null, fieldLabel: field.label });
89
104
  continue;
90
105
  }
91
- if (!Array.isArray(value)) continue;
92
- value.forEach((item, index) => {
93
- const block = item ? field.blocks?.[String(item.type)] : void 0;
94
- if (!item || !block) return;
106
+ walkBlocks({ [key]: field }, row, ({ item, block, path }) => {
107
+ if (hits.length >= CMS_SEARCH_HITS_PER_ROW) return false;
95
108
  for (const [blockKey, blockField] of Object.entries(block.fields)) {
96
- if (!isSearchableField(blockField)) continue;
109
+ if (!isSearchableField(blockField) || blockField.type === "blocks") continue;
97
110
  collect(blockField, item[blockKey], {
98
111
  field: key,
99
- block: index,
100
- fieldLabel: searchFieldPath(field.label, block.label, blockField.label)
112
+ block: formatBlockPath(path.slice(1)),
113
+ fieldLabel: searchFieldPath(...blockLabels(fields, row, path), blockField.label)
101
114
  });
102
115
  }
103
116
  });
104
117
  }
105
118
  return hits;
106
119
  }
120
+ function blockLabels(fields, row, path) {
121
+ const labels = [];
122
+ for (let end = 2; end <= path.length; end += 2) {
123
+ const location = resolveBlockPath(fields, row, path.slice(0, end));
124
+ if (!location) break;
125
+ labels.push(location.field.label, location.block?.label ?? String(location.item.type));
126
+ }
127
+ return labels;
128
+ }
107
129
  function titleOf(entry, row, defaultLocale) {
108
130
  if (entry.kind === "single" || !entry.titleField) return null;
109
131
  const value = row[entry.titleField];
@@ -162,20 +184,23 @@ async function pageRows(context, name, entry, table) {
162
184
  }).where(inArray(pageColumns.id, ids));
163
185
  return decodePageRows(entry, rows);
164
186
  }
165
- function itemPath(name, entry, row) {
166
- if (entry.kind === "single") return `/cms/${name}`;
187
+ function itemLink(name, entry, row) {
188
+ if (entry.kind === "single") return (focus) => cmsSearchLink(`/cms/${name}`, focus);
167
189
  if (entry.kind === "page") {
168
190
  const route = pageRouteOf(entry, String(row.id));
169
- return route ? `/cms/${name}/${route.key}` : null;
191
+ return route ? (focus) => cmsSearchLink(`/cms/${name}/${route.key}`, focus) : null;
170
192
  }
171
- return row.id == null ? null : `/cms/${name}/${String(row.id)}`;
193
+ if (row.id == null) return null;
194
+ const id = String(row.id);
195
+ if (entry.kind === "collection") return (focus) => cmsEntryLink(name, id, focus);
196
+ return (focus) => cmsSearchLink(`/cms/${name}/${id}`, focus);
172
197
  }
173
198
  function entryHits(context, name, entry, rows) {
174
199
  const { defaultLocale } = context.i18n;
175
200
  const hits = [];
176
201
  for (const row of rows) {
177
- const path = itemPath(name, entry, row);
178
- if (!path) continue;
202
+ const link = itemLink(name, entry, row);
203
+ if (!link) continue;
179
204
  const route = entry.kind === "page" ? pageRouteOf(entry, String(row.id)) : void 0;
180
205
  const fields = route ? pageFields(entry, route.path) : entry.fields;
181
206
  const title = route ? route.label : titleOf(entry, row, defaultLocale);
@@ -191,17 +216,14 @@ function entryHits(context, name, entry, rows) {
191
216
  fieldLabel: hit.fieldLabel,
192
217
  locale: hit.locale,
193
218
  snippet: hit.snippet,
194
- to: cmsSearchLink(path, {
195
- field: hit.field,
196
- locale: hit.locale,
197
- block: hit.block
198
- })
219
+ to: link({ field: hit.field, locale: hit.locale, block: hit.block })
199
220
  });
200
221
  }
201
222
  }
202
223
  return hits;
203
224
  }
204
225
  async function mediaHits(context) {
226
+ if (context.media === false) return [];
205
227
  const table = context.tables.cms_media;
206
228
  const columns = table ? columnsOf(table) : void 0;
207
229
  if (!table || !columns?.alt || !columns.key || !columns.folder) return [];
@@ -222,7 +244,10 @@ async function mediaHits(context) {
222
244
  fieldLabel: "Alt text",
223
245
  locale: null,
224
246
  snippet,
225
- to: folder ? `/cms/media?${new URLSearchParams({ folder })}` : "/cms/media"
247
+ to: cmsHashLink(
248
+ folder ? `/cms/media?${new URLSearchParams({ folder })}` : "/cms/media",
249
+ { media: row.key }
250
+ )
226
251
  }
227
252
  ];
228
253
  });
@@ -0,0 +1,3 @@
1
+ import type { SQL } from 'drizzle-orm';
2
+ import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core';
3
+ export declare function singleRowFirst(idColumn: AnySQLiteColumn, id: string): SQL;
@@ -0,0 +1,4 @@
1
+ import { sql } from "drizzle-orm";
2
+ export function singleRowFirst(idColumn, id) {
3
+ return sql`case when ${idColumn} = ${id} then 0 else 1 end`;
4
+ }
@@ -0,0 +1,23 @@
1
+ import type { H3Event } from 'h3';
2
+ import { z } from 'zod';
3
+ import type { CmsI18n, CmsSiteSettings } from '../../shared/index.js';
4
+ export declare function siteSettingsSchema(i18n: CmsI18n): z.ZodObject<{
5
+ name: z.ZodPipe<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<{
6
+ [k: string]: string;
7
+ } | null, Record<string, string> | null>>;
8
+ titleTemplate: z.ZodPipe<z.ZodNullable<z.ZodString>, z.ZodTransform<string | null, string | null>>;
9
+ description: z.ZodPipe<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<{
10
+ [k: string]: string;
11
+ } | null, Record<string, string> | null>>;
12
+ image: z.ZodNullable<z.ZodString>;
13
+ hidden: z.ZodBoolean;
14
+ }, z.core.$strict>;
15
+ export declare function parseSiteSettings(raw: string | null | undefined): CmsSiteSettings;
16
+ export declare function readSiteSettings(): Promise<CmsSiteSettings>;
17
+ export declare const SITE_SETTINGS_CACHE_TTL_MS = 30000;
18
+ export declare function readCachedSiteSettings(): Promise<CmsSiteSettings>;
19
+ export declare function writeSiteSettings(settings: CmsSiteSettings): Promise<void>;
20
+ export declare function configuredSiteUrl(event: H3Event): string;
21
+ export declare function trustsForwardedHost(event: H3Event): boolean;
22
+ export declare function resolveSiteUrl(event: H3Event): string;
23
+ export declare function seoRouteCacheHeader(event: H3Event): string;
@@ -0,0 +1,79 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { getRequestURL } from "h3";
3
+ import { z } from "zod";
4
+ import { useDb } from "#cms-db";
5
+ import { cms_settings } from "#cms-tables";
6
+ import { useRuntimeConfig } from "#imports";
7
+ import { cachedValue } from "./cached-value.js";
8
+ import {
9
+ CMS_SITE_SETTINGS_KEY,
10
+ EMPTY_SITE_SETTINGS,
11
+ SITE_TEXT_LIMITS,
12
+ coerceSiteSettings,
13
+ isForwardedHostTrusted,
14
+ normalizeSiteUrl,
15
+ seoRouteCacheControl,
16
+ titleTemplateErrors
17
+ } from "../../shared/index.js";
18
+ function localeTextSchema(max, locales) {
19
+ return z.record(z.string(), z.string().max(max)).nullable().superRefine((value, context) => {
20
+ for (const locale of Object.keys(value ?? {})) {
21
+ if (!locales.includes(locale))
22
+ context.addIssue({ code: "custom", message: `Unknown locale: ${locale}` });
23
+ }
24
+ }).transform((value) => {
25
+ const entries = Object.entries(value ?? {}).map(([locale, text]) => [locale, text.trim()]).filter(([, text]) => text);
26
+ return entries.length ? Object.fromEntries(entries) : null;
27
+ });
28
+ }
29
+ export function siteSettingsSchema(i18n) {
30
+ const locales = i18n.locales.length ? i18n.locales : [i18n.defaultLocale];
31
+ return z.object({
32
+ name: localeTextSchema(SITE_TEXT_LIMITS.name, locales),
33
+ titleTemplate: z.string().max(SITE_TEXT_LIMITS.titleTemplate).nullable().transform((value) => value?.trim() || null).superRefine((value, context) => {
34
+ for (const message of value ? titleTemplateErrors(value) : [])
35
+ context.addIssue({ code: "custom", message });
36
+ }),
37
+ description: localeTextSchema(SITE_TEXT_LIMITS.description, locales),
38
+ image: z.string().min(1).max(1024).nullable(),
39
+ hidden: z.boolean()
40
+ }).strict();
41
+ }
42
+ export function parseSiteSettings(raw) {
43
+ if (!raw) return { ...EMPTY_SITE_SETTINGS };
44
+ try {
45
+ return coerceSiteSettings(JSON.parse(raw));
46
+ } catch {
47
+ return { ...EMPTY_SITE_SETTINGS };
48
+ }
49
+ }
50
+ export async function readSiteSettings() {
51
+ const [row] = await useDb().select({ value: cms_settings.value }).from(cms_settings).where(eq(cms_settings.key, CMS_SITE_SETTINGS_KEY)).limit(1);
52
+ return parseSiteSettings(row?.value);
53
+ }
54
+ export const SITE_SETTINGS_CACHE_TTL_MS = 3e4;
55
+ const siteSettingsCache = cachedValue(readSiteSettings, SITE_SETTINGS_CACHE_TTL_MS);
56
+ export function readCachedSiteSettings() {
57
+ return siteSettingsCache.get();
58
+ }
59
+ export async function writeSiteSettings(settings) {
60
+ const value = JSON.stringify(settings);
61
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
62
+ await useDb().insert(cms_settings).values({ key: CMS_SITE_SETTINGS_KEY, value, updatedAt }).onConflictDoUpdate({ target: cms_settings.key, set: { value, updatedAt } });
63
+ siteSettingsCache.invalidate();
64
+ }
65
+ export function configuredSiteUrl(event) {
66
+ const { siteUrl } = useRuntimeConfig(event).public;
67
+ return normalizeSiteUrl(siteUrl);
68
+ }
69
+ export function trustsForwardedHost(event) {
70
+ const config = useRuntimeConfig(event).public;
71
+ return isForwardedHostTrusted(config.cmsTrustForwardedHost);
72
+ }
73
+ export function resolveSiteUrl(event) {
74
+ const trusted = trustsForwardedHost(event);
75
+ return configuredSiteUrl(event) || getRequestURL(event, { xForwardedHost: trusted, xForwardedProto: trusted }).origin;
76
+ }
77
+ export function seoRouteCacheHeader(event) {
78
+ return seoRouteCacheControl(configuredSiteUrl(event));
79
+ }
@@ -0,0 +1,12 @@
1
+ import type { CmsI18n, SitemapChunk, SitemapSourceSize, SitemapUrl } from '../../shared/index.js';
2
+ export declare const PAGES_SOURCE = "";
3
+ export interface SitemapContext {
4
+ siteUrl: string;
5
+ i18n: CmsI18n;
6
+ }
7
+ export interface SitemapPlan {
8
+ pages: SitemapUrl[];
9
+ sizes: SitemapSourceSize[];
10
+ }
11
+ export declare function planSitemap(context: SitemapContext): Promise<SitemapPlan>;
12
+ export declare function sitemapChunkUrls(plan: SitemapPlan, chunks: readonly SitemapChunk[], context: SitemapContext): AsyncGenerator<SitemapUrl[]>;
@@ -0,0 +1,109 @@
1
+ import { and, asc, eq, gt, isNull, or, sql } from "drizzle-orm";
2
+ import cmsConfig from "#cms-config";
3
+ import { useDb } from "#cms-db";
4
+ import {
5
+ CONTENT_NOINDEX_FIELD,
6
+ CONTENT_PUBLISHED_AT_FIELD,
7
+ CONTENT_SLUG_FIELD,
8
+ contentItemPaths,
9
+ contentRouteLocales,
10
+ contentRoutePatterns,
11
+ isRoutedContent,
12
+ pageRoutes,
13
+ pageSitemapUrls,
14
+ sitemapLastmod,
15
+ sitemapUrlsForPaths
16
+ } from "../../shared/index.js";
17
+ import { readPages } from "./page-storage.js";
18
+ import { resolveTable, tableColumns } from "./registry.js";
19
+ import { visibilityConditions } from "./visibility.js";
20
+ const BATCH_SIZE = 1e3;
21
+ export const PAGES_SOURCE = "";
22
+ function contentWhere(entry, table) {
23
+ const columns = tableColumns(table);
24
+ const conditions = visibilityConditions(entry, columns);
25
+ const noindex = columns[CONTENT_NOINDEX_FIELD];
26
+ if (noindex) conditions.push(or(isNull(noindex), eq(noindex, false)));
27
+ return conditions;
28
+ }
29
+ function routedEntries() {
30
+ const entries = [];
31
+ for (const [name, entry] of Object.entries(cmsConfig)) {
32
+ const table = isRoutedContent(entry) ? resolveTable(name) : void 0;
33
+ if (table) entries.push([name, entry, table]);
34
+ }
35
+ return entries;
36
+ }
37
+ async function readPageUrls(siteUrl) {
38
+ const found = Object.entries(cmsConfig).find(([, entry2]) => entry2.kind === "page");
39
+ if (!found) return [];
40
+ const [name, entry] = found;
41
+ const table = resolveTable(name);
42
+ if (!table) return [];
43
+ const rows = await readPages(name, entry, table);
44
+ return pageSitemapUrls(
45
+ pageRoutes(entry).map((route) => route.path),
46
+ rows,
47
+ siteUrl
48
+ );
49
+ }
50
+ async function countContent(entry, table) {
51
+ const where = contentWhere(entry, table);
52
+ const [counted] = await useDb().select({ total: sql`count(*)` }).from(table).where(where.length ? and(...where) : void 0);
53
+ return Number(counted?.total ?? 0);
54
+ }
55
+ export async function planSitemap(context) {
56
+ const pages = await readPageUrls(context.siteUrl);
57
+ const sizes = [
58
+ { source: PAGES_SOURCE, count: pages.length, urlsPerItem: 1 }
59
+ ];
60
+ for (const [name, entry, table] of routedEntries()) {
61
+ sizes.push({
62
+ source: name,
63
+ count: await countContent(entry, table),
64
+ urlsPerItem: contentRoutePatterns(entry.route).length
65
+ });
66
+ }
67
+ return { pages, sizes: sizes.filter((size) => size.count > 0) };
68
+ }
69
+ async function* contentUrls(name, context, offset, limit) {
70
+ const entry = cmsConfig[name];
71
+ const table = resolveTable(name);
72
+ if (!entry || !table) return;
73
+ const columns = tableColumns(table);
74
+ const where = contentWhere(entry, table);
75
+ const locales = contentRouteLocales(context.i18n);
76
+ let lastId = null;
77
+ let remaining = limit;
78
+ while (remaining > 0) {
79
+ const conditions = lastId === null ? where : [...where, gt(columns.id, lastId)];
80
+ const query = useDb().select({
81
+ id: columns.id,
82
+ slug: columns[CONTENT_SLUG_FIELD],
83
+ publishedAt: columns[CONTENT_PUBLISHED_AT_FIELD],
84
+ updatedAt: columns.updatedAt
85
+ }).from(table).where(conditions.length ? and(...conditions) : void 0).orderBy(asc(columns.id)).limit(Math.min(BATCH_SIZE, remaining));
86
+ const rows = await (lastId === null && offset ? query.offset(offset) : query);
87
+ if (!rows.length) return;
88
+ remaining -= rows.length;
89
+ lastId = String(rows.at(-1).id);
90
+ yield rows.flatMap(
91
+ (row) => sitemapUrlsForPaths(
92
+ contentItemPaths(entry, row, locales),
93
+ context.siteUrl,
94
+ context.i18n.defaultLocale,
95
+ sitemapLastmod(row.updatedAt)
96
+ )
97
+ );
98
+ if (rows.length < BATCH_SIZE) return;
99
+ }
100
+ }
101
+ export async function* sitemapChunkUrls(plan, chunks, context) {
102
+ for (const chunk of chunks) {
103
+ if (chunk.source === PAGES_SOURCE) {
104
+ yield plan.pages.slice(chunk.offset, chunk.offset + chunk.limit);
105
+ continue;
106
+ }
107
+ yield* contentUrls(chunk.source, context, chunk.offset, chunk.limit);
108
+ }
109
+ }
@@ -0,0 +1,4 @@
1
+ import type { CmsAccess } from '../../shared/roles.js';
2
+ export declare function assertKnownRole(role: string): void;
3
+ export declare function assertCanGrantRole(access: CmsAccess, role: string): void;
4
+ export declare function assertCanManageAccount(access: CmsAccess, role: string): void;
@@ -0,0 +1,20 @@
1
+ import { createError } from "h3";
2
+ import cmsConfig from "#cms-config";
3
+ import cmsRoles from "#cms-roles";
4
+ import { canGrantRole, canManageAccount, cmsRoleOptions } from "../../shared/roles.js";
5
+ import { forbidden } from "./permissions.js";
6
+ export function assertKnownRole(role) {
7
+ if (cmsRoleOptions(cmsRoles).some((option) => option.key === role)) return;
8
+ throw createError({ statusCode: 400, statusMessage: `Unknown role: ${role}` });
9
+ }
10
+ export function assertCanGrantRole(access, role) {
11
+ assertKnownRole(role);
12
+ if (!canGrantRole(access, role, cmsRoles, cmsConfig)) {
13
+ forbidden(`You cannot grant the role '${role}': it has permissions you do not hold`);
14
+ }
15
+ }
16
+ export function assertCanManageAccount(access, role) {
17
+ if (!canManageAccount(access, role, cmsRoles, cmsConfig)) {
18
+ forbidden("You cannot manage this account: its role has permissions you do not hold");
19
+ }
20
+ }
@@ -8,7 +8,7 @@ export declare function sessionUserFor(row: CmsUserRow): {
8
8
  id: string;
9
9
  email: string;
10
10
  name: string | null;
11
- role: "admin";
11
+ role: string;
12
12
  firstLogin: boolean;
13
13
  passwordStamp: string;
14
14
  };
@@ -18,7 +18,7 @@ export function sessionUserFor(row) {
18
18
  id: row.id,
19
19
  email: row.email,
20
20
  name: row.name ?? null,
21
- role: "admin",
21
+ role: row.role,
22
22
  firstLogin: !!row.mustChangePassword,
23
23
  passwordStamp: passwordStamp(row.passwordHash)
24
24
  };
@@ -2,3 +2,4 @@ import type { SQL } from 'drizzle-orm';
2
2
  import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core';
3
3
  import type { CmsEntry } from '../../shared/index.js';
4
4
  export declare function visibilityConditions(entry: Pick<CmsEntry, 'kind' | 'drafts'>, columns: Record<string, AnySQLiteColumn>, now?: string): SQL[];
5
+ export declare function visibleOrPreviewConditions(entry: Pick<CmsEntry, 'kind' | 'drafts'>, columns: Record<string, AnySQLiteColumn>, previewId?: string, now?: string): SQL[];
@@ -1,4 +1,4 @@
1
- import { eq, isNotNull, lte } from "drizzle-orm";
1
+ import { and, eq, isNotNull, lte, or } from "drizzle-orm";
2
2
  import { CONTENT_PUBLISHED_AT_FIELD } from "../../shared/index.js";
3
3
  import { nowTimestamp } from "../../shared/timestamps.js";
4
4
  export function visibilityConditions(entry, columns, now = nowTimestamp()) {
@@ -10,3 +10,8 @@ export function visibilityConditions(entry, columns, now = nowTimestamp()) {
10
10
  conditions.push(isNotNull(publishedAt), lte(publishedAt, now));
11
11
  return conditions;
12
12
  }
13
+ export function visibleOrPreviewConditions(entry, columns, previewId, now = nowTimestamp()) {
14
+ const visible = visibilityConditions(entry, columns, now);
15
+ if (!visible.length || previewId === void 0 || !columns.id) return visible;
16
+ return [or(and(...visible), eq(columns.id, previewId))];
17
+ }
@@ -0,0 +1,24 @@
1
+ import type { CmsEntryChange, CmsMediaChange } from '../../shared/hooks.js';
2
+ import type { CmsWebhookEntryPayload, CmsWebhookMediaPayload, CmsWebhookPayload } from '../../webhooks.js';
3
+ export interface CmsWebhookTarget {
4
+ name: string;
5
+ url: string;
6
+ secret: string;
7
+ events: string[];
8
+ timeout: number;
9
+ }
10
+ export type CmsWebhookFetch = (url: string, init: {
11
+ method: string;
12
+ headers: Record<string, string>;
13
+ body: string;
14
+ signal: AbortSignal;
15
+ }) => Promise<{
16
+ ok: boolean;
17
+ status: number;
18
+ }>;
19
+ export declare function entryWebhookPayload(change: CmsEntryChange): CmsWebhookEntryPayload;
20
+ export declare function mediaWebhookPayload(change: CmsMediaChange): CmsWebhookMediaPayload;
21
+ export declare function webhookWants(events: readonly string[], event: string): boolean;
22
+ export declare function webhookTargets(config: unknown): CmsWebhookTarget[];
23
+ export declare function deliverWebhook(target: CmsWebhookTarget, payload: CmsWebhookPayload, fetcher?: CmsWebhookFetch): Promise<void>;
24
+ export declare function deliverWebhooks(targets: CmsWebhookTarget[], payload: CmsWebhookPayload, fetcher?: CmsWebhookFetch, log?: (message: string) => void): Promise<void>;
@@ -0,0 +1,90 @@
1
+ import {
2
+ CMS_WEBHOOK_DELIVERY_HEADER,
3
+ CMS_WEBHOOK_EVENT_HEADER,
4
+ CMS_WEBHOOK_SIGNATURE_HEADER,
5
+ signCmsWebhook
6
+ } from "../../webhooks.js";
7
+ const DEFAULT_TIMEOUT = 5e3;
8
+ export function entryWebhookPayload(change) {
9
+ return {
10
+ event: `entry:${change.action}`,
11
+ action: change.action,
12
+ entry: change.entry,
13
+ kind: change.kind,
14
+ ids: change.ids,
15
+ path: change.path,
16
+ status: change.status,
17
+ at: change.at
18
+ };
19
+ }
20
+ export function mediaWebhookPayload(change) {
21
+ return {
22
+ event: `media:${change.action}`,
23
+ action: change.action,
24
+ keys: change.keys,
25
+ folder: change.folder,
26
+ from: change.from,
27
+ at: change.at
28
+ };
29
+ }
30
+ export function webhookWants(events, event) {
31
+ if (!events.length) return true;
32
+ const group = event.slice(0, event.indexOf(":"));
33
+ return events.includes(event) || events.includes(group);
34
+ }
35
+ export function webhookTargets(config) {
36
+ if (!config || typeof config !== "object") return [];
37
+ return Object.entries(config).flatMap(
38
+ ([name, value]) => {
39
+ const url = value?.url == null ? "" : String(value.url);
40
+ if (!url) return [];
41
+ const timeout = Number(value?.timeout);
42
+ return [
43
+ {
44
+ name,
45
+ url,
46
+ secret: value?.secret == null ? "" : String(value.secret),
47
+ events: Array.isArray(value?.events) ? value.events.map(String) : [],
48
+ timeout: Number.isInteger(timeout) && timeout > 0 ? timeout : DEFAULT_TIMEOUT
49
+ }
50
+ ];
51
+ }
52
+ );
53
+ }
54
+ function describeTarget(target) {
55
+ try {
56
+ return `'${target.name}' (${new URL(target.url).host})`;
57
+ } catch {
58
+ return `'${target.name}'`;
59
+ }
60
+ }
61
+ export async function deliverWebhook(target, payload, fetcher = globalThis.fetch) {
62
+ const body = JSON.stringify(payload);
63
+ const headers = {
64
+ "content-type": "application/json",
65
+ "user-agent": "nuxt-cms-webhooks",
66
+ [CMS_WEBHOOK_EVENT_HEADER]: payload.event,
67
+ [CMS_WEBHOOK_DELIVERY_HEADER]: globalThis.crypto.randomUUID()
68
+ };
69
+ if (target.secret)
70
+ headers[CMS_WEBHOOK_SIGNATURE_HEADER] = await signCmsWebhook(body, target.secret);
71
+ const response = await fetcher(target.url, {
72
+ method: "POST",
73
+ headers,
74
+ body,
75
+ signal: AbortSignal.timeout(target.timeout)
76
+ });
77
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
78
+ }
79
+ export async function deliverWebhooks(targets, payload, fetcher, log = (message) => console.warn(message)) {
80
+ await Promise.all(
81
+ targets.filter((target) => webhookWants(target.events, payload.event)).map(
82
+ (target) => deliverWebhook(target, payload, fetcher).catch((error) => {
83
+ const reason = error instanceof Error ? error.name === "TimeoutError" ? `timed out after ${target.timeout} ms` : error.message : String(error);
84
+ log(
85
+ `[nuxt-cms] webhook ${describeTarget(target)} failed on ${payload.event}: ${reason}`
86
+ );
87
+ })
88
+ )
89
+ );
90
+ }