@xleddyl/nuxt-cms 0.9.4 → 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 (431) 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 +27 -9
  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 +143 -28
  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/site.d.ts +23 -0
  351. package/dist/runtime/server/utils/site.js +79 -0
  352. package/dist/runtime/server/utils/sitemap.d.ts +12 -0
  353. package/dist/runtime/server/utils/sitemap.js +109 -0
  354. package/dist/runtime/server/utils/user-management.d.ts +4 -0
  355. package/dist/runtime/server/utils/user-management.js +20 -0
  356. package/dist/runtime/server/utils/users.d.ts +1 -1
  357. package/dist/runtime/server/utils/users.js +1 -1
  358. package/dist/runtime/server/utils/visibility.d.ts +1 -0
  359. package/dist/runtime/server/utils/visibility.js +6 -1
  360. package/dist/runtime/server/utils/webhooks.d.ts +24 -0
  361. package/dist/runtime/server/utils/webhooks.js +90 -0
  362. package/dist/runtime/shared/block-paths.d.ts +66 -0
  363. package/dist/runtime/shared/block-paths.js +310 -0
  364. package/dist/runtime/shared/block-picker.d.ts +18 -0
  365. package/dist/runtime/shared/block-picker.js +31 -0
  366. package/dist/runtime/shared/block-signature.d.ts +4 -0
  367. package/dist/runtime/shared/block-signature.js +46 -0
  368. package/dist/runtime/shared/clipboard.d.ts +31 -0
  369. package/dist/runtime/shared/clipboard.js +206 -0
  370. package/dist/runtime/shared/conditions.d.ts +36 -0
  371. package/dist/runtime/shared/conditions.js +100 -0
  372. package/dist/runtime/shared/content-routes.d.ts +42 -0
  373. package/dist/runtime/shared/content-routes.js +237 -0
  374. package/dist/runtime/shared/duplicate.js +2 -1
  375. package/dist/runtime/shared/editor-layout.d.ts +18 -0
  376. package/dist/runtime/shared/editor-layout.js +9 -0
  377. package/dist/runtime/shared/field-options.d.ts +21 -0
  378. package/dist/runtime/shared/field-options.js +90 -0
  379. package/dist/runtime/shared/graphql-sdl.d.ts +1 -0
  380. package/dist/runtime/shared/graphql-sdl.js +50 -26
  381. package/dist/runtime/shared/history.d.ts +27 -0
  382. package/dist/runtime/shared/history.js +123 -0
  383. package/dist/runtime/shared/hooks.d.ts +45 -0
  384. package/dist/runtime/shared/hooks.js +28 -0
  385. package/dist/runtime/shared/index.d.ts +90 -21
  386. package/dist/runtime/shared/index.js +121 -37
  387. package/dist/runtime/shared/inline-text.d.ts +38 -0
  388. package/dist/runtime/shared/inline-text.js +85 -0
  389. package/dist/runtime/shared/layout.d.ts +2 -1
  390. package/dist/runtime/shared/layout.js +3 -0
  391. package/dist/runtime/shared/links.d.ts +48 -0
  392. package/dist/runtime/shared/links.js +155 -0
  393. package/dist/runtime/shared/list-filters.d.ts +2 -0
  394. package/dist/runtime/shared/list-filters.js +2 -0
  395. package/dist/runtime/shared/media-focal.d.ts +30 -0
  396. package/dist/runtime/shared/media-focal.js +52 -0
  397. package/dist/runtime/shared/migrations-dir.d.ts +14 -0
  398. package/dist/runtime/shared/migrations-dir.js +48 -2
  399. package/dist/runtime/shared/presets.d.ts +67 -0
  400. package/dist/runtime/shared/presets.js +127 -0
  401. package/dist/runtime/shared/preview-links.d.ts +64 -0
  402. package/dist/runtime/shared/preview-links.js +54 -0
  403. package/dist/runtime/shared/preview.d.ts +124 -21
  404. package/dist/runtime/shared/preview.js +419 -81
  405. package/dist/runtime/shared/redirects.d.ts +75 -0
  406. package/dist/runtime/shared/redirects.js +230 -0
  407. package/dist/runtime/shared/roles.d.ts +57 -0
  408. package/dist/runtime/shared/roles.js +293 -0
  409. package/dist/runtime/shared/save-state.d.ts +8 -0
  410. package/dist/runtime/shared/save-state.js +15 -0
  411. package/dist/runtime/shared/search.d.ts +16 -1
  412. package/dist/runtime/shared/search.js +41 -7
  413. package/dist/runtime/shared/seo.d.ts +167 -0
  414. package/dist/runtime/shared/seo.js +335 -0
  415. package/dist/runtime/shared/transfer.js +2 -1
  416. package/dist/runtime/shared/translations.d.ts +29 -0
  417. package/dist/runtime/shared/translations.js +123 -0
  418. package/dist/runtime/shared/users.d.ts +1 -1
  419. package/dist/runtime/shared/validation.d.ts +23 -1
  420. package/dist/runtime/shared/validation.js +214 -64
  421. package/dist/runtime/webhooks.d.ts +33 -0
  422. package/dist/runtime/webhooks.js +57 -0
  423. package/dist/types.d.mts +5 -1
  424. package/package.json +6 -2
  425. package/dist/runtime/app/components/cms/BlockOutline.vue +0 -88
  426. package/dist/runtime/app/components/cms/BlockPalette.vue +0 -31
  427. package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +0 -12
  428. package/dist/runtime/app/components/cms/SettingsModal.vue +0 -126
  429. package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +0 -12
  430. package/dist/runtime/app/composables/cms-field-focus.d.ts +0 -4
  431. package/dist/runtime/app/composables/cms-field-focus.js +0 -20
