@xleddyl/nuxt-cms 0.9.4 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 -219
  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 -147
  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 +19 -11
  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 +40 -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 +8 -8
  124. package/dist/runtime/app/components/cms/TranslationBadges.d.vue.ts +7 -0
  125. package/dist/runtime/app/components/cms/TranslationBadges.vue +44 -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 +1153 -338
  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 +191 -54
  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,123 @@
1
+ import { isFieldVisible } from "./conditions.js";
2
+ import {
3
+ decodeTranslatableValue,
4
+ entryFieldsFor,
5
+ hasTranslatableBlockFields,
6
+ isCollectionKind,
7
+ isLocalizedLinkField,
8
+ isTranslatableField
9
+ } from "./index.js";
10
+ import { stripMarkup } from "./search.js";
11
+ export const TRANSLATIONS_COLUMN = "_translations";
12
+ export function isTranslationFilled(field, value) {
13
+ if (typeof value !== "string") return false;
14
+ return (field.type === "richtext" ? stripMarkup(value) : value).trim() !== "";
15
+ }
16
+ export function hasTranslatableContent(fields) {
17
+ return Object.values(fields).some(
18
+ (field) => isTranslatableField(field) || isLocalizedLinkField(field) || hasTranslatableBlockFields(field)
19
+ );
20
+ }
21
+ export function entryHasTranslations(entry) {
22
+ return hasTranslatableContent(entryFieldsFor(entry));
23
+ }
24
+ export function hasTranslationsColumn(entry, i18n) {
25
+ return i18n.locales.length > 1 && isCollectionKind(entry) && !Object.hasOwn(entry.fields, TRANSLATIONS_COLUMN) && hasTranslatableContent(entry.fields);
26
+ }
27
+ function visitBlocks(field, value, defaultLocale, visit) {
28
+ if (!Array.isArray(value)) return void 0;
29
+ let changed = false;
30
+ const items = value.map((item) => {
31
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
32
+ const block = field.blocks?.[String(item.type)];
33
+ if (!block) return item;
34
+ const next = visitTranslations(block.fields, item, defaultLocale, visit);
35
+ if (next !== item) changed = true;
36
+ return next;
37
+ });
38
+ return changed ? items : void 0;
39
+ }
40
+ function visitLinkLabel(field, value, defaultLocale, visit) {
41
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
42
+ const { label, ...rest } = value;
43
+ const translations = typeof label === "string" ? { [defaultLocale]: label } : label && typeof label === "object" && !Array.isArray(label) ? label : null;
44
+ const next = visit(field, translations);
45
+ if (next === void 0) return void 0;
46
+ return next && Object.keys(next).length ? { ...rest, label: next } : rest;
47
+ }
48
+ function visitTranslations(fields, values, defaultLocale, visit, keys) {
49
+ let next = null;
50
+ for (const key of keys ?? Object.keys(fields)) {
51
+ const field = Object.hasOwn(fields, key) ? fields[key] : void 0;
52
+ if (!field || !isFieldVisible(field, values, fields)) continue;
53
+ const replaced = isTranslatableField(field) ? visit(field, decodeTranslatableValue(values[key], defaultLocale)) : isLocalizedLinkField(field) ? visitLinkLabel(field, values[key], defaultLocale, visit) : field.type === "blocks" ? visitBlocks(field, values[key], defaultLocale, visit) : void 0;
54
+ if (replaced === void 0) continue;
55
+ next ??= { ...values };
56
+ next[key] = replaced;
57
+ }
58
+ return next ?? values;
59
+ }
60
+ function translationState(filled, total) {
61
+ if (filled >= total) return "complete";
62
+ return filled ? "partial" : "missing";
63
+ }
64
+ export function translationProgress(fields, values, i18n, keys) {
65
+ const slots = [];
66
+ visitTranslations(
67
+ fields,
68
+ values ?? {},
69
+ i18n.defaultLocale,
70
+ (field, translations) => {
71
+ if (translations) slots.push({ field, translations });
72
+ return void 0;
73
+ },
74
+ keys
75
+ );
76
+ const sources = slots.filter(
77
+ ({ field, translations }) => isTranslationFilled(field, translations[i18n.defaultLocale])
78
+ );
79
+ const total = sources.length;
80
+ return Object.fromEntries(
81
+ i18n.locales.map((locale) => {
82
+ const filled = sources.filter(
83
+ ({ field, translations }) => isTranslationFilled(field, translations[locale])
84
+ ).length;
85
+ const written = slots.filter(
86
+ ({ field, translations }) => isTranslationFilled(field, translations[locale])
87
+ ).length;
88
+ return [locale, { filled, total, values: written, state: translationState(filled, total) }];
89
+ })
90
+ );
91
+ }
92
+ export function translationSummary(progress) {
93
+ if (!progress?.total) return "Nothing to translate yet";
94
+ const noun = progress.total === 1 ? "value" : "values";
95
+ return `${progress.filled} of ${progress.total} ${noun} translated`;
96
+ }
97
+ export function copyTranslations(fields, values, i18n, { from, to, overwrite = false }) {
98
+ let count = 0;
99
+ if (from === to) return { values, count };
100
+ const next = visitTranslations(fields, values, i18n.defaultLocale, (field, translations) => {
101
+ const source = translations?.[from];
102
+ if (!translations || !isTranslationFilled(field, source)) return void 0;
103
+ const target = translations[to];
104
+ if (target === source) return void 0;
105
+ if (!overwrite && isTranslationFilled(field, target)) return void 0;
106
+ count++;
107
+ return { ...translations, [to]: source };
108
+ });
109
+ return { values: next, count };
110
+ }
111
+ export function clearTranslations(fields, values, i18n, locale) {
112
+ let count = 0;
113
+ if (locale === i18n.defaultLocale) return { values, count };
114
+ const next = visitTranslations(fields, values, i18n.defaultLocale, (field, translations) => {
115
+ if (!translations || !Object.hasOwn(translations, locale)) return void 0;
116
+ if (isTranslationFilled(field, translations[locale])) count++;
117
+ const rest = Object.fromEntries(
118
+ Object.entries(translations).filter(([code]) => code !== locale)
119
+ );
120
+ return Object.keys(rest).length ? rest : null;
121
+ });
122
+ return { values: next, count };
123
+ }
@@ -1,4 +1,4 @@
1
- export type CmsUserRole = 'superadmin' | 'admin';
1
+ export type CmsUserRole = 'superadmin' | 'admin' | (string & {});
2
2
  export interface CmsSessionUser {
3
3
  id?: string;
4
4
  email: string;
@@ -1,6 +1,13 @@
1
1
  import { z } from 'zod';
2
- import type { CmsEntry, CmsI18n, CmsValidationLevel, FieldConfig } from './index.js';
2
+ import type { CmsConfig, CmsEntry, CmsI18n, CmsValidationLevel, FieldConfig } from './index.js';
3
3
  export declare const objectKeySchema: z.ZodString;
4
+ export declare const mediaUpdateSchema: z.ZodObject<{
5
+ key: z.ZodString;
6
+ alt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
7
+ folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
8
+ focalX: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
9
+ focalY: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
10
+ }, z.core.$strip>;
4
11
  export interface ValidationMessages {
5
12
  required: string;
6
13
  invalidDate: string;
@@ -12,10 +19,24 @@ export interface ValidationMessages {
12
19
  minBlocks: (min: number) => string;
13
20
  maxBlocks: (max: number) => string;
14
21
  maxTags: (max: number) => string;
22
+ minValue: (min: number) => string;
23
+ maxValue: (max: number) => string;
24
+ minLength: (min: number) => string;
25
+ maxLength: (max: number) => string;
26
+ unique: string;
27
+ immutable: string;
28
+ linkTarget: string;
29
+ linkUrlAndTarget: string;
30
+ linkExternal: string;
31
+ linkEntry: (entry: string) => string;
32
+ linkUnknownPage: string;
33
+ invalidLinkLabel: string;
15
34
  }
35
+ export declare function validationMessages(messages?: Partial<ValidationMessages>): ValidationMessages;
16
36
  export declare function datetimeSchema(message: string): z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
17
37
  export declare function blocksCountMessage(field: Pick<FieldConfig, 'min' | 'max'>, count: number, messages?: Partial<ValidationMessages>): string | null;
18
38
  export declare function tagsCountMessage(field: Pick<FieldConfig, 'max'>, count: number, messages?: Partial<ValidationMessages>): string | null;
39
+ export declare function valueBoundsMessage(field: Pick<FieldConfig, 'type' | 'min' | 'max'>, value: unknown, messages?: Partial<ValidationMessages>): string | null;
19
40
  export interface EntryValidationIssue {
20
41
  path: (string | number)[];
21
42
  message: string;
@@ -27,6 +48,7 @@ export declare function entryValidationWarnings(entry: ValidatedEntry, values: R
27
48
  export declare function buildEntrySchema(entry: ValidatedEntry & {
28
49
  drafts?: boolean;
29
50
  path?: string;
51
+ config?: CmsConfig;
30
52
  }, i18n: CmsI18n, messages?: Partial<ValidationMessages>): z.ZodObject<{
31
53
  [x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
32
54
  }, z.core.$strip>;
@@ -2,12 +2,31 @@ import { z } from "zod";
2
2
  import {
3
3
  BLOCK_HIDDEN_KEY,
4
4
  declaresHiddenField,
5
+ focalPointIssue,
6
+ allowsExternalLinks,
7
+ declaredLinkTargets,
5
8
  isFieldVisible,
6
9
  isTranslatableField,
10
+ linkTargetNames,
11
+ linkUrlError,
7
12
  normalizeTags,
8
- selectOptionValues
13
+ pageLinkPath,
14
+ formatBlockPath,
15
+ selectOptionValues,
16
+ walkBlocks
9
17
  } from "./index.js";
10
18
  export const objectKeySchema = z.string().min(1).max(1024).refine((k) => !k.includes("..") && !k.startsWith("/"), "Invalid object key");
19
+ const focalCoordinateSchema = z.number().min(0).max(1).nullish();
20
+ export const mediaUpdateSchema = z.object({
21
+ key: objectKeySchema,
22
+ alt: z.string().max(1024).nullish(),
23
+ folder: z.string().max(255).nullish(),
24
+ focalX: focalCoordinateSchema,
25
+ focalY: focalCoordinateSchema
26
+ }).superRefine((body, ctx) => {
27
+ const message = focalPointIssue(body.focalX, body.focalY);
28
+ if (message) ctx.addIssue({ code: "custom", message, path: ["focalX"] });
29
+ });
11
30
  const DEFAULT_MESSAGES = {
12
31
  required: "Required field",
13
32
  invalidDate: "Invalid date (yyyy-mm-dd)",
@@ -18,8 +37,23 @@ const DEFAULT_MESSAGES = {
18
37
  requiredLocale: (locale) => `Required field (locale '${locale}')`,
19
38
  minBlocks: (min) => `Add at least ${min} ${min === 1 ? "block" : "blocks"}`,
20
39
  maxBlocks: (max) => `Add at most ${max} ${max === 1 ? "block" : "blocks"}`,
21
- maxTags: (max) => `Add at most ${max} ${max === 1 ? "tag" : "tags"}`
40
+ maxTags: (max) => `Add at most ${max} ${max === 1 ? "tag" : "tags"}`,
41
+ minValue: (min) => `Must be at least ${min}`,
42
+ maxValue: (max) => `Must be at most ${max}`,
43
+ minLength: (min) => `Use at least ${min} ${min === 1 ? "character" : "characters"}`,
44
+ maxLength: (max) => `Use at most ${max} ${max === 1 ? "character" : "characters"}`,
45
+ unique: "This value is already used by another entry",
46
+ immutable: "This value cannot be changed once saved",
47
+ linkTarget: "Choose a page or enter a URL",
48
+ linkUrlAndTarget: "A link has either a URL or a target, not both",
49
+ linkExternal: "This link cannot point to an external URL",
50
+ linkEntry: (entry) => `This link cannot point to '${entry}'`,
51
+ linkUnknownPage: "Unknown page",
52
+ invalidLinkLabel: "Invalid link text"
22
53
  };
54
+ export function validationMessages(messages) {
55
+ return { ...DEFAULT_MESSAGES, ...messages };
56
+ }
23
57
  const DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
24
58
  export function datetimeSchema(message) {
25
59
  return z.string().trim().refine(
@@ -50,7 +84,8 @@ function scalarSchema(field, m) {
50
84
  case "json":
51
85
  return z.unknown();
52
86
  default:
53
- return field.required ? z.string().trim().min(1, m.required) : z.string();
87
+ if (field.required) return z.string().trim().min(1, m.required);
88
+ return field.unique ? z.string().transform((value) => value.trim() ? value : null) : z.string();
54
89
  }
55
90
  }
56
91
  function optionalize(field, base, m) {
@@ -64,26 +99,101 @@ function translatableSchema(field, i18n, m) {
64
99
  }).refine((v) => Object.keys(v).every((k) => locales.includes(k)), m.unknownLocale);
65
100
  return field.required ? record.refine((v) => !!v[defaultLocale]?.trim(), m.requiredLocale(defaultLocale)) : record.nullish().transform((v) => v ?? null);
66
101
  }
67
- function blockFieldSchema(blockField, i18n, m) {
102
+ function isRecord(value) {
103
+ return !!value && typeof value === "object" && !Array.isArray(value);
104
+ }
105
+ function trimmed(value) {
106
+ return typeof value === "string" ? value.trim() : "";
107
+ }
108
+ function linkLabelValue(field, raw, i18n, m) {
109
+ if (!field.withLabel || raw == null) return {};
110
+ if (field.translatable && i18n.locales.length) {
111
+ const entries = isRecord(raw) ? Object.entries(raw) : typeof raw === "string" ? [[i18n.defaultLocale, raw]] : null;
112
+ if (!entries) return { error: m.invalidLinkLabel };
113
+ if (entries.some(([locale]) => !i18n.locales.includes(locale)))
114
+ return { error: m.unknownLocale };
115
+ const label2 = Object.fromEntries(
116
+ entries.map(([locale, text]) => [locale, trimmed(text)]).filter(([, text]) => !!text)
117
+ );
118
+ return Object.keys(label2).length ? { label: label2 } : {};
119
+ }
120
+ if (typeof raw !== "string") return { error: m.invalidLinkLabel };
121
+ const label = raw.trim();
122
+ return label ? { label } : {};
123
+ }
124
+ function linkSchema(field, i18n, m, config) {
125
+ const targets = config ? linkTargetNames(config, field) : declaredLinkTargets(field);
126
+ const parse = z.unknown().nullish().transform((input, ctx) => {
127
+ const fail = (message) => {
128
+ ctx.addIssue({ code: "custom", message });
129
+ return z.NEVER;
130
+ };
131
+ if (input == null) return null;
132
+ if (!isRecord(input)) return fail(m.linkTarget);
133
+ const url = trimmed(input.url);
134
+ const entry = trimmed(input.entry);
135
+ const id = trimmed(input.id);
136
+ const { label, error } = linkLabelValue(field, input.label, i18n, m);
137
+ if (error) return fail(error);
138
+ if (url && (entry || id)) return fail(m.linkUrlAndTarget);
139
+ const extra = {
140
+ ...label !== void 0 ? { label } : {},
141
+ ...input.newTab === true ? { newTab: true } : {}
142
+ };
143
+ if (url) {
144
+ if (!allowsExternalLinks(field)) return fail(m.linkExternal);
145
+ const urlError = linkUrlError(url);
146
+ if (urlError) return fail(urlError);
147
+ return { url, ...extra };
148
+ }
149
+ if (!entry && !id) return label !== void 0 ? fail(m.linkTarget) : null;
150
+ if (!entry || !id) return fail(m.linkTarget);
151
+ if (targets && !targets.includes(entry)) return fail(m.linkEntry(entry));
152
+ const target = config?.[entry];
153
+ if (target?.kind === "page" && !pageLinkPath(target, id)) return fail(m.linkUnknownPage);
154
+ return { entry, id, ...extra };
155
+ });
156
+ if (!field.required) return parse;
157
+ return parse.refine((value) => value !== null, m.required);
158
+ }
159
+ function blockFieldSchema(blockField, i18n, m, config) {
160
+ if (blockField.type === "link") return linkSchema(blockField, i18n, m, config);
161
+ if (blockField.type === "blocks") return blocksFieldSchema(blockField, i18n, m, config);
68
162
  if (isTranslatableField(blockField) && i18n.locales.length)
69
- return translatableSchema(blockField, i18n, m);
70
- return optionalize(blockField, scalarSchema(blockField, m), m);
163
+ return withValueBounds(blockField, translatableSchema(blockField, i18n, m), m);
164
+ return withValueBounds(blockField, optionalize(blockField, scalarSchema(blockField, m), m), m);
165
+ }
166
+ function blockVariantSchema(type, block, i18n, m, config) {
167
+ const variant = z.object({
168
+ type: z.literal(type),
169
+ ...declaresHiddenField(block) ? {} : { [BLOCK_HIDDEN_KEY]: z.boolean().optional() },
170
+ ...Object.fromEntries(
171
+ Object.entries(block.fields).map(([key, blockField]) => [
172
+ key,
173
+ blockFieldSchema(unconditional(blockField), i18n, m, config)
174
+ ])
175
+ )
176
+ });
177
+ const conditional = conditionalFields(block.fields);
178
+ if (!conditional.length) return variant;
179
+ return variant.superRefine((item, ctx) => {
180
+ for (const issue of conditionalIssues(conditional, item, block.fields, i18n, m))
181
+ ctx.addIssue({ code: "custom", ...issue });
182
+ });
71
183
  }
72
- function blocksSchema(field, i18n, m) {
184
+ function blocksSchema(field, i18n, m, config) {
73
185
  const variants = Object.entries(field.blocks ?? {}).map(
74
- ([type, block]) => z.object({
75
- type: z.literal(type),
76
- ...declaresHiddenField(block) ? {} : { [BLOCK_HIDDEN_KEY]: z.boolean().optional() },
77
- ...Object.fromEntries(
78
- Object.entries(block.fields).map(([key, blockField]) => [
79
- key,
80
- blockFieldSchema(blockField, i18n, m)
81
- ])
82
- )
83
- })
186
+ ([type, block]) => blockVariantSchema(type, block, i18n, m, config)
84
187
  );
85
188
  return z.array(z.discriminatedUnion("type", variants));
86
189
  }
190
+ function blocksFieldSchema(declared, i18n, m, config) {
191
+ const field = unconditional(declared);
192
+ const list = blocksSchema(field, i18n, m, config);
193
+ const base = field.required ? list.min(1, m.required) : list;
194
+ const bounded = declared.showIf ? base : boundedBlocks(base, field, m);
195
+ return field.required ? bounded : bounded.nullish().transform((v) => v ?? null);
196
+ }
87
197
  export function blocksCountMessage(field, count, messages) {
88
198
  const m = { ...DEFAULT_MESSAGES, ...messages };
89
199
  if (field.max !== void 0 && count > field.max) return m.maxBlocks(field.max);
@@ -95,8 +205,61 @@ export function tagsCountMessage(field, count, messages) {
95
205
  if (field.max !== void 0 && count > field.max) return m.maxTags(field.max);
96
206
  return null;
97
207
  }
98
- function countMessage(field, count, m) {
99
- return field.type === "tags" ? tagsCountMessage(field, count, m) : blocksCountMessage(field, count, m);
208
+ export function valueBoundsMessage(field, value, messages) {
209
+ const m = { ...DEFAULT_MESSAGES, ...messages };
210
+ if (field.type === "number" && typeof value === "number") {
211
+ if (field.min !== void 0 && value < field.min) return m.minValue(field.min);
212
+ if (field.max !== void 0 && value > field.max) return m.maxValue(field.max);
213
+ return null;
214
+ }
215
+ if (field.type !== "text") return null;
216
+ const texts = typeof value === "string" ? [value] : value && typeof value === "object" ? Object.values(value).filter((text) => typeof text === "string") : [];
217
+ for (const text of texts) {
218
+ const length = text.trim() ? text.length : 0;
219
+ if (!length) continue;
220
+ if (field.min !== void 0 && length < field.min) return m.minLength(field.min);
221
+ if (field.max !== void 0 && length > field.max) return m.maxLength(field.max);
222
+ }
223
+ return null;
224
+ }
225
+ function boundsMessage(field, value, m) {
226
+ if (field.type === "tags")
227
+ return Array.isArray(value) ? tagsCountMessage(field, value.length, m) : null;
228
+ if (field.type === "blocks")
229
+ return Array.isArray(value) ? blocksCountMessage(field, value.length, m) : null;
230
+ return valueBoundsMessage(field, value, m);
231
+ }
232
+ function hasValueBounds(field) {
233
+ return (field.type === "number" || field.type === "text") && (field.min !== void 0 || field.max !== void 0);
234
+ }
235
+ function withValueBounds(field, schema, m) {
236
+ if (field.showIf || !hasValueBounds(field)) return schema;
237
+ return schema.superRefine((value, ctx) => {
238
+ const message = valueBoundsMessage(field, value, m);
239
+ if (message) ctx.addIssue({ code: "custom", message });
240
+ });
241
+ }
242
+ function unconditional(field) {
243
+ return field.showIf ? { ...field, required: false } : field;
244
+ }
245
+ function conditionalFields(fields) {
246
+ return Object.entries(fields).filter(
247
+ ([, field]) => !!field.showIf && (!!field.required || field.min !== void 0 || field.max !== void 0)
248
+ );
249
+ }
250
+ function conditionalIssues(conditional, values, scope, i18n, m) {
251
+ const issues = [];
252
+ for (const [key, field] of conditional) {
253
+ if (!isFieldVisible(field, values, scope)) continue;
254
+ const value = values[key];
255
+ if (field.required && isEmptyValue(field, value, i18n.defaultLocale)) {
256
+ issues.push({ path: [key], message: m.required });
257
+ continue;
258
+ }
259
+ const message = boundsMessage(field, value, m);
260
+ if (message) issues.push({ path: [key], message });
261
+ }
262
+ return issues;
100
263
  }
101
264
  function tagsSchema(field, bounded, m) {
102
265
  return z.array(z.string()).nullish().transform((value) => normalizeTags(value ?? [])).superRefine((tags, ctx) => {
@@ -118,12 +281,16 @@ function hookIssues(result) {
118
281
  (issue) => !!issue && typeof issue === "object" && typeof issue.message === "string"
119
282
  );
120
283
  }
121
- function hasValidateHooks(entry) {
122
- if (entry.validate) return true;
123
- return Object.values(entry.fields).some(
124
- (field) => field.type === "blocks" && Object.values(field.blocks ?? {}).some((block) => block.validate)
284
+ function hasBlockValidateHooks(fields) {
285
+ return Object.values(fields).some(
286
+ (field) => field.type === "blocks" && Object.values(field.blocks ?? {}).some(
287
+ (block) => !!block.validate || hasBlockValidateHooks(block.fields)
288
+ )
125
289
  );
126
290
  }
291
+ function hasValidateHooks(entry) {
292
+ return !!entry.validate || hasBlockValidateHooks(entry.fields);
293
+ }
127
294
  export function entryValidationIssues(entry, values, i18n, path) {
128
295
  const context = {
129
296
  locales: i18n.locales,
@@ -140,23 +307,18 @@ export function entryValidationIssues(entry, values, i18n, path) {
140
307
  });
141
308
  }
142
309
  }
143
- for (const [key, field] of Object.entries(entry.fields)) {
144
- const list = values[key];
145
- if (field.type !== "blocks" || !Array.isArray(list)) continue;
146
- list.forEach((item, index) => {
147
- if (!item || typeof item !== "object") return;
148
- const block = field.blocks?.[String(item.type)];
149
- if (!block?.validate) return;
150
- const result = block.validate(item, { ...context, index });
151
- for (const issue of hookIssues(result)) {
152
- issues.push({
153
- path: issue.field ? [key, index, issue.field] : [key, index],
154
- message: issue.message,
155
- level: issue.level === "warning" ? "warning" : "error"
156
- });
157
- }
158
- });
159
- }
310
+ walkBlocks(entry.fields, values, ({ item, block, path: path2, index }) => {
311
+ if (!block.validate) return;
312
+ for (const issue of hookIssues(
313
+ block.validate(item, { ...context, index, blockPath: formatBlockPath(path2) })
314
+ )) {
315
+ issues.push({
316
+ path: issue.field ? [...path2, issue.field] : [...path2],
317
+ message: issue.message,
318
+ level: issue.level === "warning" ? "warning" : "error"
319
+ });
320
+ }
321
+ });
160
322
  return issues;
161
323
  }
162
324
  export function entryValidationWarnings(entry, values, i18n, path) {
@@ -178,13 +340,9 @@ function isEmptyValue(field, value, defaultLocale) {
178
340
  export function buildEntrySchema(entry, i18n, messages) {
179
341
  const m = { ...DEFAULT_MESSAGES, ...messages };
180
342
  const shape = {};
181
- const conditional = [];
182
- const conditionalBounds = [];
343
+ const conditional = conditionalFields(entry.fields);
183
344
  for (const [key, declared] of Object.entries(entry.fields)) {
184
- const field = declared.showIf ? { ...declared, required: false } : declared;
185
- if (declared.showIf && declared.required) conditional.push([key, declared]);
186
- if (declared.showIf && (declared.type === "blocks" || declared.type === "tags") && (declared.min !== void 0 || declared.max !== void 0))
187
- conditionalBounds.push([key, declared]);
345
+ const field = unconditional(declared);
188
346
  if (field.type === "relation" && field.cardinality === "many-to-many") {
189
347
  const list = z.array(z.string().min(1));
190
348
  shape[key] = field.required ? list.min(1, m.required) : list.nullish().transform((v) => v ?? []);
@@ -199,15 +357,16 @@ export function buildEntrySchema(entry, i18n, messages) {
199
357
  shape[key] = tagsSchema(field, !declared.showIf, m);
200
358
  continue;
201
359
  }
360
+ if (field.type === "link") {
361
+ shape[key] = linkSchema(field, i18n, m, entry.config);
362
+ continue;
363
+ }
202
364
  if (field.type === "blocks") {
203
- const list = blocksSchema(field, i18n, m);
204
- const base = field.required ? list.min(1, m.required) : list;
205
- const bounded = declared.showIf ? base : boundedBlocks(base, field, m);
206
- shape[key] = field.required ? bounded : bounded.nullish().transform((v) => v ?? null);
365
+ shape[key] = blocksFieldSchema(declared, i18n, m, entry.config);
207
366
  continue;
208
367
  }
209
368
  if (isTranslatableField(field) && i18n.locales.length) {
210
- shape[key] = translatableSchema(field, i18n, m);
369
+ shape[key] = withValueBounds(declared, translatableSchema(field, i18n, m), m);
211
370
  continue;
212
371
  }
213
372
  if (field.type === "json") {
@@ -215,27 +374,18 @@ export function buildEntrySchema(entry, i18n, messages) {
215
374
  shape[key] = field.required ? json.refine((v) => v !== void 0 && v !== null, m.required) : json.nullish().transform((v) => v ?? null);
216
375
  continue;
217
376
  }
218
- shape[key] = optionalize(field, scalarSchema(field, m), m);
377
+ shape[key] = withValueBounds(declared, optionalize(field, scalarSchema(field, m), m), m);
219
378
  }
220
379
  if (entry.drafts) {
221
380
  shape.status = z.enum(["draft", "published"]).nullish().transform((v) => v ?? void 0);
222
381
  }
223
382
  const schema = z.object(shape);
224
383
  const hooks = hasValidateHooks(entry);
225
- if (!conditional.length && !conditionalBounds.length && !hooks) return schema;
384
+ if (!conditional.length && !hooks) return schema;
226
385
  return schema.superRefine((parsed, ctx) => {
227
386
  const values = parsed;
228
- for (const [key, field] of conditional) {
229
- if (!isFieldVisible(field, values)) continue;
230
- if (!isEmptyValue(field, values[key], i18n.defaultLocale)) continue;
231
- ctx.addIssue({ code: "custom", path: [key], message: m.required });
232
- }
233
- for (const [key, field] of conditionalBounds) {
234
- const list = values[key];
235
- if (!isFieldVisible(field, values) || !Array.isArray(list)) continue;
236
- const message = countMessage(field, list.length, m);
237
- if (message) ctx.addIssue({ code: "custom", path: [key], message });
238
- }
387
+ for (const issue of conditionalIssues(conditional, values, entry.fields, i18n, m))
388
+ ctx.addIssue({ code: "custom", ...issue });
239
389
  if (!hooks) return;
240
390
  for (const issue of entryValidationIssues(entry, values, i18n, entry.path)) {
241
391
  if (issue.level === "error")
@@ -0,0 +1,33 @@
1
+ import type { CmsEntryAction, CmsEntryStatus, CmsMediaAction, CmsEntryChange, CmsMediaChange } from './shared/hooks.js';
2
+ import type { CmsEntryKind } from './shared/index.js';
3
+ export type { CmsEntryAction, CmsEntryChange, CmsEntryKind, CmsEntryStatus, CmsMediaAction, CmsMediaChange, };
4
+ export declare const CMS_WEBHOOK_SIGNATURE_HEADER = "x-cms-signature";
5
+ export declare const CMS_WEBHOOK_EVENT_HEADER = "x-cms-event";
6
+ export declare const CMS_WEBHOOK_DELIVERY_HEADER = "x-cms-delivery";
7
+ export declare const CMS_WEBHOOK_TOLERANCE_SECONDS = 300;
8
+ export type CmsWebhookEvent = `entry:${CmsEntryAction}` | `media:${CmsMediaAction}`;
9
+ export interface CmsWebhookEntryPayload {
10
+ event: `entry:${CmsEntryAction}`;
11
+ action: CmsEntryAction;
12
+ entry: string;
13
+ kind: CmsEntryKind;
14
+ ids: string[];
15
+ path: string | null;
16
+ status: CmsEntryStatus | null;
17
+ at: string;
18
+ }
19
+ export interface CmsWebhookMediaPayload {
20
+ event: `media:${CmsMediaAction}`;
21
+ action: CmsMediaAction;
22
+ keys: string[];
23
+ folder: string | null;
24
+ from: string | null;
25
+ at: string;
26
+ }
27
+ export type CmsWebhookPayload = CmsWebhookEntryPayload | CmsWebhookMediaPayload;
28
+ export declare function signCmsWebhook(body: string, secret: string, timestamp?: number): Promise<string>;
29
+ export declare function parseCmsWebhookSignature(header: string): {
30
+ timestamp: number;
31
+ signatures: string[];
32
+ } | null;
33
+ export declare function verifyCmsWebhook(input: Request | string, header: string | null | undefined, secret: string, toleranceSeconds?: number): Promise<CmsWebhookPayload | null>;
@@ -0,0 +1,57 @@
1
+ export const CMS_WEBHOOK_SIGNATURE_HEADER = "x-cms-signature";
2
+ export const CMS_WEBHOOK_EVENT_HEADER = "x-cms-event";
3
+ export const CMS_WEBHOOK_DELIVERY_HEADER = "x-cms-delivery";
4
+ export const CMS_WEBHOOK_TOLERANCE_SECONDS = 300;
5
+ const encoder = new TextEncoder();
6
+ function toHex(buffer) {
7
+ return Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, "0")).join("");
8
+ }
9
+ async function hmacHex(secret, message) {
10
+ const key = await globalThis.crypto.subtle.importKey(
11
+ "raw",
12
+ encoder.encode(secret),
13
+ { name: "HMAC", hash: "SHA-256" },
14
+ false,
15
+ ["sign"]
16
+ );
17
+ return toHex(await globalThis.crypto.subtle.sign("HMAC", key, encoder.encode(message)));
18
+ }
19
+ function safeEqual(a, b) {
20
+ if (a.length !== b.length) return false;
21
+ let diff = 0;
22
+ for (let index = 0; index < a.length; index++) diff |= a.charCodeAt(index) ^ b.charCodeAt(index);
23
+ return diff === 0;
24
+ }
25
+ export async function signCmsWebhook(body, secret, timestamp = Math.floor(Date.now() / 1e3)) {
26
+ return `t=${timestamp},v1=${await hmacHex(secret, `${timestamp}.${body}`)}`;
27
+ }
28
+ export function parseCmsWebhookSignature(header) {
29
+ let timestamp = null;
30
+ const signatures = [];
31
+ for (const part of header.split(",")) {
32
+ const separator = part.indexOf("=");
33
+ if (separator < 0) continue;
34
+ const key = part.slice(0, separator).trim();
35
+ const value = part.slice(separator + 1).trim();
36
+ if (key === "t" && /^\d+$/.test(value)) timestamp = Number(value);
37
+ if (key === "v1" && /^[0-9a-f]{64}$/.test(value)) signatures.push(value);
38
+ }
39
+ return timestamp === null || !signatures.length ? null : { timestamp, signatures };
40
+ }
41
+ export async function verifyCmsWebhook(input, header, secret, toleranceSeconds = CMS_WEBHOOK_TOLERANCE_SECONDS) {
42
+ if (!secret) return null;
43
+ const signatureHeader = header ?? (typeof input === "string" ? null : input.headers.get(CMS_WEBHOOK_SIGNATURE_HEADER));
44
+ if (!signatureHeader) return null;
45
+ const parsed = parseCmsWebhookSignature(signatureHeader);
46
+ if (!parsed) return null;
47
+ if (Math.abs(Math.floor(Date.now() / 1e3) - parsed.timestamp) > toleranceSeconds) return null;
48
+ const body = typeof input === "string" ? input : await input.text();
49
+ const expected = await hmacHex(secret, `${parsed.timestamp}.${body}`);
50
+ if (!parsed.signatures.some((signature) => safeEqual(signature, expected))) return null;
51
+ try {
52
+ const payload = JSON.parse(body);
53
+ return payload && typeof payload === "object" ? payload : null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
package/dist/types.d.mts CHANGED
@@ -1,3 +1,7 @@
1
+ export { type CmsRoleInput, type CmsRolesInput } from '../dist/runtime/shared/roles.js'
2
+
3
+ export { type CmsEntryAction, type CmsEntryChange, type CmsEntryStatus, type CmsHookContext, type CmsHookUser, type CmsMediaAction, type CmsMediaChange } from '../dist/runtime/shared/hooks.js'
4
+
1
5
  export { default } from './module.mjs'
2
6
 
3
- export { type ModuleOptions, type ModuleOptionsDatabase, type ModuleOptionsMedia } from './module.mjs'
7
+ export { type CmsWebhookEventFilter, type ModuleOptions, type ModuleOptionsDatabase, type ModuleOptionsMedia, type ModuleOptionsWebhook } from './module.mjs'