@velastack/cms 0.1.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.
- package/LICENSE +21 -0
- package/README.md +644 -0
- package/dist/cms.d.ts +6 -0
- package/dist/cms.js +30 -0
- package/dist/components/admin-bar/admin-bar-internal.svelte +1697 -0
- package/dist/components/admin-bar/admin-bar-internal.svelte.d.ts +12 -0
- package/dist/components/admin-bar/admin-bar.css +5 -0
- package/dist/components/admin-bar/admin-bar.svelte +355 -0
- package/dist/components/admin-bar/admin-bar.svelte.d.ts +7 -0
- package/dist/components/admin-bar/click-outside.d.ts +8 -0
- package/dist/components/admin-bar/click-outside.js +17 -0
- package/dist/components/admin-bar/css-root.svelte +23 -0
- package/dist/components/admin-bar/css-root.svelte.d.ts +19 -0
- package/dist/components/admin-bar/delete-page-dialog.svelte +326 -0
- package/dist/components/admin-bar/delete-page-dialog.svelte.d.ts +32 -0
- package/dist/components/admin-bar/history-panel.svelte +264 -0
- package/dist/components/admin-bar/history-panel.svelte.d.ts +8 -0
- package/dist/components/admin-bar/icons/bold.svelte +13 -0
- package/dist/components/admin-bar/icons/bold.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/check.svelte +11 -0
- package/dist/components/admin-bar/icons/check.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/chevron-down.svelte +11 -0
- package/dist/components/admin-bar/icons/chevron-down.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/chevron-right.svelte +11 -0
- package/dist/components/admin-bar/icons/chevron-right.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/chevron-up.svelte +11 -0
- package/dist/components/admin-bar/icons/chevron-up.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/code.svelte +14 -0
- package/dist/components/admin-bar/icons/code.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/external-link.svelte +15 -0
- package/dist/components/admin-bar/icons/external-link.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/file.svelte +19 -0
- package/dist/components/admin-bar/icons/file.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/heading-2.svelte +16 -0
- package/dist/components/admin-bar/icons/heading-2.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/heading-3.svelte +17 -0
- package/dist/components/admin-bar/icons/heading-3.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/icon.svelte +41 -0
- package/dist/components/admin-bar/icons/icon.svelte.d.ts +21 -0
- package/dist/components/admin-bar/icons/images.svelte +16 -0
- package/dist/components/admin-bar/icons/images.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/italic.svelte +15 -0
- package/dist/components/admin-bar/icons/italic.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/link-2-off.svelte +16 -0
- package/dist/components/admin-bar/icons/link-2-off.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/link.svelte +14 -0
- package/dist/components/admin-bar/icons/link.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/list-ordered.svelte +18 -0
- package/dist/components/admin-bar/icons/list-ordered.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/list.svelte +18 -0
- package/dist/components/admin-bar/icons/list.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/loader-circle.svelte +11 -0
- package/dist/components/admin-bar/icons/loader-circle.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/minus.svelte +11 -0
- package/dist/components/admin-bar/icons/minus.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/quote.svelte +24 -0
- package/dist/components/admin-bar/icons/quote.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/search.svelte +14 -0
- package/dist/components/admin-bar/icons/search.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/trash-2.svelte +17 -0
- package/dist/components/admin-bar/icons/trash-2.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/triangle-alert.svelte +15 -0
- package/dist/components/admin-bar/icons/triangle-alert.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/types.d.ts +7 -0
- package/dist/components/admin-bar/icons/types.js +1 -0
- package/dist/components/admin-bar/icons/undo-2.svelte +14 -0
- package/dist/components/admin-bar/icons/undo-2.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/upload.svelte +15 -0
- package/dist/components/admin-bar/icons/upload.svelte.d.ts +3 -0
- package/dist/components/admin-bar/icons/x.svelte +14 -0
- package/dist/components/admin-bar/icons/x.svelte.d.ts +3 -0
- package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte +96 -0
- package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte.d.ts +7 -0
- package/dist/components/admin-bar/locales-panel.svelte +137 -0
- package/dist/components/admin-bar/locales-panel.svelte.d.ts +9 -0
- package/dist/components/admin-bar/media-panel.svelte +282 -0
- package/dist/components/admin-bar/media-panel.svelte.d.ts +7 -0
- package/dist/components/admin-bar/new-page-chooser-dialog.svelte +56 -0
- package/dist/components/admin-bar/new-page-chooser-dialog.svelte.d.ts +10 -0
- package/dist/components/admin-bar/new-page-dialog.svelte +126 -0
- package/dist/components/admin-bar/new-page-dialog.svelte.d.ts +14 -0
- package/dist/components/admin-bar/page-config.d.ts +61 -0
- package/dist/components/admin-bar/page-config.js +24 -0
- package/dist/components/admin-bar/pages-panel.svelte +639 -0
- package/dist/components/admin-bar/pages-panel.svelte.d.ts +13 -0
- package/dist/components/admin-bar/panel-footer.svelte +21 -0
- package/dist/components/admin-bar/panel-footer.svelte.d.ts +8 -0
- package/dist/components/admin-bar/panel-header.svelte +29 -0
- package/dist/components/admin-bar/panel-header.svelte.d.ts +10 -0
- package/dist/components/admin-bar/panel.svelte +41 -0
- package/dist/components/admin-bar/panel.svelte.d.ts +10 -0
- package/dist/components/admin-bar/publish-dialog.svelte +387 -0
- package/dist/components/admin-bar/publish-dialog.svelte.d.ts +16 -0
- package/dist/components/admin-bar/resolve-route.d.ts +20 -0
- package/dist/components/admin-bar/resolve-route.js +50 -0
- package/dist/components/admin-bar/seo-panel.svelte +387 -0
- package/dist/components/admin-bar/seo-panel.svelte.d.ts +7 -0
- package/dist/components/admin-bar/share-preview-link-dialog.svelte +97 -0
- package/dist/components/admin-bar/share-preview-link-dialog.svelte.d.ts +9 -0
- package/dist/components/admin-bar/site-settings-panel.svelte +225 -0
- package/dist/components/admin-bar/site-settings-panel.svelte.d.ts +7 -0
- package/dist/components/admin-bar/status-pill.svelte +49 -0
- package/dist/components/admin-bar/status-pill.svelte.d.ts +11 -0
- package/dist/components/admin-bar/theme.svelte.d.ts +16 -0
- package/dist/components/admin-bar/theme.svelte.js +39 -0
- package/dist/components/admin-bar/ui/badge/badge.svelte +57 -0
- package/dist/components/admin-bar/ui/badge/badge.svelte.d.ts +16 -0
- package/dist/components/admin-bar/ui/badge/index.d.ts +4 -0
- package/dist/components/admin-bar/ui/badge/index.js +4 -0
- package/dist/components/admin-bar/ui/button/button.svelte +73 -0
- package/dist/components/admin-bar/ui/button/button.svelte.d.ts +16 -0
- package/dist/components/admin-bar/ui/button/index.d.ts +4 -0
- package/dist/components/admin-bar/ui/button/index.js +4 -0
- package/dist/components/admin-bar/ui/collapsible/collapsible-content.svelte +7 -0
- package/dist/components/admin-bar/ui/collapsible/collapsible-content.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/collapsible/collapsible-trigger.svelte +7 -0
- package/dist/components/admin-bar/ui/collapsible/collapsible-trigger.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/collapsible/collapsible.svelte +7 -0
- package/dist/components/admin-bar/ui/collapsible/collapsible.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/collapsible/index.d.ts +4 -0
- package/dist/components/admin-bar/ui/collapsible/index.js +6 -0
- package/dist/components/admin-bar/ui/dialog/dialog-close.svelte +7 -0
- package/dist/components/admin-bar/ui/dialog/dialog-close.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/dialog/dialog-content.svelte +46 -0
- package/dist/components/admin-bar/ui/dialog/dialog-content.svelte.d.ts +11 -0
- package/dist/components/admin-bar/ui/dialog/dialog-description.svelte +17 -0
- package/dist/components/admin-bar/ui/dialog/dialog-description.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/dialog/dialog-footer.svelte +23 -0
- package/dist/components/admin-bar/ui/dialog/dialog-footer.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/dialog/dialog-header.svelte +20 -0
- package/dist/components/admin-bar/ui/dialog/dialog-header.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/dialog/dialog-overlay.svelte +20 -0
- package/dist/components/admin-bar/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/dialog/dialog-portal.svelte +7 -0
- package/dist/components/admin-bar/ui/dialog/dialog-portal.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/dialog/dialog-title.svelte +17 -0
- package/dist/components/admin-bar/ui/dialog/dialog-title.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/dialog/dialog-trigger.svelte +7 -0
- package/dist/components/admin-bar/ui/dialog/dialog-trigger.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/dialog/dialog.svelte +7 -0
- package/dist/components/admin-bar/ui/dialog/dialog.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/dialog/index.d.ts +11 -0
- package/dist/components/admin-bar/ui/dialog/index.js +13 -0
- package/dist/components/admin-bar/ui/input/index.d.ts +3 -0
- package/dist/components/admin-bar/ui/input/index.js +3 -0
- package/dist/components/admin-bar/ui/input/input.svelte +24 -0
- package/dist/components/admin-bar/ui/input/input.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/kbd/index.d.ts +4 -0
- package/dist/components/admin-bar/ui/kbd/index.js +6 -0
- package/dist/components/admin-bar/ui/kbd/kbd-group.svelte +20 -0
- package/dist/components/admin-bar/ui/kbd/kbd-group.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/kbd/kbd-shortcut.svelte +52 -0
- package/dist/components/admin-bar/ui/kbd/kbd-shortcut.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/kbd/kbd.svelte +27 -0
- package/dist/components/admin-bar/ui/kbd/kbd.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/label/index.d.ts +3 -0
- package/dist/components/admin-bar/ui/label/index.js +3 -0
- package/dist/components/admin-bar/ui/label/label.svelte +20 -0
- package/dist/components/admin-bar/ui/label/label.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/index.d.ts +18 -0
- package/dist/components/admin-bar/ui/menubar/index.js +20 -0
- package/dist/components/admin-bar/ui/menubar/menubar-checkbox-item.svelte +46 -0
- package/dist/components/admin-bar/ui/menubar/menubar-checkbox-item.svelte.d.ts +10 -0
- package/dist/components/admin-bar/ui/menubar/menubar-content.svelte +28 -0
- package/dist/components/admin-bar/ui/menubar/menubar-content.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/menubar-group-heading.svelte +25 -0
- package/dist/components/admin-bar/ui/menubar/menubar-group-heading.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/menubar/menubar-group.svelte +12 -0
- package/dist/components/admin-bar/ui/menubar/menubar-group.svelte.d.ts +7 -0
- package/dist/components/admin-bar/ui/menubar/menubar-item.svelte +27 -0
- package/dist/components/admin-bar/ui/menubar/menubar-item.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/menubar/menubar-label.svelte +25 -0
- package/dist/components/admin-bar/ui/menubar/menubar-label.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/menubar/menubar-menu.svelte +7 -0
- package/dist/components/admin-bar/ui/menubar/menubar-menu.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/menubar/menubar-portal.svelte +7 -0
- package/dist/components/admin-bar/ui/menubar/menubar-portal.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/menubar/menubar-radio-group.svelte +11 -0
- package/dist/components/admin-bar/ui/menubar/menubar-radio-group.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/menubar-radio-item.svelte +37 -0
- package/dist/components/admin-bar/ui/menubar/menubar-radio-item.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/menubar/menubar-separator.svelte +17 -0
- package/dist/components/admin-bar/ui/menubar/menubar-separator.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/menubar-shortcut.svelte +23 -0
- package/dist/components/admin-bar/ui/menubar/menubar-shortcut.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/menubar/menubar-sub-content.svelte +20 -0
- package/dist/components/admin-bar/ui/menubar/menubar-sub-content.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/menubar-sub-trigger.svelte +29 -0
- package/dist/components/admin-bar/ui/menubar/menubar-sub-trigger.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/menubar/menubar-sub.svelte +7 -0
- package/dist/components/admin-bar/ui/menubar/menubar-sub.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/menubar-trigger.svelte +20 -0
- package/dist/components/admin-bar/ui/menubar/menubar-trigger.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/menubar/menubar.svelte +20 -0
- package/dist/components/admin-bar/ui/menubar/menubar.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/select/index.d.ts +12 -0
- package/dist/components/admin-bar/ui/select/index.js +14 -0
- package/dist/components/admin-bar/ui/select/select-content.svelte +45 -0
- package/dist/components/admin-bar/ui/select/select-content.svelte.d.ts +11 -0
- package/dist/components/admin-bar/ui/select/select-group-heading.svelte +21 -0
- package/dist/components/admin-bar/ui/select/select-group-heading.svelte.d.ts +10 -0
- package/dist/components/admin-bar/ui/select/select-group.svelte +17 -0
- package/dist/components/admin-bar/ui/select/select-group.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/select/select-item.svelte +40 -0
- package/dist/components/admin-bar/ui/select/select-item.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/select/select-label.svelte +20 -0
- package/dist/components/admin-bar/ui/select/select-label.svelte.d.ts +6 -0
- package/dist/components/admin-bar/ui/select/select-portal.svelte +7 -0
- package/dist/components/admin-bar/ui/select/select-portal.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/select/select-scroll-down-button.svelte +23 -0
- package/dist/components/admin-bar/ui/select/select-scroll-down-button.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/select/select-scroll-up-button.svelte +23 -0
- package/dist/components/admin-bar/ui/select/select-scroll-up-button.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/select/select-separator.svelte +21 -0
- package/dist/components/admin-bar/ui/select/select-separator.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/select/select-trigger.svelte +31 -0
- package/dist/components/admin-bar/ui/select/select-trigger.svelte.d.ts +8 -0
- package/dist/components/admin-bar/ui/select/select.svelte +11 -0
- package/dist/components/admin-bar/ui/select/select.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/separator/index.d.ts +2 -0
- package/dist/components/admin-bar/ui/separator/index.js +4 -0
- package/dist/components/admin-bar/ui/separator/separator.svelte +21 -0
- package/dist/components/admin-bar/ui/separator/separator.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/textarea/index.d.ts +3 -0
- package/dist/components/admin-bar/ui/textarea/index.js +3 -0
- package/dist/components/admin-bar/ui/textarea/textarea.svelte +21 -0
- package/dist/components/admin-bar/ui/textarea/textarea.svelte.d.ts +5 -0
- package/dist/components/admin-bar/user-avatar.svelte +29 -0
- package/dist/components/admin-bar/user-avatar.svelte.d.ts +7 -0
- package/dist/components/admin-bar/utils.d.ts +10 -0
- package/dist/components/admin-bar/utils.js +46 -0
- package/dist/components/admin-bar/variants.d.ts +20 -0
- package/dist/components/admin-bar/variants.js +14 -0
- package/dist/components/cms/cms-boolean.svelte +102 -0
- package/dist/components/cms/cms-boolean.svelte.d.ts +10 -0
- package/dist/components/cms/cms-date-time-editable.svelte +66 -0
- package/dist/components/cms/cms-date-time-editable.svelte.d.ts +11 -0
- package/dist/components/cms/cms-date-time.svelte +67 -0
- package/dist/components/cms/cms-date-time.svelte.d.ts +19 -0
- package/dist/components/cms/cms-entries.svelte +24 -0
- package/dist/components/cms/cms-entries.svelte.d.ts +31 -0
- package/dist/components/cms/cms-image-editable.svelte +439 -0
- package/dist/components/cms/cms-image-editable.svelte.d.ts +11 -0
- package/dist/components/cms/cms-image.svelte +78 -0
- package/dist/components/cms/cms-image.svelte.d.ts +18 -0
- package/dist/components/cms/cms-link-editable.svelte +429 -0
- package/dist/components/cms/cms-link-editable.svelte.d.ts +13 -0
- package/dist/components/cms/cms-link.svelte +92 -0
- package/dist/components/cms/cms-link.svelte.d.ts +25 -0
- package/dist/components/cms/cms-markdown-editable.svelte +74 -0
- package/dist/components/cms/cms-markdown-editable.svelte.d.ts +9 -0
- package/dist/components/cms/cms-markdown.svelte +56 -0
- package/dist/components/cms/cms-markdown.svelte.d.ts +10 -0
- package/dist/components/cms/cms-media-picker.svelte +150 -0
- package/dist/components/cms/cms-media-picker.svelte.d.ts +8 -0
- package/dist/components/cms/cms-number-editable.svelte +77 -0
- package/dist/components/cms/cms-number-editable.svelte.d.ts +13 -0
- package/dist/components/cms/cms-number.svelte +63 -0
- package/dist/components/cms/cms-number.svelte.d.ts +16 -0
- package/dist/components/cms/cms-repeater-editable.svelte +137 -0
- package/dist/components/cms/cms-repeater-editable.svelte.d.ts +31 -0
- package/dist/components/cms/cms-repeater.svelte +55 -0
- package/dist/components/cms/cms-repeater.svelte.d.ts +30 -0
- package/dist/components/cms/cms-rich-text-editable.svelte +101 -0
- package/dist/components/cms/cms-rich-text-editable.svelte.d.ts +9 -0
- package/dist/components/cms/cms-rich-text-toolbar.svelte +246 -0
- package/dist/components/cms/cms-rich-text-toolbar.svelte.d.ts +9 -0
- package/dist/components/cms/cms-rich-text.svelte +74 -0
- package/dist/components/cms/cms-rich-text.svelte.d.ts +10 -0
- package/dist/components/cms/cms-store.svelte.d.ts +294 -0
- package/dist/components/cms/cms-store.svelte.js +625 -0
- package/dist/components/cms/cms-text.svelte +131 -0
- package/dist/components/cms/cms-text.svelte.d.ts +11 -0
- package/dist/components/cms/index.d.ts +13 -0
- package/dist/components/cms/index.js +11 -0
- package/dist/components/cms/install-scope.svelte.d.ts +7 -0
- package/dist/components/cms/install-scope.svelte.js +33 -0
- package/dist/components/cms/locale-merge.d.ts +41 -0
- package/dist/components/cms/locale-merge.js +77 -0
- package/dist/components/cms/overlay-sync.d.ts +82 -0
- package/dist/components/cms/overlay-sync.js +76 -0
- package/dist/components/cms/path.d.ts +30 -0
- package/dist/components/cms/path.js +156 -0
- package/dist/components/cms/scope.d.ts +120 -0
- package/dist/components/cms/scope.js +23 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +28 -0
- package/dist/srv/README.md +341 -0
- package/dist/srv/api-adapter.d.ts +20 -0
- package/dist/srv/api-adapter.js +178 -0
- package/dist/srv/cms.d.ts +62 -0
- package/dist/srv/cms.js +35 -0
- package/dist/srv/index.d.ts +5 -0
- package/dist/srv/index.js +4 -0
- package/dist/srv/load-cms.d.ts +105 -0
- package/dist/srv/load-cms.js +227 -0
- package/dist/srv/mock-adapter.d.ts +96 -0
- package/dist/srv/mock-adapter.js +171 -0
- package/dist/srv/types.d.ts +114 -0
- package/dist/srv/types.js +1 -0
- package/package.json +116 -0
- package/plugin/README.md +137 -0
- package/plugin/dist/adapter.d.ts +35 -0
- package/plugin/dist/adapter.js +346 -0
- package/plugin/dist/adapter.js.map +1 -0
- package/plugin/dist/build-state.d.ts +10 -0
- package/plugin/dist/build-state.js +26 -0
- package/plugin/dist/build-state.js.map +1 -0
- package/plugin/dist/fallback-shim.js +194 -0
- package/plugin/dist/index.d.ts +56 -0
- package/plugin/dist/index.js +396 -0
- package/plugin/dist/index.js.map +1 -0
- package/plugin/dist/manifest.d.ts +76 -0
- package/plugin/dist/manifest.js +367 -0
- package/plugin/dist/manifest.js.map +1 -0
- package/plugin/dist/media.d.ts +36 -0
- package/plugin/dist/media.js +153 -0
- package/plugin/dist/media.js.map +1 -0
- package/plugin/dist/parse-svelte.d.ts +35 -0
- package/plugin/dist/parse-svelte.js +120 -0
- package/plugin/dist/parse-svelte.js.map +1 -0
- package/plugin/dist/route-tree.d.ts +27 -0
- package/plugin/dist/route-tree.js +69 -0
- package/plugin/dist/route-tree.js.map +1 -0
- package/plugin/dist/transforms.d.ts +42 -0
- package/plugin/dist/transforms.js +95 -0
- package/plugin/dist/transforms.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nathan Cahill
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
# VelaStack CMS
|
|
2
|
+
|
|
3
|
+
A scope-aware CMS for SvelteKit, for SSG and fullstack websites.
|
|
4
|
+
|
|
5
|
+
```svelte
|
|
6
|
+
<!-- src/routes/(marketing)/about/+page.svelte -->
|
|
7
|
+
<script>
|
|
8
|
+
import { CmsText, CmsRichText } from '@velastack/cms';
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<h1>
|
|
12
|
+
<CmsText name="hero.title" fallback="About us" />
|
|
13
|
+
</h1>
|
|
14
|
+
|
|
15
|
+
<CmsRichText name="body" />
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That's the whole authoring API. No field paths, no manual wiring. The build-time Vite plugin discovers every `<CmsText/>` (and friends) reachable from each route, computes which route scope they belong to, and emits a manifest. At request time, `loadCms(event, …)` resolves the right documents for the current route. At edit time, the admin bar swaps display components for inline editors — without shipping a single byte of editing code to public visitors.
|
|
19
|
+
|
|
20
|
+
## Why scope-aware?
|
|
21
|
+
|
|
22
|
+
A reusable component like `Header.svelte` may be mounted from `(marketing)/+layout.svelte` and `(app)/+layout.svelte`. Its `<CmsText name="header.title" />` is **the same field name** in both places, but those should be **different stored values** — one for the marketing site, one for the app shell.
|
|
23
|
+
|
|
24
|
+
CMS identity therefore can't be `(component file + field name)`. It has to be `(usage scope + field name)`. The plugin walks each `+layout.svelte` / `+page.svelte`'s static import graph, attributes every CMS field reference to the route entrypoint that reached it, and emits:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
{
|
|
28
|
+
'/(marketing)/about': {
|
|
29
|
+
scopes: [
|
|
30
|
+
{ scopeId: 'layout:/', fields: ['footer.links'] },
|
|
31
|
+
{ scopeId: 'layout:/(marketing)', fields: ['header.title', 'announcement.text'] },
|
|
32
|
+
{ scopeId: 'page:/(marketing)/about', fields: ['hero.title', 'body'], metadata: ['title', 'description', 'canonical', 'robots'] }
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
…
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm install @velastack/cms
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
### 1. Register the Vite plugin
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// vite.config.ts
|
|
51
|
+
import { defineConfig } from 'vite';
|
|
52
|
+
import { sveltekit } from '@sveltejs/kit/vite';
|
|
53
|
+
import { cms } from '@velastack/cms/vite';
|
|
54
|
+
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
plugins: [cms(), sveltekit()]
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The plugin reads `src/routes/` and `src/lib/` by default. Override with `cms({ routesDir, libDir })` if your project is laid out differently.
|
|
61
|
+
|
|
62
|
+
### 2. Create the CMS in `$lib/cms.ts`
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// src/lib/cms.ts
|
|
66
|
+
import { createCms, mockAdapter } from '@velastack/cms/server';
|
|
67
|
+
|
|
68
|
+
export const { load: loadCms, generateEntries } = createCms({
|
|
69
|
+
adapter: mockAdapter(),
|
|
70
|
+
locales: ['en'] // or ['en', 'es', 'fr'] — first entry is the default locale
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Swap `mockAdapter` for your real adapter once you have a backend (see [Adapters](#adapters)).
|
|
75
|
+
The `locales` array names every BCP-47 locale your site supports; the first entry is the
|
|
76
|
+
**default locale** used for read-time fallback when a value is missing in the requested
|
|
77
|
+
locale (see [Locales](#locales)).
|
|
78
|
+
|
|
79
|
+
### 3. Wire `loadCms` into the root `+layout.server.ts`
|
|
80
|
+
|
|
81
|
+
Optionally include `svelte-meta-tags` for easy page metadata handling, but it isn't a requirement.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// src/routes/+layout.server.ts
|
|
85
|
+
import { error, redirect, type ServerLoad } from '@sveltejs/kit';
|
|
86
|
+
import { defineBaseMetaTags } from 'svelte-meta-tags';
|
|
87
|
+
import { loadCms } from '$lib/cms.js';
|
|
88
|
+
|
|
89
|
+
export const load: ServerLoad = async (event) => {
|
|
90
|
+
const { baseMetaTags } = defineBaseMetaTags({
|
|
91
|
+
title: 'My site',
|
|
92
|
+
titleTemplate: '%s · My site'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Pick the locale however you like — pathname segment (`/es/about`),
|
|
96
|
+
// `Accept-Language`, a session cookie, etc. Pass whatever string you
|
|
97
|
+
// land on; the loader doesn't care how it was derived.
|
|
98
|
+
const locale = event.url.searchParams.get('locale') ?? 'en';
|
|
99
|
+
|
|
100
|
+
const { cms, notFound, gone, redirectTo } = await loadCms(event, { locale });
|
|
101
|
+
if (redirectTo) redirect(308, redirectTo);
|
|
102
|
+
if (gone) error(410, 'Gone');
|
|
103
|
+
if (notFound) error(404, 'Not found');
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
baseMetaTags,
|
|
107
|
+
cms
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`loadCms` returns four mutually-exclusive page-kind resolutions, in priority order:
|
|
113
|
+
|
|
114
|
+
| Field | Meaning | Caller does |
|
|
115
|
+
| ------------ | ---------------------------------------------------------------------------------- | ------------------- |
|
|
116
|
+
| `redirectTo` | Page was replaced with a permanent redirect to this URL. | `redirect(308, …)` |
|
|
117
|
+
| `gone` | Page was deliberately, permanently removed. | `error(410, …)` |
|
|
118
|
+
| `notFound` | Page-kind scope had owned params and the adapter returned no doc and no tombstone. | `error(404, …)` |
|
|
119
|
+
| (none set) | Render normally with `data.cms`. | return `{ cms, … }` |
|
|
120
|
+
|
|
121
|
+
Layout scopes never tombstone — only page-kind scopes can resolve to `gone` / `redirectTo`.
|
|
122
|
+
|
|
123
|
+
### 4. Render `<AdminBar/>` in the root layout
|
|
124
|
+
|
|
125
|
+
Along with optional `<MetaTags />` handling.
|
|
126
|
+
|
|
127
|
+
```svelte
|
|
128
|
+
<!-- src/routes/+layout.svelte -->
|
|
129
|
+
<script lang="ts">
|
|
130
|
+
import { MetaTags, deepMerge } from 'svelte-meta-tags';
|
|
131
|
+
import { AdminBar, cms } from '@velastack/cms';
|
|
132
|
+
|
|
133
|
+
let { data, children } = $props();
|
|
134
|
+
let metaTags = $derived(deepMerge(data.baseMetaTags, cms.metadata));
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<AdminBar />
|
|
138
|
+
<MetaTags {...metaTags} />
|
|
139
|
+
|
|
140
|
+
{@render children()}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`<AdminBar/>` is a tiny sync wrapper. Its UI and every editable component are dynamically imported, so public visitors never download editing code.
|
|
144
|
+
|
|
145
|
+
### 5. Author your routes
|
|
146
|
+
|
|
147
|
+
```svelte
|
|
148
|
+
<!-- src/routes/(marketing)/about/+page.svelte -->
|
|
149
|
+
<script lang="ts">
|
|
150
|
+
import { CmsText, CmsRichText } from '@velastack/cms';
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<h1>
|
|
154
|
+
<CmsText name="hero.title" fallback="About us" />
|
|
155
|
+
</h1>
|
|
156
|
+
|
|
157
|
+
<CmsRichText name="body" />
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
That's it — the plugin handles scope and the loader handles data fetching.
|
|
161
|
+
|
|
162
|
+
## Production builds: prerender + media download
|
|
163
|
+
|
|
164
|
+
`loadCms` is **server-only** — call it from `+layout.server.ts` (or `+page.server.ts`), never from a `+page.svelte` or universal `+page.ts`. Calling it in the browser throws. Editing/preview happens through `cmsStore` from `@velastack/cms`, which is gated by your `cms_session` cookie and opaque preview/version keys.
|
|
165
|
+
|
|
166
|
+
The recommended production shape is **prerender by default**:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
// src/routes/+layout.server.ts
|
|
170
|
+
export const prerender = true;
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
When the customer app is also built against a real backend, configure the Vite plugin with the same endpoint your `apiAdapter` uses:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
// vite.config.ts
|
|
177
|
+
import { cms } from '@velastack/cms/vite';
|
|
178
|
+
|
|
179
|
+
export default defineConfig({
|
|
180
|
+
plugins: [
|
|
181
|
+
cms({
|
|
182
|
+
endpoint: 'https://cms.example.com/v1/projects/p1/cms',
|
|
183
|
+
locales: ['en', 'es', 'fr']
|
|
184
|
+
}),
|
|
185
|
+
sveltekit()
|
|
186
|
+
]
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
With `endpoint` set, `vite build`:
|
|
191
|
+
|
|
192
|
+
1. Walks every locale's published content over HTTP and collects every `/uploads/<file>` URL it sees in `cms.docs` trees and entry metadata.
|
|
193
|
+
2. Downloads each unique file into `static/cms-media/<file>` (override with `mediaDir`). Files already on disk are skipped — second builds are fast.
|
|
194
|
+
3. While SvelteKit prerenders, the `apiAdapter` rewrites those same URLs to `/cms-media/<file>` (override with `mediaPrefix`) inside the responses it hands back. Prerendered HTML/JSON references local paths only — the CMS backend stays off the production hot path.
|
|
195
|
+
|
|
196
|
+
Only URLs that flow through `apiAdapter` responses are rewritten. If you build a media URL by hand (e.g. `<img src={`${cms.endpoint}/uploads/${slug}.png`}>`), it stays pointing at the backend — read it through `<CmsImage/>` or `cms.docs[scope][field].url` to get the local path.
|
|
197
|
+
|
|
198
|
+
**`@sveltejs/enhanced-img` interop:** since media lands in `static/`, you can reference specific images by static path (e.g. `<enhanced:img src="/cms-media/hero.webp" />`) and get all the usual `<picture>`/`srcset` benefits. Fully dynamic enhanced-img on CMS-driven images isn't built in.
|
|
199
|
+
|
|
200
|
+
If `endpoint` is omitted (e.g. when using `mockAdapter` for tests/demos), the plugin's media steps are no-ops — builds proceed exactly as before.
|
|
201
|
+
|
|
202
|
+
## CMS components
|
|
203
|
+
|
|
204
|
+
All four included CMS components share the same prop shape:
|
|
205
|
+
|
|
206
|
+
| Prop | Type | Notes |
|
|
207
|
+
| ---------- | ------------------ | ------------------------------------------------------------------------ |
|
|
208
|
+
| `name` | `string` | Field path inside this scope. Must be a static string at v1. |
|
|
209
|
+
| `fallback` | varies | Rendered when no value is stored. `string` for text, HTML for rich text. |
|
|
210
|
+
| `value` | `unknown` (opt-in) | Per-item override used inside `<CmsRepeater/>`; bypasses scope lookup. |
|
|
211
|
+
|
|
212
|
+
### `<CmsText />`
|
|
213
|
+
|
|
214
|
+
```svelte
|
|
215
|
+
<CmsText name="hero.title" fallback="About us" />
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Plain text. In edit mode, swaps for an inline `<input>` bound to the draft store.
|
|
219
|
+
|
|
220
|
+
### `<CmsRichText />`
|
|
221
|
+
|
|
222
|
+
```svelte
|
|
223
|
+
<CmsRichText name="body" fallback="<p>Coming soon.</p>" />
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Rendered with `{@html}`. Edit mode swaps for a `<textarea>`.
|
|
227
|
+
|
|
228
|
+
### `<CmsImage />`
|
|
229
|
+
|
|
230
|
+
```svelte
|
|
231
|
+
<CmsImage name="hero.image" alt="Hero" />
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Renders an `<img>`. Edit mode shows the current image plus a URL input.
|
|
235
|
+
|
|
236
|
+
### `<CmsRepeater />`
|
|
237
|
+
|
|
238
|
+
```svelte
|
|
239
|
+
<CmsRepeater name="gallery.items">
|
|
240
|
+
{#snippet children(item)}
|
|
241
|
+
<figure>
|
|
242
|
+
<CmsImage name="src" value={item.src} alt={item.caption} />
|
|
243
|
+
<figcaption>
|
|
244
|
+
<CmsText name="caption" value={item.caption} />
|
|
245
|
+
</figcaption>
|
|
246
|
+
</figure>
|
|
247
|
+
{/snippet}
|
|
248
|
+
</CmsRepeater>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Iterates an array stored at `name`. Inside the snippet, pass per-item values via `value=` to bypass scope lookup. Edit mode renders the same snippet for each item plus add/remove controls and per-key inputs.
|
|
252
|
+
|
|
253
|
+
### `<CmsEntries />`
|
|
254
|
+
|
|
255
|
+
```svelte
|
|
256
|
+
<script lang="ts">
|
|
257
|
+
import { CmsEntries } from '@velastack/cms';
|
|
258
|
+
</script>
|
|
259
|
+
|
|
260
|
+
<CmsEntries routeId="/(marketing)/rooms/[slug]">
|
|
261
|
+
{#snippet children(entry)}
|
|
262
|
+
<li>
|
|
263
|
+
<a href={resolve('/(marketing)/rooms/[slug]', entry.params)}>
|
|
264
|
+
{entry.metadata.title}
|
|
265
|
+
</a>
|
|
266
|
+
</li>
|
|
267
|
+
{/snippet}
|
|
268
|
+
</CmsEntries>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Iterates over a set of pages by route ID. Useful for index pages.
|
|
272
|
+
|
|
273
|
+
## Custom CMS components
|
|
274
|
+
|
|
275
|
+
VelaStack CMS discovers your custom or third-party CMS components by convention.
|
|
276
|
+
|
|
277
|
+
### Auto-discovery (zero config)
|
|
278
|
+
|
|
279
|
+
A component is treated as a CMS component if **any** of the following hold:
|
|
280
|
+
|
|
281
|
+
1. **It lives in your app's `src/lib/components/cms/`.** Drop a new `.svelte` file there and it's registered automatically:
|
|
282
|
+
|
|
283
|
+
```svelte
|
|
284
|
+
<!-- src/lib/components/cms/cms-link.svelte -->
|
|
285
|
+
<script lang="ts">
|
|
286
|
+
import { CmsText } from '@velastack/cms';
|
|
287
|
+
let { name, fallback, value } = $props();
|
|
288
|
+
</script>
|
|
289
|
+
|
|
290
|
+
<a href={typeof value === 'string' ? value : undefined}>
|
|
291
|
+
<CmsText name={`${name}.label`} {fallback} />
|
|
292
|
+
</a>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Use it from any route: `<CmsLink name="hero.cta" />`. The plugin picks up `hero.cta` as a field on the page's scope; no plugin config edit required.
|
|
296
|
+
|
|
297
|
+
2. **It's a named export from your local `src/lib/components/cms/index.{ts,js}` barrel.** Re-export your component there if you prefer a single import path..
|
|
298
|
+
|
|
299
|
+
3. **It's imported from the `@velastack/cms` package itself.** The four built-ins (`CmsText`, `CmsRichText`, `CmsImage`, `CmsRepeater`) work this way: any import resolving inside the installed `@velastack/cms` package is auto-classified.
|
|
300
|
+
|
|
301
|
+
### Third-party packs (opt-in)
|
|
302
|
+
|
|
303
|
+
For CMS components published in npm packages outside @velastack/cms, declare them with the plugin's `components` option:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
// vite.config.ts
|
|
307
|
+
import { cms } from '@velastack/cms/vite';
|
|
308
|
+
|
|
309
|
+
cms({
|
|
310
|
+
components: [
|
|
311
|
+
// Named exports — `import { Hero, Quote } from 'my-cms-pack'`
|
|
312
|
+
{ source: 'my-cms-pack', names: ['Hero', 'Quote'] },
|
|
313
|
+
// Single-file default export — `import Accordion from 'my-cms-pack/accordion.svelte'`
|
|
314
|
+
{ source: 'my-cms-pack/accordion.svelte', default: true }
|
|
315
|
+
]
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Listed sources are auto-registered for traversal; the plugin walks `.svelte` files inside those packages to discover any further CMS usages they contain.
|
|
320
|
+
|
|
321
|
+
If you want the walker to descend into a package whose `.svelte` files **contain** CMS usages but aren't themselves CMS components (a wrapper / design-system scenario), add it to `traverse`:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
cms({
|
|
325
|
+
traverse: ['my-design-system', /^@my-org\//]
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
String patterns match `source === pattern || source.startsWith(pattern + '/')`. RegExp is the escape hatch — use it sparingly; matching too broadly will pull arbitrary `node_modules` `.svelte` files into the walk.
|
|
330
|
+
|
|
331
|
+
### Component contract
|
|
332
|
+
|
|
333
|
+
A CMS component should:
|
|
334
|
+
|
|
335
|
+
- Accept `{ name: string; fallback?: string; value?: unknown }` props (and any extras you need).
|
|
336
|
+
- Call `getCmsScope()` from `cms` to find its scope.
|
|
337
|
+
- Read drafts via `cmsStore.hasDraft(scopeKey, name)` / `cmsStore.getValue(scopeKey, name)` when `cmsStore.isEditing` is true; fall back to `page.data.cms.docs[scopeKey][name]` otherwise.
|
|
338
|
+
- Skip scope lookup when `value !== undefined` — that prop is the per-item override used inside `<CmsRepeater/>`.
|
|
339
|
+
- Optionally provide an editable sibling that's dynamically `import()`'d when `cmsStore.isEditing` flips on, so editing code doesn't ship to public visitors.
|
|
340
|
+
|
|
341
|
+
The four built-ins are reference implementations; copy `src/lib/components/cms/cms-text.svelte` (or one of the other built-ins with a paired `*-editable.svelte` sibling) as a starting point.
|
|
342
|
+
|
|
343
|
+
## Architecture
|
|
344
|
+
|
|
345
|
+
### Scopes
|
|
346
|
+
|
|
347
|
+
A scope is `{ kind, routeId, ownedParams }`. There are two kinds:
|
|
348
|
+
|
|
349
|
+
- **`layout:/(marketing)`** — content the marketing layout uses. Shared across every page rendered through `(marketing)`.
|
|
350
|
+
- **`page:/(marketing)/rooms/[slug]`** — page-specific content. With `ownedParams: ['slug']`, the runtime composes scope keys per slug: `page:/(marketing)/rooms/[slug]?slug=suite-1`.
|
|
351
|
+
|
|
352
|
+
A request through `/(marketing)/rooms/suite-1` produces three scope queries (root layout, marketing layout, page) and the adapter resolves all three. The runtime returns one merged `CmsPayload`:
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
{
|
|
356
|
+
locale: 'en', // resolved locale for this request
|
|
357
|
+
locales: ['en', 'es', 'fr'], // supported set, default = first entry
|
|
358
|
+
docs: {
|
|
359
|
+
'layout:/(marketing)': { 'header.title': '…' },
|
|
360
|
+
'page:/(marketing)/rooms/[slug]?slug=suite-1': { 'hero.title': '…' }
|
|
361
|
+
},
|
|
362
|
+
scopes: { /* one entry per scopeKey, with kind/routeId/params/fields/metadata */ },
|
|
363
|
+
metadata: { title: '…', description: '…' } // page-scoped only
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Components read from this via `getContext(CMS_SCOPE)` + `page.data.cms`.
|
|
368
|
+
|
|
369
|
+
### Auto-injected scope context
|
|
370
|
+
|
|
371
|
+
The Vite plugin transforms every `+layout.svelte` and `+page.svelte` by inserting:
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import { installCmsScope } from '@velastack/cms';
|
|
375
|
+
|
|
376
|
+
installCmsScope({
|
|
377
|
+
scopeId: 'page:/(marketing)/rooms/[slug]',
|
|
378
|
+
kind: 'page',
|
|
379
|
+
routeId: '/(marketing)/rooms/[slug]',
|
|
380
|
+
ownedParams: ['slug']
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`installCmsScope` synchronously seeds the scope from `page.params` (so SSR is correct) and keeps `scopeKey` in sync via `$effect` (so client-side navigations between sibling param values update reactively).
|
|
385
|
+
|
|
386
|
+
### Edit mode
|
|
387
|
+
|
|
388
|
+
`cmsStore` is a runes-backed singleton:
|
|
389
|
+
|
|
390
|
+
- `isEditing: boolean`
|
|
391
|
+
- `drafts: Record<scopeKey, Record<fieldName, value>>`
|
|
392
|
+
- `metadataDrafts: Record<scopeKey, Record<metaKey, value>>`
|
|
393
|
+
- `setValue` / `getValue` / `hasDraft`
|
|
394
|
+
- `setMetadataValue` / `getMetadataValue` / `hasMetadataDraft`
|
|
395
|
+
- `save()` — currently logs to the console (replace with a write-back call once you wire your adapter)
|
|
396
|
+
- `clearDrafts()`
|
|
397
|
+
|
|
398
|
+
Display components prefer drafts over published values when `isEditing` is true. Editable variants (`*-editable.svelte`) and the SEO panel are dynamic-import-only chunks; nothing edit-related ships in the public bundle.
|
|
399
|
+
|
|
400
|
+
### Page metadata
|
|
401
|
+
|
|
402
|
+
Page scopes carry an editable metadata field list (default: `title`, `description`, `canonical`, `robots`). Resolution order, per design:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
editable page metadata (cms.metadata)
|
|
406
|
+
→ static route/template defaults (+page.ts pageMetaTags)
|
|
407
|
+
→ static site defaults (defineBaseMetaTags)
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Hand `cms.metadata` directly to `definePageMetaTags(...)` from `svelte-meta-tags` and you're done.
|
|
411
|
+
|
|
412
|
+
## Adapters
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
export interface CmsAdapter {
|
|
416
|
+
readonly endpoint?: string;
|
|
417
|
+
|
|
418
|
+
fetchDocs(
|
|
419
|
+
queries: CmsScopeQuery[],
|
|
420
|
+
context: CmsAdapterContext
|
|
421
|
+
): Promise<Record<string, CmsAdapterResolution>> | Record<string, CmsAdapterResolution>;
|
|
422
|
+
|
|
423
|
+
fetchEntries(routeId: string, context: CmsAdapterContext): Promise<CmsEntry[]> | CmsEntry[];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
type CmsAdapterContext = {
|
|
427
|
+
fetch: typeof fetch; // SvelteKit's request-scoped fetch
|
|
428
|
+
previewKey?: string | null; // ?preview= — overlay an open release's pending edits
|
|
429
|
+
versionKey?: string | null; // ?version= — load a past published release snapshot
|
|
430
|
+
locale: string; // BCP-47 bound at loadCms({ locale }) time
|
|
431
|
+
locales: string[]; // supported set; first entry is the default locale
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
type CmsScopeQuery = {
|
|
435
|
+
scopeId: string;
|
|
436
|
+
kind: 'layout' | 'page';
|
|
437
|
+
routeId: string;
|
|
438
|
+
params: Record<string, string>; // owned params for this scope only
|
|
439
|
+
fields: string[];
|
|
440
|
+
locale: string; // request locale, repeated per query for adapter convenience
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
type CmsAdapterResolution = CmsAdapterDoc | CmsAdapterTombstone;
|
|
444
|
+
type CmsAdapterDoc = { contents: Record<string, unknown> };
|
|
445
|
+
type CmsAdapterTombstone = { kind: 'gone' } | { kind: 'redirect'; to: string };
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Each `CmsScopeQuery` carries the composed `scopeId`, the `kind` (`'layout' | 'page'`), the route id, the resolved owned params, the field list, and the locale. Map those to backend reads however you want. The `context.fetch` argument is the SvelteKit request-scoped fetch for HTTP-backed adapters.
|
|
449
|
+
|
|
450
|
+
Page-kind docs own a `metadata` branch on the doc tree. `loadCms` aliases that branch onto `cms.metadata` (for `definePageMetaTags(...)`) without removing it from the doc — components addressing `metadata.title` etc. read straight through.
|
|
451
|
+
|
|
452
|
+
A page-kind scope can also resolve to a **tombstone** (`{ kind: 'gone' }` or `{ kind: 'redirect', to }`) instead of a doc. The loader short-circuits the page render and surfaces `gone` / `redirectTo` on the `LoadCmsResult` so the caller can return 410 / 308. Layout scopes never tombstone.
|
|
453
|
+
|
|
454
|
+
`fetchEntries` enumerates publishable entries at one route id (used by `generateEntries` for prerender). Tombstoned entries are returned with `redirectTo` / `gone` flags so prerender visits the URL and SvelteKit emits the redirect file; the loader filters them out of `cms.entries` before display.
|
|
455
|
+
|
|
456
|
+
### `mockAdapter` (built-in)
|
|
457
|
+
|
|
458
|
+
Useful for tests, demos, and pre-backend development. Storage is keyed by `[locale][routeId]`:
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
import { mockAdapter } from '@velastack/cms/server';
|
|
462
|
+
|
|
463
|
+
const adapter = mockAdapter({
|
|
464
|
+
layoutDocs: {
|
|
465
|
+
en: { '/(marketing)': { header: { title: 'Climb Angola' } } },
|
|
466
|
+
es: { '/(marketing)': { header: { title: 'Escalar Angola' } } }
|
|
467
|
+
},
|
|
468
|
+
pageDocs: {
|
|
469
|
+
en: {
|
|
470
|
+
'/(marketing)/about': [
|
|
471
|
+
{
|
|
472
|
+
params: {},
|
|
473
|
+
published: {
|
|
474
|
+
hero: { title: 'About us' },
|
|
475
|
+
metadata: { title: 'About us' }
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
]
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
A page can be **tombstoned at publish time** by setting `tombstone` on its `PageEntry`:
|
|
485
|
+
|
|
486
|
+
```ts
|
|
487
|
+
mockAdapter({
|
|
488
|
+
pageDocs: {
|
|
489
|
+
en: {
|
|
490
|
+
'/(marketing)/rooms/[slug]': [
|
|
491
|
+
// Hard 410 — this URL is permanently gone.
|
|
492
|
+
{ params: { slug: 'old-suite' }, published: {}, tombstone: { kind: 'gone' } },
|
|
493
|
+
// Permanent redirect — visitors land on the new URL.
|
|
494
|
+
{
|
|
495
|
+
params: { slug: 'legacy' },
|
|
496
|
+
published: {},
|
|
497
|
+
tombstone: { kind: 'redirect', to: '/rooms/suite-1' }
|
|
498
|
+
}
|
|
499
|
+
]
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Pass `resolvePreview` to enable release-preview overlay; release items can also stage tombstones in flight via `outcome` on a `page-delete` item:
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
{
|
|
509
|
+
kind: 'page-delete',
|
|
510
|
+
routeId: '/(marketing)/rooms/[slug]',
|
|
511
|
+
params: { slug: 'legacy' },
|
|
512
|
+
locale: 'en',
|
|
513
|
+
outcome: { kind: 'redirect', to: '/rooms/suite-1' } // omit for hard delete (404 after publish)
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Writing your own adapter
|
|
518
|
+
|
|
519
|
+
A skeleton sketch:
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
import type { CmsAdapter, CmsAdapterResolution } from '@velastack/cms/server';
|
|
523
|
+
|
|
524
|
+
export const myAdapter = (config: { project: string; client: MyClient }): CmsAdapter => ({
|
|
525
|
+
async fetchDocs(queries, { fetch, locale, previewKey }) {
|
|
526
|
+
const rows = await config.client.findMany({
|
|
527
|
+
project: config.project,
|
|
528
|
+
keys: queries.map((q) => ({
|
|
529
|
+
scopeId: q.scopeId,
|
|
530
|
+
routeId: q.routeId,
|
|
531
|
+
params: q.params,
|
|
532
|
+
locale: q.locale
|
|
533
|
+
})),
|
|
534
|
+
previewKey,
|
|
535
|
+
fetch
|
|
536
|
+
});
|
|
537
|
+
const out: Record<string, CmsAdapterResolution> = {};
|
|
538
|
+
for (const r of rows) {
|
|
539
|
+
out[r.scopeId] = r.tombstone ?? { contents: r.contents };
|
|
540
|
+
}
|
|
541
|
+
return out;
|
|
542
|
+
},
|
|
543
|
+
async fetchEntries(routeId, { fetch, locale }) {
|
|
544
|
+
const rows = await config.client.listEntries({
|
|
545
|
+
project: config.project,
|
|
546
|
+
routeId,
|
|
547
|
+
locale,
|
|
548
|
+
fetch
|
|
549
|
+
});
|
|
550
|
+
return rows.map((r) => ({
|
|
551
|
+
params: r.params,
|
|
552
|
+
metadata: r.metadata ?? {},
|
|
553
|
+
...(r.redirectTo ? { redirectTo: r.redirectTo } : {}),
|
|
554
|
+
...(r.gone ? { gone: true as const } : {})
|
|
555
|
+
}));
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
The adapter doesn't need to implement default-locale fallback — `resolveCmsPayload` issues a parallel default-locale `fetchDocs` when needed and merges trees on the loader side. Just return what's stored for the requested locale.
|
|
561
|
+
|
|
562
|
+
## Locales
|
|
563
|
+
|
|
564
|
+
Locale support is first-class and lives at the release / version dimension: every release contains items across every locale you've touched, so publishing ships all locales atomically. Items are tagged `(scope, locale, tree)`; switching the editor's preview locale is a free UI op against the same release.
|
|
565
|
+
|
|
566
|
+
**Configuration:** pass the supported set to `createCms({ locales })`. The first entry is the **default locale**.
|
|
567
|
+
|
|
568
|
+
**Per-request:** the consumer decides how to derive a locale from the request (pathname, `Accept-Language`, cookie, query param, …) and passes the resulting string to `loadCms(event, { locale })`. The library doesn't read URLs itself.
|
|
569
|
+
|
|
570
|
+
**Default-locale fallback:** when `locale !== locales[0]`, the loader fetches docs in both locales and merges (`mergeTree(default, requested)`), so a missing `hero.title` in `es` falls through to the EN value. Arrays replace wholesale — explicit ES content always overrides EN, but absent ES keys inherit. Page-kind tombstones in either locale apply (requested wins on conflict).
|
|
571
|
+
|
|
572
|
+
**Editor preview locale:** the admin bar uses `?locale=` to override whichever locale the consumer's logic would have picked, so editors can preview any locale at any URL without changing pathname routing. The bar's Locales panel shows per-locale edit counts (`workingCopyCountsByLocale`) plus translation gaps (default-locale items not yet edited in each other locale).
|
|
573
|
+
|
|
574
|
+
**On the payload:**
|
|
575
|
+
|
|
576
|
+
```ts
|
|
577
|
+
cms.locale; // 'es'
|
|
578
|
+
cms.locales; // ['en', 'es', 'fr']
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## Redirects & tombstones
|
|
582
|
+
|
|
583
|
+
Pages can be deleted with three different outcomes:
|
|
584
|
+
|
|
585
|
+
| Outcome | HTTP | When to use |
|
|
586
|
+
| ------------------- | ---- | ------------------------------------------------------------ |
|
|
587
|
+
| `notFound` (no doc) | 404 | Default — the URL just isn't there. |
|
|
588
|
+
| `gone` | 410 | URL was deliberately retired; tell crawlers to forget it. |
|
|
589
|
+
| `redirectTo` | 308 | URL moved permanently; preserve link equity to the new path. |
|
|
590
|
+
|
|
591
|
+
Tombstones live on the adapter's `PageEntry` (`tombstone: CmsAdapterTombstone`) for already-published deletions, and on a release's `page-delete` item (`outcome?: CmsAdapterTombstone`) for staged deletions in the editor's working copy. The loader returns whichever applies on `LoadCmsResult.{ gone, redirectTo }`; your `+layout.server.ts` branches on those flags before rendering.
|
|
592
|
+
|
|
593
|
+
For prerender, `fetchEntries` returns tombstoned entries with `redirectTo` / `gone` flags so SvelteKit visits the URL and emits the redirect file — but `cms.entries[routeId]` strips them, so `<CmsEntries>` lists never display deleted pages.
|
|
594
|
+
|
|
595
|
+
## Plugin options
|
|
596
|
+
|
|
597
|
+
```ts
|
|
598
|
+
cms({
|
|
599
|
+
routesDir: 'src/routes', // path to SvelteKit routes (default)
|
|
600
|
+
libDir: 'src/lib', // path to project lib (default)
|
|
601
|
+
components: [
|
|
602
|
+
// third-party CMS packs — see "Custom CMS components"
|
|
603
|
+
{ source: 'my-cms-pack', names: ['Hero', 'Quote'] }
|
|
604
|
+
],
|
|
605
|
+
traverse: [
|
|
606
|
+
// bare specifiers whose .svelte files should be walked
|
|
607
|
+
'my-design-system'
|
|
608
|
+
],
|
|
609
|
+
|
|
610
|
+
// Production build options — see "Production builds" above.
|
|
611
|
+
endpoint: 'https://cms.example.com/v1/projects/p1/cms',
|
|
612
|
+
locales: ['en'], // mirror createCms({ locales })
|
|
613
|
+
mediaDir: 'static/cms-media', // download target (default)
|
|
614
|
+
mediaPrefix: '/cms-media' // URL prefix in rewritten content (default)
|
|
615
|
+
});
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
The plugin also exposes `virtual:vela-cms/manifest` (typed via the package's ambient declaration). You almost never need to import it directly — `loadCms` does that internally — but it's there if you want to introspect the manifest at build time.
|
|
619
|
+
|
|
620
|
+
## Constraints (v1)
|
|
621
|
+
|
|
622
|
+
Static analysis is conservative on purpose:
|
|
623
|
+
|
|
624
|
+
- `name=` props must be string literals or `name={'literal'}`. Computed names are not extracted.
|
|
625
|
+
- Only static `import` of `.svelte` files is followed when walking the component graph. Bare specifiers (npm packages) are skipped unless they're the @velastack/cms package itself, are listed under `components`, or match a `traverse` pattern.
|
|
626
|
+
- `<svelte:component this={…} />` and dynamic component selection are not traced.
|
|
627
|
+
- Layout reset segments (`+page@layout.svelte`) are not yet supported.
|
|
628
|
+
|
|
629
|
+
Repeater item internals can be edited via the per-key inputs the editable variant emits; nested `<CmsText name="…" value={item.x} />` overrides stay non-editable by design.
|
|
630
|
+
|
|
631
|
+
## Project layout
|
|
632
|
+
|
|
633
|
+
```
|
|
634
|
+
plugin/src/ Vite plugin (route discovery, AST parse, manifest builder, transform)
|
|
635
|
+
src/lib/ Public library
|
|
636
|
+
components/cms/ Display + editable components, scope helpers, store
|
|
637
|
+
components/admin-bar/ Admin bar wrapper + async internal + SEO panel
|
|
638
|
+
srv/ loadCms, mockAdapter, types
|
|
639
|
+
src/routes/ Test harness / showcase
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
## License
|
|
643
|
+
|
|
644
|
+
MIT
|
package/dist/cms.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const loadCms: (event: import("@sveltejs/kit").ServerLoadEvent, { locale }: {
|
|
2
|
+
locale: string;
|
|
3
|
+
}) => Promise<import("./srv/index.js").LoadCmsResult>, generateEntries: {
|
|
4
|
+
(): Promise<import("./srv/index.js").CmsEntry<import("$app/types").RouteParams<import("$app/types").RouteId>>[]>;
|
|
5
|
+
<R extends import("$app/types").RouteId>(routeId: R): Promise<import("./srv/index.js").CmsEntry<import("$app/types").RouteParams<R>>[]>;
|
|
6
|
+
};
|