@@ -0,0 +1,75 @@
1
+ import type { CmsContentRouteItem, CmsLocalePath } from './content-routes.js';
2
+ import type { CmsEntry } from './index.js';
3
+ export declare const REDIRECT_STATUSES: readonly [301, 302, 307, 308];
4
+ export type CmsRedirectStatus = (typeof REDIRECT_STATUSES)[number];
5
+ export type CmsRedirectSource = 'manual' | 'auto';
6
+ export declare const DEFAULT_REDIRECT_STATUS: CmsRedirectStatus;
7
+ export declare const REDIRECT_FROM_MAX_LENGTH = 1024;
8
+ export declare const REDIRECT_TO_MAX_LENGTH = 2048;
9
+ export declare const REDIRECT_IMPORT_MAX_ROWS = 5000;
10
+ export declare const REDIRECT_CSV_COLUMNS: readonly ["from", "to", "status", "keepQuery"];
11
+ export interface CmsRedirectInput {
12
+ from: string;
13
+ to: string;
14
+ status?: CmsRedirectStatus;
15
+ keepQuery?: boolean;
16
+ }
17
+ export interface CmsRedirectDraft {
18
+ from: string;
19
+ to: string;
20
+ status: CmsRedirectStatus;
21
+ keepQuery: boolean;
22
+ }
23
+ export interface CmsRedirect extends CmsRedirectDraft {
24
+ id: string;
25
+ source: CmsRedirectSource;
26
+ createdAt: string;
27
+ updatedAt: string;
28
+ }
29
+ export interface CmsRedirectTarget {
30
+ to: string;
31
+ status: CmsRedirectStatus;
32
+ keepQuery: boolean;
33
+ }
34
+ export interface CmsRedirectMove {
35
+ from: string;
36
+ to: string;
37
+ }
38
+ export interface CmsRedirectLink {
39
+ id: string;
40
+ from: string;
41
+ to: string;
42
+ }
43
+ export interface CmsRedirectPlan {
44
+ upserts: CmsRedirectMove[];
45
+ retargets: {
46
+ id: string;
47
+ to: string;
48
+ }[];
49
+ deletes: string[];
50
+ }
51
+ export declare function normalizeRedirectPath(path: string): string;
52
+ export declare function isExternalRedirectTarget(to: string): boolean;
53
+ export declare function redirectTargetPath(to: string): string | null;
54
+ export declare function redirectApplies(path: string): boolean;
55
+ export declare function redirectFromErrors(from: unknown): string[];
56
+ export declare function redirectToErrors(to: unknown): string[];
57
+ export declare function isRedirectStatus(value: unknown): value is CmsRedirectStatus;
58
+ export declare function normalizeRedirectInput(input: CmsRedirectInput): CmsRedirectDraft;
59
+ export declare function redirectTargetMap(redirects: readonly Pick<CmsRedirectDraft, 'from' | 'to'>[]): Map<string, string | null>;
60
+ export declare function findRedirectLoop(targets: ReadonlyMap<string, string | null>, from: string, to: string): string[] | null;
61
+ export declare function redirectErrors(input: CmsRedirectInput, others: ReadonlyMap<string, string | null>): string[];
62
+ export declare function retargetRedirect(to: string, path: string): string;
63
+ export declare function redirectLocation(to: string, search: string, keepQuery: boolean): string;
64
+ export declare function isContentItemLive(item: CmsContentRouteItem, now?: Date): boolean;
65
+ export interface CmsContentMoves {
66
+ moves: CmsRedirectMove[];
67
+ livePaths: string[];
68
+ }
69
+ export declare function contentPathMoves(oldPaths: readonly CmsLocalePath[], newPaths: readonly CmsLocalePath[]): CmsContentMoves;
70
+ export declare function rowAfterUpdate<T extends Record<string, unknown>>(existing: T, set: Record<string, unknown>): T;
71
+ export declare function contentItemMoves(entry: Pick<CmsEntry, 'kind' | 'route'>, before: CmsContentRouteItem, after: CmsContentRouteItem, locales: readonly string[], now?: Date): CmsContentMoves;
72
+ export declare function mergeContentMoves(list: readonly CmsContentMoves[]): CmsContentMoves;
73
+ export declare function planRedirectMoves(existing: readonly CmsRedirectLink[], moves: readonly CmsRedirectMove[], livePaths: readonly string[]): CmsRedirectPlan;
74
+ export declare function redirectRowsFromTable(rows: string[][]): CmsRedirectInput[];
75
+ export declare function redirectsToTable(redirects: readonly CmsRedirectDraft[]): string[][];
@@ -0,0 +1,230 @@
1
+ import { contentItemPaths, isRoutedContent } from "./content-routes.js";
2
+ import { normalizeTimestamp } from "./timestamps.js";
3
+ export const REDIRECT_STATUSES = [301, 302, 307, 308];
4
+ export const DEFAULT_REDIRECT_STATUS = 301;
5
+ export const REDIRECT_FROM_MAX_LENGTH = 1024;
6
+ export const REDIRECT_TO_MAX_LENGTH = 2048;
7
+ export const REDIRECT_IMPORT_MAX_ROWS = 5e3;
8
+ export const REDIRECT_CSV_COLUMNS = ["from", "to", "status", "keepQuery"];
9
+ const RESERVED_SEGMENTS = ["cms", "api", "_nuxt"];
10
+ const SKIPPED_PREFIXES = ["/__nuxt"];
11
+ const UNSAFE_CHARACTERS = /[\s\\\u0000-\u001f\u007f]/;
12
+ const ABSOLUTE_URL = /^https?:\/\//i;
13
+ function canonicalSegment(segment) {
14
+ try {
15
+ return encodeURIComponent(decodeURIComponent(segment));
16
+ } catch {
17
+ return segment;
18
+ }
19
+ }
20
+ export function normalizeRedirectPath(path) {
21
+ const bare = path.replace(/[?#].*$/, "");
22
+ const segments = bare.split("/").filter(Boolean).map(canonicalSegment);
23
+ return `/${segments.join("/")}`;
24
+ }
25
+ export function isExternalRedirectTarget(to) {
26
+ return ABSOLUTE_URL.test(to);
27
+ }
28
+ export function redirectTargetPath(to) {
29
+ if (isExternalRedirectTarget(to) || !to.startsWith("/") || to.startsWith("//")) return null;
30
+ return normalizeRedirectPath(to);
31
+ }
32
+ function reservedSegment(path) {
33
+ const first = path.split("/")[1] ?? "";
34
+ return RESERVED_SEGMENTS.find((segment) => segment === first);
35
+ }
36
+ export function redirectApplies(path) {
37
+ const bare = path.replace(/[?#].*$/, "");
38
+ return !reservedSegment(normalizeRedirectPath(bare)) && !SKIPPED_PREFIXES.some((prefix) => bare.startsWith(prefix));
39
+ }
40
+ export function redirectFromErrors(from) {
41
+ if (typeof from !== "string" || !from.trim()) return ["From is required"];
42
+ if (from.length > REDIRECT_FROM_MAX_LENGTH)
43
+ return [`From is longer than ${REDIRECT_FROM_MAX_LENGTH} characters`];
44
+ if (!from.startsWith("/") || from.startsWith("//"))
45
+ return ["From must be a path starting with /, without a host"];
46
+ if (UNSAFE_CHARACTERS.test(from)) return ["From cannot hold spaces or backslashes"];
47
+ if (/[?#]/.test(from)) return ["From cannot hold a query or a hash"];
48
+ const reserved = reservedSegment(normalizeRedirectPath(from));
49
+ if (reserved) return [`From cannot be under /${reserved}`];
50
+ return [];
51
+ }
52
+ export function redirectToErrors(to) {
53
+ if (typeof to !== "string" || !to.trim()) return ["To is required"];
54
+ if (to.length > REDIRECT_TO_MAX_LENGTH)
55
+ return [`To is longer than ${REDIRECT_TO_MAX_LENGTH} characters`];
56
+ if (UNSAFE_CHARACTERS.test(to)) return ["To cannot hold spaces or backslashes"];
57
+ if (isExternalRedirectTarget(to)) {
58
+ try {
59
+ const url = new URL(to);
60
+ if (!url.hostname) return ["To must be a valid URL"];
61
+ if (url.username || url.password) return ["To cannot hold credentials"];
62
+ return [];
63
+ } catch {
64
+ return ["To must be a valid URL"];
65
+ }
66
+ }
67
+ if (!to.startsWith("/") || to.startsWith("//"))
68
+ return ["To must be a path starting with / or an absolute http(s) URL"];
69
+ return [];
70
+ }
71
+ export function isRedirectStatus(value) {
72
+ return REDIRECT_STATUSES.includes(value);
73
+ }
74
+ export function normalizeRedirectInput(input) {
75
+ return {
76
+ from: normalizeRedirectPath(input.from.trim()),
77
+ to: input.to.trim(),
78
+ status: input.status ?? DEFAULT_REDIRECT_STATUS,
79
+ keepQuery: input.keepQuery ?? true
80
+ };
81
+ }
82
+ export function redirectTargetMap(redirects) {
83
+ return new Map(redirects.map((redirect) => [redirect.from, redirectTargetPath(redirect.to)]));
84
+ }
85
+ export function findRedirectLoop(targets, from, to) {
86
+ const chain = [from];
87
+ const seen = /* @__PURE__ */ new Set([from]);
88
+ let current = redirectTargetPath(to);
89
+ while (current !== null) {
90
+ chain.push(current);
91
+ if (current === from) return chain;
92
+ if (seen.has(current)) return null;
93
+ seen.add(current);
94
+ const next = targets.get(current);
95
+ if (next === void 0) return null;
96
+ current = next;
97
+ }
98
+ return null;
99
+ }
100
+ export function redirectErrors(input, others) {
101
+ const errors = [...redirectFromErrors(input.from), ...redirectToErrors(input.to)];
102
+ if (input.status !== void 0 && !isRedirectStatus(input.status))
103
+ errors.push(`Status must be one of ${REDIRECT_STATUSES.join(", ")}`);
104
+ if (errors.length) return errors;
105
+ const draft = normalizeRedirectInput(input);
106
+ if (redirectTargetPath(draft.to) === draft.from) return ["A redirect cannot point to itself"];
107
+ if (others.has(draft.from)) return [`A redirect from ${draft.from} already exists`];
108
+ const loop = findRedirectLoop(others, draft.from, draft.to);
109
+ if (loop) return [`This redirect makes a loop: ${loop.join(" \u2192 ")}`];
110
+ return [];
111
+ }
112
+ function targetSuffix(to) {
113
+ const index = to.search(/[?#]/);
114
+ return index === -1 ? "" : to.slice(index);
115
+ }
116
+ export function retargetRedirect(to, path) {
117
+ return `${path}${targetSuffix(to)}`;
118
+ }
119
+ export function redirectLocation(to, search, keepQuery) {
120
+ const query = search.replace(/^\?/, "");
121
+ if (!keepQuery || !query) return to;
122
+ const hashIndex = to.indexOf("#");
123
+ const base = hashIndex === -1 ? to : to.slice(0, hashIndex);
124
+ const hash = hashIndex === -1 ? "" : to.slice(hashIndex);
125
+ const joiner = !base.includes("?") ? "?" : /[?&]$/.test(base) ? "" : "&";
126
+ return `${base}${joiner}${query}${hash}`;
127
+ }
128
+ export function isContentItemLive(item, now = /* @__PURE__ */ new Date()) {
129
+ if (item.status !== "published") return false;
130
+ const publishedAt = normalizeTimestamp(item.publishedAt);
131
+ if (typeof publishedAt !== "string" || !publishedAt) return false;
132
+ const date = new Date(publishedAt);
133
+ return !Number.isNaN(date.getTime()) && date.getTime() <= now.getTime();
134
+ }
135
+ export function contentPathMoves(oldPaths, newPaths) {
136
+ const livePaths = [...new Set(newPaths.map((entry) => normalizeRedirectPath(entry.path)))];
137
+ const live = new Set(livePaths);
138
+ const moves = /* @__PURE__ */ new Map();
139
+ for (const old of oldPaths) {
140
+ const next = newPaths.find((entry) => entry.locale === old.locale);
141
+ if (!next) continue;
142
+ const from = normalizeRedirectPath(old.path);
143
+ if (from === normalizeRedirectPath(next.path) || live.has(from) || moves.has(from)) continue;
144
+ moves.set(from, next.path);
145
+ }
146
+ return { moves: [...moves].map(([from, to]) => ({ from, to })), livePaths };
147
+ }
148
+ export function rowAfterUpdate(existing, set) {
149
+ const next = { ...existing };
150
+ for (const [key, value] of Object.entries(set)) if (value !== void 0) next[key] = value;
151
+ if (Object.hasOwn(existing, "status") && next.status == null) next.status = existing.status;
152
+ return next;
153
+ }
154
+ export function contentItemMoves(entry, before, after, locales, now = /* @__PURE__ */ new Date()) {
155
+ if (!isRoutedContent(entry) || !isContentItemLive(before, now) || !isContentItemLive(after, now))
156
+ return { moves: [], livePaths: [] };
157
+ return contentPathMoves(
158
+ contentItemPaths(entry, before, locales),
159
+ contentItemPaths(entry, after, locales)
160
+ );
161
+ }
162
+ export function mergeContentMoves(list) {
163
+ const livePaths = [...new Set(list.flatMap((moves2) => moves2.livePaths))];
164
+ const live = new Set(livePaths.map(normalizeRedirectPath));
165
+ const moves = list.flatMap((item) => item.moves).filter((move) => !live.has(normalizeRedirectPath(move.from)));
166
+ return { moves, livePaths };
167
+ }
168
+ export function planRedirectMoves(existing, moves, livePaths) {
169
+ const live = new Set(livePaths.map(normalizeRedirectPath));
170
+ const moved = new Map(moves.map((move) => [normalizeRedirectPath(move.from), move.to]));
171
+ const deletes = /* @__PURE__ */ new Set();
172
+ const retargets = [];
173
+ for (const redirect of existing) {
174
+ if (live.has(redirect.from)) {
175
+ deletes.add(redirect.id);
176
+ continue;
177
+ }
178
+ if (moved.has(redirect.from)) continue;
179
+ const target = redirectTargetPath(redirect.to);
180
+ const next = target === null ? void 0 : moved.get(target);
181
+ if (next === void 0) continue;
182
+ const to = retargetRedirect(redirect.to, next);
183
+ if (redirectTargetPath(to) === redirect.from) deletes.add(redirect.id);
184
+ else retargets.push({ id: redirect.id, to });
185
+ }
186
+ return {
187
+ upserts: [...moved].map(([from, to]) => ({ from, to })),
188
+ retargets,
189
+ deletes: [...deletes]
190
+ };
191
+ }
192
+ function csvBoolean(value) {
193
+ const text = (value ?? "").trim().toLowerCase();
194
+ if (!text) return void 0;
195
+ if (["true", "1", "yes"].includes(text)) return true;
196
+ if (["false", "0", "no"].includes(text)) return false;
197
+ return void 0;
198
+ }
199
+ export function redirectRowsFromTable(rows) {
200
+ const [header, ...body] = rows;
201
+ if (!header) return [];
202
+ const columns = header.map((cell) => cell.trim().toLowerCase());
203
+ const index = (name) => columns.indexOf(name.toLowerCase());
204
+ const fromIndex = index("from");
205
+ const toIndex = index("to");
206
+ if (fromIndex === -1 || toIndex === -1) throw new Error("The CSV needs a from and a to column");
207
+ const statusIndex = index("status");
208
+ const keepIndex = index("keepQuery");
209
+ return body.map((cells) => {
210
+ const status = statusIndex === -1 ? "" : (cells[statusIndex] ?? "").trim();
211
+ const keepQuery = keepIndex === -1 ? void 0 : csvBoolean(cells[keepIndex]);
212
+ return {
213
+ from: cells[fromIndex] ?? "",
214
+ to: cells[toIndex] ?? "",
215
+ ...status ? { status: Number(status) } : {},
216
+ ...keepQuery === void 0 ? {} : { keepQuery }
217
+ };
218
+ });
219
+ }
220
+ export function redirectsToTable(redirects) {
221
+ return [
222
+ [...REDIRECT_CSV_COLUMNS],
223
+ ...redirects.map((redirect) => [
224
+ redirect.from,
225
+ redirect.to,
226
+ String(redirect.status),
227
+ redirect.keepQuery ? "true" : "false"
228
+ ])
229
+ ];
230
+ }
@@ -0,0 +1,57 @@
1
+ import type { CmsConfig, CmsEntry } from './index.js';
2
+ export declare const CMS_ADMIN_ROLE = "admin";
3
+ export declare const CMS_SUPERADMIN_ROLE = "superadmin";
4
+ export declare const CMS_RESERVED_ROLES: readonly ["admin", "superadmin"];
5
+ export declare const CMS_ENTRY_PERMISSION_ACTIONS: readonly ["read", "create", "update", "delete", "publish", "import", "export"];
6
+ export declare const CMS_MEDIA_PERMISSION_ACTIONS: readonly ["read", "write", "delete"];
7
+ export declare const CMS_SYSTEM_SCOPES: {
8
+ readonly media: readonly ["read", "write", "delete"];
9
+ readonly users: readonly ["manage"];
10
+ readonly settings: readonly ["manage"];
11
+ readonly redirects: readonly ["manage"];
12
+ };
13
+ export type CmsEntryPermissionAction = (typeof CMS_ENTRY_PERMISSION_ACTIONS)[number];
14
+ export type CmsSystemScope = keyof typeof CMS_SYSTEM_SCOPES;
15
+ export interface CmsRoleInput {
16
+ label?: string;
17
+ description?: string;
18
+ can: string[];
19
+ }
20
+ export type CmsRolesInput = Record<string, CmsRoleInput>;
21
+ export interface CmsRole {
22
+ label: string;
23
+ description: string | null;
24
+ can: string[];
25
+ }
26
+ export type CmsRoles = Record<string, CmsRole>;
27
+ export interface CmsRoleOption {
28
+ key: string;
29
+ label: string;
30
+ description: string | null;
31
+ }
32
+ export interface CmsAccess {
33
+ role: string;
34
+ known: boolean;
35
+ full: boolean;
36
+ permissions: string[];
37
+ }
38
+ export declare function roleLabel(key: string): string;
39
+ export declare function entryPermissionActions(entry: Pick<CmsEntry, 'kind' | 'drafts'>): CmsEntryPermissionAction[];
40
+ export declare function cmsPermissions(config: CmsConfig): string[];
41
+ export declare function matchesPermission(pattern: string, permission: string): boolean;
42
+ export declare function expandPermissions(patterns: readonly string[], config: CmsConfig): string[];
43
+ export declare function resolveCmsAccess(role: string | null | undefined, roles: CmsRoles, config: CmsConfig): CmsAccess;
44
+ export declare function can(access: CmsAccess | null | undefined, permission: string): boolean;
45
+ export declare function canAny(access: CmsAccess | null | undefined, permissions: string[]): boolean;
46
+ export declare function publishPermissionError(access: CmsAccess, name: string, entry: Pick<CmsEntry, 'drafts'>, previous: unknown, next: unknown): string | null;
47
+ export declare function deletePermissionError(access: CmsAccess, name: string, entry: Pick<CmsEntry, 'drafts'>, status: unknown): string | null;
48
+ export declare function canGrantRole(access: CmsAccess | null | undefined, role: string, roles: CmsRoles, config: CmsConfig): boolean;
49
+ export declare function canManageAccount(access: CmsAccess | null | undefined, role: string, roles: CmsRoles, config: CmsConfig): boolean;
50
+ export declare function cmsRoleOptions(roles: CmsRoles): CmsRoleOption[];
51
+ export declare function cmsRoleLabel(role: string | null | undefined, roles: CmsRoles): string;
52
+ export declare function resolveRoles(input: CmsRolesInput | undefined): CmsRoles;
53
+ export declare function validateRoles(input: unknown, config: CmsConfig): {
54
+ roles: CmsRoles;
55
+ errors: string[];
56
+ warnings: string[];
57
+ };
@@ -0,0 +1,293 @@
1
+ import { declaredLinkTargets, entryFieldsFor, isCollectionKind, relationTarget } from "./index.js";
2
+ export const CMS_ADMIN_ROLE = "admin";
3
+ export const CMS_SUPERADMIN_ROLE = "superadmin";
4
+ export const CMS_RESERVED_ROLES = [CMS_ADMIN_ROLE, CMS_SUPERADMIN_ROLE];
5
+ export const CMS_ENTRY_PERMISSION_ACTIONS = [
6
+ "read",
7
+ "create",
8
+ "update",
9
+ "delete",
10
+ "publish",
11
+ "import",
12
+ "export"
13
+ ];
14
+ export const CMS_MEDIA_PERMISSION_ACTIONS = ["read", "write", "delete"];
15
+ export const CMS_SYSTEM_SCOPES = {
16
+ media: CMS_MEDIA_PERMISSION_ACTIONS,
17
+ users: ["manage"],
18
+ settings: ["manage"],
19
+ redirects: ["manage"]
20
+ };
21
+ const ROLE_KEY_PATTERN = /^[a-z][a-zA-Z0-9_-]*$/;
22
+ const ROLE_KEYS = /* @__PURE__ */ new Set(["label", "description", "can"]);
23
+ const SYSTEM_SCOPES = Object.keys(CMS_SYSTEM_SCOPES);
24
+ function isSystemScope(scope) {
25
+ return Object.hasOwn(CMS_SYSTEM_SCOPES, scope);
26
+ }
27
+ function isRecord(value) {
28
+ return !!value && typeof value === "object" && !Array.isArray(value);
29
+ }
30
+ export function roleLabel(key) {
31
+ const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim().toLowerCase();
32
+ return words.charAt(0).toUpperCase() + words.slice(1);
33
+ }
34
+ export function entryPermissionActions(entry) {
35
+ if (!isCollectionKind(entry)) return ["read", "update"];
36
+ return CMS_ENTRY_PERMISSION_ACTIONS.filter((action) => action !== "publish" || !!entry.drafts);
37
+ }
38
+ export function cmsPermissions(config) {
39
+ return [
40
+ ...Object.entries(config).flatMap(
41
+ ([name, entry]) => entryPermissionActions(entry).map((action) => `${name}.${action}`)
42
+ ),
43
+ ...SYSTEM_SCOPES.flatMap(
44
+ (scope) => CMS_SYSTEM_SCOPES[scope].map((action) => `${scope}.${action}`)
45
+ )
46
+ ];
47
+ }
48
+ function splitPermission(value) {
49
+ const dot = value.indexOf(".");
50
+ return dot === -1 ? [value, ""] : [value.slice(0, dot), value.slice(dot + 1)];
51
+ }
52
+ export function matchesPermission(pattern, permission) {
53
+ if (pattern === "*") return true;
54
+ const [patternScope, patternAction] = splitPermission(pattern);
55
+ const [scope, action] = splitPermission(permission);
56
+ return (patternScope === "*" || patternScope === scope) && (patternAction === "*" || patternAction === action);
57
+ }
58
+ export function expandPermissions(patterns, config) {
59
+ const universe = cmsPermissions(config);
60
+ const granted = new Set(
61
+ universe.filter(
62
+ (permission) => patterns.some((pattern) => matchesPermission(pattern, permission))
63
+ )
64
+ );
65
+ for (const permission of [...granted]) {
66
+ const [scope, action] = splitPermission(permission);
67
+ if (action === "manage") continue;
68
+ const read = `${scope}.read`;
69
+ if (universe.includes(read)) granted.add(read);
70
+ }
71
+ return universe.filter((permission) => granted.has(permission));
72
+ }
73
+ export function resolveCmsAccess(role, roles, config) {
74
+ const key = role || CMS_ADMIN_ROLE;
75
+ if (key === CMS_SUPERADMIN_ROLE) {
76
+ return { role: key, known: true, full: true, permissions: cmsPermissions(config) };
77
+ }
78
+ if (key === CMS_ADMIN_ROLE) {
79
+ return {
80
+ role: key,
81
+ known: true,
82
+ full: false,
83
+ permissions: cmsPermissions(config).filter((permission) => permission !== "users.manage")
84
+ };
85
+ }
86
+ const definition = Object.hasOwn(roles, key) ? roles[key] : void 0;
87
+ if (!definition) return { role: key, known: false, full: false, permissions: [] };
88
+ return {
89
+ role: key,
90
+ known: true,
91
+ full: false,
92
+ permissions: expandPermissions(definition.can, config)
93
+ };
94
+ }
95
+ export function can(access, permission) {
96
+ if (!access?.known) return false;
97
+ return access.full || access.permissions.includes(permission);
98
+ }
99
+ export function canAny(access, permissions) {
100
+ return permissions.some((permission) => can(access, permission));
101
+ }
102
+ export function publishPermissionError(access, name, entry, previous, next) {
103
+ if (!entry.drafts || can(access, `${name}.publish`)) return null;
104
+ if (previous === "published") {
105
+ return `Editing a published item needs the ${name}.publish permission`;
106
+ }
107
+ if (next === "published") return `Publishing needs the ${name}.publish permission`;
108
+ return null;
109
+ }
110
+ export function deletePermissionError(access, name, entry, status) {
111
+ if (!entry.drafts || status !== "published" || can(access, `${name}.publish`)) return null;
112
+ return `Deleting a published or scheduled item needs the ${name}.publish permission`;
113
+ }
114
+ export function canGrantRole(access, role, roles, config) {
115
+ if (!access?.known || role === CMS_SUPERADMIN_ROLE) return false;
116
+ const target = resolveCmsAccess(role, roles, config);
117
+ if (!target.known) return false;
118
+ if (access.full) return true;
119
+ if (target.full) return false;
120
+ return target.permissions.every((permission) => access.permissions.includes(permission));
121
+ }
122
+ export function canManageAccount(access, role, roles, config) {
123
+ if (!can(access, "users.manage") || role === CMS_SUPERADMIN_ROLE) return false;
124
+ if (!resolveCmsAccess(role, roles, config).known) return true;
125
+ return canGrantRole(access, role, roles, config);
126
+ }
127
+ export function cmsRoleOptions(roles) {
128
+ return [
129
+ {
130
+ key: CMS_ADMIN_ROLE,
131
+ label: "Admin",
132
+ description: "Everything in the panel except managing users."
133
+ },
134
+ ...Object.entries(roles).map(([key, role]) => ({
135
+ key,
136
+ label: role.label,
137
+ description: role.description
138
+ }))
139
+ ];
140
+ }
141
+ export function cmsRoleLabel(role, roles) {
142
+ const key = role || CMS_ADMIN_ROLE;
143
+ if (key === CMS_SUPERADMIN_ROLE) return "Super admin";
144
+ if (key === CMS_ADMIN_ROLE) return "Admin";
145
+ return Object.hasOwn(roles, key) ? roles[key].label : key;
146
+ }
147
+ export function resolveRoles(input) {
148
+ return Object.fromEntries(
149
+ Object.entries(input ?? {}).map(([key, role]) => [
150
+ key,
151
+ {
152
+ label: role.label?.trim() || roleLabel(key),
153
+ description: role.description?.trim() || null,
154
+ can: [...new Set(role.can)]
155
+ }
156
+ ])
157
+ );
158
+ }
159
+ function permissionError(permission, config, allActions) {
160
+ if (permission === "*") return null;
161
+ const parts = permission.split(".");
162
+ if (parts.length !== 2 || parts.some((part) => !part)) {
163
+ return `'${permission}' must be '*' or '<scope>.<action>' (for example 'news.update' or 'media.write')`;
164
+ }
165
+ const [scope, action] = parts;
166
+ if (scope === "*") {
167
+ if (action === "*" || allActions.has(action)) return null;
168
+ return `'${permission}': unknown action '${action}' (expected one of ${[...allActions].join(
169
+ ", "
170
+ )})`;
171
+ }
172
+ const entry = Object.hasOwn(config, scope) ? config[scope] : void 0;
173
+ if (entry && isSystemScope(scope)) {
174
+ const systemActions = CMS_SYSTEM_SCOPES[scope];
175
+ if (action === "*") {
176
+ return `'${permission}' is ambiguous: '${scope}' is both an entry and a built-in scope, list the actions instead`;
177
+ }
178
+ if (systemActions.includes(action)) return null;
179
+ }
180
+ if (entry) {
181
+ if (action === "*") return null;
182
+ const actions = entryPermissionActions(entry);
183
+ if (actions.includes(action)) return null;
184
+ if (action === "publish" && isCollectionKind(entry)) {
185
+ return `'${permission}': entry '${scope}' has no drafts, so there is nothing to publish`;
186
+ }
187
+ return `'${permission}': ${entry.kind} entry '${scope}' supports ${actions.join(", ")}`;
188
+ }
189
+ if (isSystemScope(scope)) {
190
+ const actions = CMS_SYSTEM_SCOPES[scope];
191
+ if (action === "*" || actions.includes(action)) return null;
192
+ return `'${permission}': '${scope}' supports ${actions.join(", ")}`;
193
+ }
194
+ return `'${permission}': unknown entry or scope '${scope}' (expected an entry key, ${SYSTEM_SCOPES.join(
195
+ ", "
196
+ )} or '*')`;
197
+ }
198
+ function collectFields(fields) {
199
+ return Object.values(fields).flatMap((field) => [
200
+ field,
201
+ ...Object.values(field.blocks ?? {}).flatMap((block) => collectFields(block.fields))
202
+ ]);
203
+ }
204
+ function accessWarnings(key, permissions, config) {
205
+ const warnings = [];
206
+ const granted = new Set(permissions);
207
+ const relationTargets = /* @__PURE__ */ new Set();
208
+ let needsMedia = false;
209
+ for (const [name, entry] of Object.entries(config)) {
210
+ if (!granted.has(`${name}.create`) && !granted.has(`${name}.update`)) continue;
211
+ for (const field of collectFields(entryFieldsFor(entry))) {
212
+ if (field.type === "media") needsMedia = true;
213
+ if (field.type === "relation" && relationTarget(field))
214
+ relationTargets.add(relationTarget(field));
215
+ if (field.type === "link")
216
+ for (const target of declaredLinkTargets(field) ?? []) relationTargets.add(target);
217
+ }
218
+ }
219
+ if (needsMedia && !granted.has("media.read")) {
220
+ warnings.push(
221
+ `cms.roles.${key} edits entries with media fields but cannot pick media: add 'media.read'`
222
+ );
223
+ }
224
+ for (const target of relationTargets) {
225
+ if (!granted.has(`${target}.read`)) {
226
+ warnings.push(
227
+ `cms.roles.${key} edits entries that link to '${target}' but cannot list it: add '${target}.read'`
228
+ );
229
+ }
230
+ }
231
+ return warnings;
232
+ }
233
+ export function validateRoles(input, config) {
234
+ const errors = [];
235
+ const warnings = [];
236
+ if (input === void 0) return { roles: {}, errors, warnings };
237
+ if (!isRecord(input)) {
238
+ return {
239
+ roles: {},
240
+ errors: ["cms.roles must be an object like { editor: { label, can: ['news.*'] } }"],
241
+ warnings
242
+ };
243
+ }
244
+ const allActions = /* @__PURE__ */ new Set([
245
+ ...CMS_ENTRY_PERMISSION_ACTIONS,
246
+ ...SYSTEM_SCOPES.flatMap((scope) => CMS_SYSTEM_SCOPES[scope])
247
+ ]);
248
+ const valid = {};
249
+ for (const [key, role] of Object.entries(input)) {
250
+ const at = `cms.roles.${key}`;
251
+ const before = errors.length;
252
+ if (CMS_RESERVED_ROLES.includes(key)) {
253
+ errors.push(`${at}: '${key}' is a built-in role and always has full access`);
254
+ continue;
255
+ }
256
+ if (!ROLE_KEY_PATTERN.test(key)) {
257
+ errors.push(
258
+ `${at}: role key must start with a lowercase letter and use letters, digits, '-' or '_'`
259
+ );
260
+ }
261
+ if (!isRecord(role)) {
262
+ errors.push(`${at} must be an object like { label, can: [...] }`);
263
+ continue;
264
+ }
265
+ for (const name of Object.keys(role)) {
266
+ if (!ROLE_KEYS.has(name)) errors.push(`${at} has an unknown key '${name}'`);
267
+ }
268
+ if (role.label !== void 0 && (typeof role.label !== "string" || !role.label.trim())) {
269
+ errors.push(`${at}.label must be a non-empty string`);
270
+ }
271
+ if (role.description !== void 0 && typeof role.description !== "string") {
272
+ errors.push(`${at}.description must be a string`);
273
+ }
274
+ if (!Array.isArray(role.can)) {
275
+ errors.push(`${at}.can must be an array of permissions`);
276
+ continue;
277
+ }
278
+ for (const permission of role.can) {
279
+ if (typeof permission !== "string") {
280
+ errors.push(`${at}.can must only hold strings`);
281
+ continue;
282
+ }
283
+ const error = permissionError(permission, config, allActions);
284
+ if (error) errors.push(`${at}.can: ${error}`);
285
+ }
286
+ if (errors.length === before) valid[key] = role;
287
+ }
288
+ const roles = resolveRoles(valid);
289
+ for (const [key, role] of Object.entries(roles)) {
290
+ warnings.push(...accessWarnings(key, expandPermissions(role.can, config), config));
291
+ }
292
+ return { roles, errors, warnings };
293
+ }
@@ -0,0 +1,8 @@
1
+ type State = Record<string, unknown>;
2
+ export declare function cloneState<T extends State>(state: T): T;
3
+ export interface CmsSavedState {
4
+ state: State;
5
+ clean: boolean;
6
+ }
7
+ export declare function mergeSavedState(submitted: State, current: State, saved: State): CmsSavedState;
8
+ export {};
@@ -0,0 +1,15 @@
1
+ function sameValue(a, b) {
2
+ return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
3
+ }
4
+ export function cloneState(state) {
5
+ return JSON.parse(JSON.stringify(state));
6
+ }
7
+ export function mergeSavedState(submitted, current, saved) {
8
+ if (sameValue(current, submitted)) return { state: { ...saved }, clean: true };
9
+ const state = {};
10
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(saved), ...Object.keys(current)])) {
11
+ const edited = Object.hasOwn(current, key) && !sameValue(current[key], submitted[key]);
12
+ state[key] = edited || !Object.hasOwn(saved, key) ? current[key] : saved[key];
13
+ }
14
+ return { state, clean: sameValue(state, saved) };
15
+ }