@xleddyl/nuxt-cms 0.9.4 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/module.d.mts +20 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1037 -148
- package/dist/runtime/app/blocks/CmsBlocks.d.vue.ts +1 -1
- package/dist/runtime/app/blocks/CmsBlocks.vue +57 -9
- package/dist/runtime/app/blocks/CmsBlocks.vue.d.ts +1 -1
- package/dist/runtime/app/blocks/CmsField.d.ts +24 -0
- package/dist/runtime/app/blocks/CmsField.js +16 -0
- package/dist/runtime/app/blocks/node.d.ts +37 -0
- package/dist/runtime/app/blocks/node.js +17 -0
- package/dist/runtime/app/blocks/resolve.d.ts +18 -0
- package/dist/runtime/app/blocks/resolve.js +34 -0
- package/dist/runtime/app/components/cms/BlockPicker.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue +87 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.d.vue.ts +38 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue +44 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue.d.ts +38 -0
- package/dist/runtime/app/components/cms/BlockTree.d.vue.ts +29 -0
- package/dist/runtime/app/components/cms/BlockTree.vue +322 -0
- package/dist/runtime/app/components/cms/BlockTree.vue.d.ts +29 -0
- package/dist/runtime/app/components/cms/BlocksField.vue +569 -218
- package/dist/runtime/app/components/cms/BrandMark.vue +7 -13
- package/dist/runtime/app/components/cms/ConfirmGroups.vue +15 -13
- package/dist/runtime/app/components/cms/{BlockPalette.vue.d.ts → EditorHistory.d.vue.ts} +6 -5
- package/dist/runtime/app/components/cms/EditorHistory.vue +34 -0
- package/dist/runtime/app/components/cms/{BlockPalette.d.vue.ts → EditorHistory.vue.d.ts} +6 -5
- package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +11 -5
- package/dist/runtime/app/components/cms/EntryDrawer.vue +183 -152
- package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +11 -5
- package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +16 -4
- package/dist/runtime/app/components/cms/EntryForm.vue +106 -146
- package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +16 -4
- package/dist/runtime/app/components/cms/FieldInput.vue +10 -2
- package/dist/runtime/app/components/cms/FormSections.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/FormSections.vue +71 -0
- package/dist/runtime/app/components/cms/FormSections.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/ImportModal.vue +46 -58
- package/dist/runtime/app/components/cms/LinkField.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/LinkField.vue +250 -0
- package/dist/runtime/app/components/cms/LinkField.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/ListFilters.vue +10 -12
- package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +2 -1
- package/dist/runtime/app/components/cms/LocaleSwitch.vue +29 -5
- package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +2 -1
- package/dist/runtime/app/components/cms/MediaDetail.d.vue.ts +2 -0
- package/dist/runtime/app/components/cms/MediaDetail.vue +112 -83
- package/dist/runtime/app/components/cms/MediaDetail.vue.d.ts +2 -0
- package/dist/runtime/app/components/cms/MediaField.vue +35 -25
- package/dist/runtime/app/components/cms/MediaFolderPicker.vue +26 -71
- package/dist/runtime/app/components/cms/MediaGallery.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/MediaGallery.vue +442 -419
- package/dist/runtime/app/components/cms/MediaGallery.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/MediaThumb.vue +9 -25
- package/dist/runtime/app/components/cms/MediaUpload.vue +1 -1
- package/dist/runtime/app/components/cms/{BlockOutline.d.vue.ts → NestedBlockList.d.vue.ts} +5 -5
- package/dist/runtime/app/components/cms/NestedBlockList.vue +87 -0
- package/dist/runtime/app/components/cms/{BlockOutline.vue.d.ts → NestedBlockList.vue.d.ts} +5 -5
- package/dist/runtime/app/components/cms/PoweredBy.vue +12 -5
- package/dist/runtime/app/components/cms/PresetModal.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/PresetModal.vue +70 -0
- package/dist/runtime/app/components/cms/PresetModal.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/RelationField.vue +2 -8
- package/dist/runtime/app/components/cms/SettingsAccount.vue +60 -71
- package/dist/runtime/app/components/cms/SettingsColumns.vue +47 -69
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue +50 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntry.d.vue.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntry.vue +11 -95
- package/dist/runtime/app/components/cms/SettingsEntry.vue.d.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntryActions.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue +42 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue +17 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsLayout.vue +18 -14
- package/dist/runtime/app/components/cms/SettingsPresetForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue +60 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresets.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue +65 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.d.vue.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue +45 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue.d.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue +59 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue +77 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue +117 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue +43 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSite.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue +140 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.d.vue.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue +82 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue.d.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue +179 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUsers.d.vue.ts +9 -6
- package/dist/runtime/app/components/cms/SettingsUsers.vue +88 -234
- package/dist/runtime/app/components/cms/SettingsUsers.vue.d.ts +9 -6
- package/dist/runtime/app/components/cms/SharePreview.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/SharePreview.vue +219 -0
- package/dist/runtime/app/components/cms/SharePreview.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/SlugField.vue +5 -3
- package/dist/runtime/app/components/cms/StatusBadge.vue +9 -8
- package/dist/runtime/app/components/cms/TranslationBadges.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue +45 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationMenu.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue +28 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/VisualEditor.d.vue.ts +22 -4
- package/dist/runtime/app/components/cms/VisualEditor.vue +1152 -335
- package/dist/runtime/app/components/cms/VisualEditor.vue.d.ts +22 -4
- package/dist/runtime/app/components/cms/WelcomeModal.vue +32 -50
- package/dist/runtime/app/composables/cms-clipboard.d.ts +8 -0
- package/dist/runtime/app/composables/cms-clipboard.js +38 -0
- package/dist/runtime/app/composables/cms-confirm.d.ts +6 -8
- package/dist/runtime/app/composables/cms-confirm.js +5 -7
- package/dist/runtime/app/composables/cms-entry-disabled.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry-disabled.js +7 -0
- package/dist/runtime/app/composables/cms-entry-form.d.ts +15 -0
- package/dist/runtime/app/composables/cms-entry-form.js +40 -0
- package/dist/runtime/app/composables/cms-entry-settings.d.ts +30 -0
- package/dist/runtime/app/composables/cms-entry-settings.js +122 -0
- package/dist/runtime/app/composables/cms-entry.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry.js +36 -11
- package/dist/runtime/app/composables/cms-history.d.ts +10 -0
- package/dist/runtime/app/composables/cms-history.js +24 -0
- package/dist/runtime/app/composables/cms-link-targets.d.ts +10 -0
- package/dist/runtime/app/composables/cms-link-targets.js +40 -0
- package/dist/runtime/app/composables/cms-link-usage.d.ts +1 -0
- package/dist/runtime/app/composables/cms-link-usage.js +45 -0
- package/dist/runtime/app/composables/cms-media-library.d.ts +19 -9
- package/dist/runtime/app/composables/cms-media-library.js +31 -13
- package/dist/runtime/app/composables/cms-media-uploader.js +6 -20
- package/dist/runtime/app/composables/cms-permissions.d.ts +18 -0
- package/dist/runtime/app/composables/cms-permissions.js +54 -0
- package/dist/runtime/app/composables/cms-presets.d.ts +11 -0
- package/dist/runtime/app/composables/cms-presets.js +40 -0
- package/dist/runtime/app/composables/cms-preview-mode.d.ts +4 -0
- package/dist/runtime/app/composables/cms-preview-mode.js +8 -0
- package/dist/runtime/app/composables/cms-query.d.ts +3 -0
- package/dist/runtime/app/composables/cms-query.js +45 -20
- package/dist/runtime/app/composables/cms-record-list.d.ts +31 -0
- package/dist/runtime/app/composables/cms-record-list.js +75 -0
- package/dist/runtime/app/composables/cms-route.d.ts +6 -0
- package/dist/runtime/app/composables/cms-route.js +36 -0
- package/dist/runtime/app/composables/cms-runtime.d.ts +1 -0
- package/dist/runtime/app/composables/cms-save-handoff.d.ts +4 -0
- package/dist/runtime/app/composables/cms-save-handoff.js +19 -0
- package/dist/runtime/app/composables/cms-seo.d.ts +12 -0
- package/dist/runtime/app/composables/cms-seo.js +69 -0
- package/dist/runtime/app/composables/cms-settings-form.d.ts +13 -0
- package/dist/runtime/app/composables/cms-settings-form.js +29 -0
- package/dist/runtime/app/composables/cms-settings-sections.d.ts +26 -0
- package/dist/runtime/app/composables/cms-settings-sections.js +181 -0
- package/dist/runtime/app/composables/cms-settings-stores.d.ts +52 -0
- package/dist/runtime/app/composables/cms-settings-stores.js +156 -0
- package/dist/runtime/app/composables/cms-settings.d.ts +3 -3
- package/dist/runtime/app/composables/cms-settings.js +14 -8
- package/dist/runtime/app/composables/cms-site-disabled.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site-disabled.js +8 -0
- package/dist/runtime/app/composables/cms-site.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site.js +22 -0
- package/dist/runtime/app/composables/cms-toast.d.ts +2 -8
- package/dist/runtime/app/composables/cms-toast.js +3 -9
- package/dist/runtime/app/composables/cms-translation-actions.d.ts +15 -0
- package/dist/runtime/app/composables/cms-translation-actions.js +108 -0
- package/dist/runtime/app/css-url.d.ts +4 -0
- package/dist/runtime/app/image/CmsImage.d.vue.ts +89 -0
- package/dist/runtime/app/image/CmsImage.vue +91 -0
- package/dist/runtime/app/image/CmsImage.vue.d.ts +89 -0
- package/dist/runtime/app/layouts/cms-admin.d.vue.ts +2 -2
- package/dist/runtime/app/layouts/cms-admin.vue +98 -76
- package/dist/runtime/app/layouts/cms-admin.vue.d.ts +2 -2
- package/dist/runtime/app/layouts/cms-editor.vue +7 -2
- package/dist/runtime/app/links/CmsLink.d.vue.ts +18 -0
- package/dist/runtime/app/links/CmsLink.vue +18 -0
- package/dist/runtime/app/links/CmsLink.vue.d.ts +18 -0
- package/dist/runtime/app/middleware/cms-auth.js +13 -1
- package/dist/runtime/app/pages/admin-collection.vue +473 -297
- package/dist/runtime/app/pages/admin-content.vue +190 -53
- package/dist/runtime/app/pages/admin-entry.vue +79 -75
- package/dist/runtime/app/pages/admin-index.vue +18 -5
- package/dist/runtime/app/pages/admin-login.vue +1 -1
- package/dist/runtime/app/pages/admin-logout.vue +1 -1
- package/dist/runtime/app/pages/admin-media.vue +3 -3
- package/dist/runtime/app/pages/admin-preview.vue +112 -24
- package/dist/runtime/app/plugins/cms-field.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-field.js +8 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.js +19 -0
- package/dist/runtime/app/plugins/cms-preview.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview.js +27 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.d.vue.ts +7 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue +63 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue.d.ts +7 -0
- package/dist/runtime/app/preview/PreviewOverlay.d.vue.ts +17 -3
- package/dist/runtime/app/preview/PreviewOverlay.vue +629 -166
- package/dist/runtime/app/preview/PreviewOverlay.vue.d.ts +17 -3
- package/dist/runtime/app/preview/field-marker.d.ts +12 -0
- package/dist/runtime/app/preview/field-marker.js +20 -0
- package/dist/runtime/app/preview/inline-text.d.ts +15 -0
- package/dist/runtime/app/preview/inline-text.js +158 -0
- package/dist/runtime/app/utils/block-editor.d.ts +22 -0
- package/dist/runtime/app/utils/block-editor.js +12 -0
- package/dist/runtime/app/utils/keyboard.d.ts +6 -0
- package/dist/runtime/app/utils/keyboard.js +52 -0
- package/dist/runtime/app/utils/redirects.d.ts +6 -0
- package/dist/runtime/app/utils/redirects.js +7 -0
- package/dist/runtime/app/utils/ui.d.ts +8 -0
- package/dist/runtime/app/utils/ui.js +15 -1
- package/dist/runtime/app/utils/users.d.ts +15 -0
- package/dist/runtime/app/utils/users.js +14 -0
- package/dist/runtime/assets/cms.css +1 -1
- package/dist/runtime/generate.js +16 -2
- package/dist/runtime/server/api/collection-export.get.js +3 -2
- package/dist/runtime/server/api/collection-import.post.js +39 -11
- package/dist/runtime/server/api/collection.get.js +27 -9
- package/dist/runtime/server/api/collection.post.js +28 -11
- package/dist/runtime/server/api/collection.put.js +35 -13
- package/dist/runtime/server/api/item.delete.js +19 -7
- package/dist/runtime/server/api/item.get.js +3 -2
- package/dist/runtime/server/api/item.put.js +52 -18
- package/dist/runtime/server/api/link-usage.post.d.ts +5 -0
- package/dist/runtime/server/api/link-usage.post.js +21 -0
- package/dist/runtime/server/api/media-delete.post.js +4 -2
- package/dist/runtime/server/api/media-download.get.js +2 -2
- package/dist/runtime/server/api/media-folder.delete.js +8 -2
- package/dist/runtime/server/api/media-folder.patch.js +10 -2
- package/dist/runtime/server/api/media-folder.post.js +4 -2
- package/dist/runtime/server/api/media-move.post.js +7 -4
- package/dist/runtime/server/api/media-presign.post.js +2 -2
- package/dist/runtime/server/api/media-upload.put.js +2 -2
- package/dist/runtime/server/api/media-usage.post.d.ts +2 -4
- package/dist/runtime/server/api/media-usage.post.js +13 -9
- package/dist/runtime/server/api/media.delete.js +4 -2
- package/dist/runtime/server/api/media.get.js +22 -7
- package/dist/runtime/server/api/media.post.js +4 -2
- package/dist/runtime/server/api/media.put.js +33 -16
- package/dist/runtime/server/api/preset.delete.d.ts +4 -0
- package/dist/runtime/server/api/preset.delete.js +9 -0
- package/dist/runtime/server/api/preset.put.d.ts +3 -0
- package/dist/runtime/server/api/preset.put.js +23 -0
- package/dist/runtime/server/api/presets-field.get.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.get.js +13 -0
- package/dist/runtime/server/api/presets-field.post.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.post.js +36 -0
- package/dist/runtime/server/api/presets.get.d.ts +3 -0
- package/dist/runtime/server/api/presets.get.js +7 -0
- package/dist/runtime/server/api/preview-link.delete.d.ts +4 -0
- package/dist/runtime/server/api/preview-link.delete.js +15 -0
- package/dist/runtime/server/api/preview-links.get.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.get.js +10 -0
- package/dist/runtime/server/api/preview-links.post.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.post.js +17 -0
- package/dist/runtime/server/api/preview.get.d.ts +3 -0
- package/dist/runtime/server/api/preview.get.js +7 -0
- package/dist/runtime/server/api/redirect.delete.d.ts +4 -0
- package/dist/runtime/server/api/redirect.delete.js +10 -0
- package/dist/runtime/server/api/redirect.put.d.ts +3 -0
- package/dist/runtime/server/api/redirect.put.js +8 -0
- package/dist/runtime/server/api/redirects-import.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects-import.post.js +13 -0
- package/dist/runtime/server/api/redirects.get.d.ts +3 -0
- package/dist/runtime/server/api/redirects.get.js +7 -0
- package/dist/runtime/server/api/redirects.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects.post.js +7 -0
- package/dist/runtime/server/api/search.get.js +8 -3
- package/dist/runtime/server/api/settings.delete.js +2 -2
- package/dist/runtime/server/api/settings.get.js +4 -2
- package/dist/runtime/server/api/settings.put.js +2 -2
- package/dist/runtime/server/api/site.get.d.ts +3 -0
- package/dist/runtime/server/api/site.get.js +7 -0
- package/dist/runtime/server/api/site.put.d.ts +3 -0
- package/dist/runtime/server/api/site.put.js +16 -0
- package/dist/runtime/server/api/tags.get.js +3 -2
- package/dist/runtime/server/api/user.delete.js +8 -2
- package/dist/runtime/server/api/user.put.js +14 -4
- package/dist/runtime/server/api/users.get.js +2 -2
- package/dist/runtime/server/api/users.post.js +8 -4
- package/dist/runtime/server/middleware/hidden-site.d.ts +2 -0
- package/dist/runtime/server/middleware/hidden-site.js +8 -0
- package/dist/runtime/server/middleware/preview-links.d.ts +2 -0
- package/dist/runtime/server/middleware/preview-links.js +6 -0
- package/dist/runtime/server/middleware/redirects.d.ts +2 -0
- package/dist/runtime/server/middleware/redirects.js +18 -0
- package/dist/runtime/server/plugins/session-validate.js +4 -1
- package/dist/runtime/server/plugins/webhooks.d.ts +3 -0
- package/dist/runtime/server/plugins/webhooks.js +27 -0
- package/dist/runtime/server/routes/graphql.js +5 -3
- package/dist/runtime/server/routes/robots.d.ts +2 -0
- package/dist/runtime/server/routes/robots.js +16 -0
- package/dist/runtime/server/routes/sitemap.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.js +67 -0
- package/dist/runtime/server/utils/cached-value.d.ts +5 -0
- package/dist/runtime/server/utils/cached-value.js +24 -0
- package/dist/runtime/server/utils/content-routes.d.ts +5 -0
- package/dist/runtime/server/utils/content-routes.js +29 -0
- package/dist/runtime/server/utils/field-rules.d.ts +19 -0
- package/dist/runtime/server/utils/field-rules.js +72 -0
- package/dist/runtime/server/utils/graphql.d.ts +3 -1
- package/dist/runtime/server/utils/graphql.js +143 -28
- package/dist/runtime/server/utils/hooks.d.ts +14 -0
- package/dist/runtime/server/utils/hooks.js +71 -0
- package/dist/runtime/server/utils/import.d.ts +7 -2
- package/dist/runtime/server/utils/import.js +108 -14
- package/dist/runtime/server/utils/link-usage.d.ts +4 -0
- package/dist/runtime/server/utils/link-usage.js +28 -0
- package/dist/runtime/server/utils/media-references.d.ts +6 -1
- package/dist/runtime/server/utils/media-references.js +49 -20
- package/dist/runtime/server/utils/media-usage.d.ts +2 -0
- package/dist/runtime/server/utils/media-usage.js +5 -3
- package/dist/runtime/server/utils/page-rows.d.ts +2 -0
- package/dist/runtime/server/utils/page-rows.js +42 -29
- package/dist/runtime/server/utils/permissions.d.ts +14 -0
- package/dist/runtime/server/utils/permissions.js +43 -0
- package/dist/runtime/server/utils/presets.d.ts +18 -0
- package/dist/runtime/server/utils/presets.js +68 -0
- package/dist/runtime/server/utils/preview-links.d.ts +207 -0
- package/dist/runtime/server/utils/preview-links.js +161 -0
- package/dist/runtime/server/utils/redirects.d.ts +31 -0
- package/dist/runtime/server/utils/redirects.js +240 -0
- package/dist/runtime/server/utils/registry.js +4 -1
- package/dist/runtime/server/utils/relations.d.ts +8 -0
- package/dist/runtime/server/utils/relations.js +25 -6
- package/dist/runtime/server/utils/require-admin.d.ts +0 -1
- package/dist/runtime/server/utils/require-admin.js +4 -8
- package/dist/runtime/server/utils/search.d.ts +1 -0
- package/dist/runtime/server/utils/search.js +44 -19
- package/dist/runtime/server/utils/site.d.ts +23 -0
- package/dist/runtime/server/utils/site.js +79 -0
- package/dist/runtime/server/utils/sitemap.d.ts +12 -0
- package/dist/runtime/server/utils/sitemap.js +109 -0
- package/dist/runtime/server/utils/user-management.d.ts +4 -0
- package/dist/runtime/server/utils/user-management.js +20 -0
- package/dist/runtime/server/utils/users.d.ts +1 -1
- package/dist/runtime/server/utils/users.js +1 -1
- package/dist/runtime/server/utils/visibility.d.ts +1 -0
- package/dist/runtime/server/utils/visibility.js +6 -1
- package/dist/runtime/server/utils/webhooks.d.ts +24 -0
- package/dist/runtime/server/utils/webhooks.js +90 -0
- package/dist/runtime/shared/block-paths.d.ts +66 -0
- package/dist/runtime/shared/block-paths.js +310 -0
- package/dist/runtime/shared/block-picker.d.ts +18 -0
- package/dist/runtime/shared/block-picker.js +31 -0
- package/dist/runtime/shared/block-signature.d.ts +4 -0
- package/dist/runtime/shared/block-signature.js +46 -0
- package/dist/runtime/shared/clipboard.d.ts +31 -0
- package/dist/runtime/shared/clipboard.js +206 -0
- package/dist/runtime/shared/conditions.d.ts +36 -0
- package/dist/runtime/shared/conditions.js +100 -0
- package/dist/runtime/shared/content-routes.d.ts +42 -0
- package/dist/runtime/shared/content-routes.js +237 -0
- package/dist/runtime/shared/duplicate.js +2 -1
- package/dist/runtime/shared/editor-layout.d.ts +18 -0
- package/dist/runtime/shared/editor-layout.js +9 -0
- package/dist/runtime/shared/field-options.d.ts +21 -0
- package/dist/runtime/shared/field-options.js +90 -0
- package/dist/runtime/shared/graphql-sdl.d.ts +1 -0
- package/dist/runtime/shared/graphql-sdl.js +50 -26
- package/dist/runtime/shared/history.d.ts +27 -0
- package/dist/runtime/shared/history.js +123 -0
- package/dist/runtime/shared/hooks.d.ts +45 -0
- package/dist/runtime/shared/hooks.js +28 -0
- package/dist/runtime/shared/index.d.ts +90 -21
- package/dist/runtime/shared/index.js +121 -37
- package/dist/runtime/shared/inline-text.d.ts +38 -0
- package/dist/runtime/shared/inline-text.js +85 -0
- package/dist/runtime/shared/layout.d.ts +2 -1
- package/dist/runtime/shared/layout.js +3 -0
- package/dist/runtime/shared/links.d.ts +48 -0
- package/dist/runtime/shared/links.js +155 -0
- package/dist/runtime/shared/list-filters.d.ts +2 -0
- package/dist/runtime/shared/list-filters.js +2 -0
- package/dist/runtime/shared/media-focal.d.ts +30 -0
- package/dist/runtime/shared/media-focal.js +52 -0
- package/dist/runtime/shared/migrations-dir.d.ts +14 -0
- package/dist/runtime/shared/migrations-dir.js +48 -2
- package/dist/runtime/shared/presets.d.ts +67 -0
- package/dist/runtime/shared/presets.js +127 -0
- package/dist/runtime/shared/preview-links.d.ts +64 -0
- package/dist/runtime/shared/preview-links.js +54 -0
- package/dist/runtime/shared/preview.d.ts +124 -21
- package/dist/runtime/shared/preview.js +419 -81
- package/dist/runtime/shared/redirects.d.ts +75 -0
- package/dist/runtime/shared/redirects.js +230 -0
- package/dist/runtime/shared/roles.d.ts +57 -0
- package/dist/runtime/shared/roles.js +293 -0
- package/dist/runtime/shared/save-state.d.ts +8 -0
- package/dist/runtime/shared/save-state.js +15 -0
- package/dist/runtime/shared/search.d.ts +16 -1
- package/dist/runtime/shared/search.js +41 -7
- package/dist/runtime/shared/seo.d.ts +167 -0
- package/dist/runtime/shared/seo.js +335 -0
- package/dist/runtime/shared/transfer.js +2 -1
- package/dist/runtime/shared/translations.d.ts +29 -0
- package/dist/runtime/shared/translations.js +123 -0
- package/dist/runtime/shared/users.d.ts +1 -1
- package/dist/runtime/shared/validation.d.ts +23 -1
- package/dist/runtime/shared/validation.js +214 -64
- package/dist/runtime/webhooks.d.ts +33 -0
- package/dist/runtime/webhooks.js +57 -0
- package/dist/types.d.mts +5 -1
- package/package.json +6 -2
- package/dist/runtime/app/components/cms/BlockOutline.vue +0 -88
- package/dist/runtime/app/components/cms/BlockPalette.vue +0 -31
- package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +0 -12
- package/dist/runtime/app/components/cms/SettingsModal.vue +0 -126
- package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +0 -12
- package/dist/runtime/app/composables/cms-field-focus.d.ts +0 -4
- package/dist/runtime/app/composables/cms-field-focus.js +0 -20
|
@@ -22,28 +22,46 @@ import { useRuntimeConfig } from "#imports";
|
|
|
22
22
|
import {
|
|
23
23
|
CONTENT_SLUG_FIELD,
|
|
24
24
|
CONTENT_PUBLISHED_AT_FIELD,
|
|
25
|
+
contentItemPath,
|
|
26
|
+
contentItemPaths,
|
|
27
|
+
contentRouteLocales,
|
|
25
28
|
decodeTranslatableMedia,
|
|
26
29
|
entryFieldsFor,
|
|
30
|
+
blockFieldsDeep,
|
|
31
|
+
blocksFieldPaths,
|
|
27
32
|
hasTranslatableBlockFields,
|
|
33
|
+
isExternalLinkUrl,
|
|
28
34
|
isMultiValueField,
|
|
29
35
|
isPrivateField,
|
|
36
|
+
isRoutedContent,
|
|
37
|
+
isStringListField,
|
|
30
38
|
isTranslatableMediaField,
|
|
39
|
+
linkHref,
|
|
40
|
+
linkLabelText,
|
|
41
|
+
localeText,
|
|
31
42
|
localizeBlocks,
|
|
43
|
+
mapBlocks,
|
|
32
44
|
mediaPublicUrl,
|
|
33
45
|
mediaTypeFor,
|
|
34
46
|
pickTranslatedMedia,
|
|
47
|
+
previewGrantAllows,
|
|
35
48
|
publicBlocks,
|
|
49
|
+
readLinkValue,
|
|
50
|
+
relationTarget,
|
|
36
51
|
translatableFieldKeys
|
|
37
52
|
} from "../../shared/index.js";
|
|
38
53
|
import {
|
|
54
|
+
SITE_QUERY_FIELD,
|
|
39
55
|
blockTypeName,
|
|
40
56
|
blocksFieldTypeName,
|
|
41
57
|
renderGraphqlSdl,
|
|
42
58
|
typeName
|
|
43
59
|
} from "../../shared/graphql-sdl.js";
|
|
44
60
|
import { normalizeTimestampFields } from "../../shared/timestamps.js";
|
|
61
|
+
import { findContentByPath } from "./content-routes.js";
|
|
45
62
|
import { useMediaIndex } from "./media-index.js";
|
|
46
63
|
import { readPages } from "./page-storage.js";
|
|
64
|
+
import { readSiteSettings } from "./site.js";
|
|
47
65
|
import {
|
|
48
66
|
isListFilterOperator,
|
|
49
67
|
joinListCondition,
|
|
@@ -52,7 +70,7 @@ import {
|
|
|
52
70
|
} from "./list-filter.js";
|
|
53
71
|
import { getContentI18n, resolveTable, tableColumns } from "./registry.js";
|
|
54
72
|
import { singleRowFirst } from "./single-row.js";
|
|
55
|
-
import { visibilityConditions } from "./visibility.js";
|
|
73
|
+
import { visibilityConditions, visibleOrPreviewConditions } from "./visibility.js";
|
|
56
74
|
const MAX_LIMIT = 100;
|
|
57
75
|
const DEFAULT_LIMIT = 50;
|
|
58
76
|
const LOCALE = Symbol("locale");
|
|
@@ -99,6 +117,12 @@ function tableFor(name) {
|
|
|
99
117
|
function publishedFilter(entry, table) {
|
|
100
118
|
return visibilityConditions(entry, tableColumns(table));
|
|
101
119
|
}
|
|
120
|
+
function previewItemId(ctx, name) {
|
|
121
|
+
return previewGrantAllows(ctx.preview, name) ? ctx.preview.itemId : void 0;
|
|
122
|
+
}
|
|
123
|
+
function singleItemFilter(entry, table, ctx, name) {
|
|
124
|
+
return visibleOrPreviewConditions(entry, tableColumns(table), previewItemId(ctx, name));
|
|
125
|
+
}
|
|
102
126
|
function localeOf(parent) {
|
|
103
127
|
return parent[LOCALE] ?? getContentI18n().defaultLocale;
|
|
104
128
|
}
|
|
@@ -112,6 +136,23 @@ function resolveLocaleArg(locale) {
|
|
|
112
136
|
}
|
|
113
137
|
return String(locale);
|
|
114
138
|
}
|
|
139
|
+
function linkSource(value, locale) {
|
|
140
|
+
const link = readLinkValue(value);
|
|
141
|
+
if (!link || !link.url && !(link.entry && link.id)) return null;
|
|
142
|
+
return { link, locale };
|
|
143
|
+
}
|
|
144
|
+
function blockLinks(field, value, locale) {
|
|
145
|
+
return mapBlocks(field, value, (item, block) => {
|
|
146
|
+
const keys = Object.entries(block?.fields ?? {}).filter(([, blockField]) => blockField.type === "link").map(([key]) => key);
|
|
147
|
+
if (!keys.length) return item;
|
|
148
|
+
const resolved = { ...item };
|
|
149
|
+
for (const key of keys) resolved[key] = linkSource(resolved[key], locale);
|
|
150
|
+
return resolved;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function hasBlockLinks(field) {
|
|
154
|
+
return blockFieldsDeep(field).some((blockField) => blockField.type === "link");
|
|
155
|
+
}
|
|
115
156
|
function localizeRow(entry, row, locale) {
|
|
116
157
|
const { defaultLocale } = getContentI18n();
|
|
117
158
|
const result = normalizeTimestampFields({ ...row, [LOCALE]: locale });
|
|
@@ -126,9 +167,15 @@ function localizeRow(entry, row, locale) {
|
|
|
126
167
|
result[key] = value?.[locale] ?? value?.[defaultLocale] ?? null;
|
|
127
168
|
}
|
|
128
169
|
for (const [key, field] of Object.entries(fields)) {
|
|
129
|
-
if (isPrivateField(field)
|
|
170
|
+
if (isPrivateField(field)) continue;
|
|
171
|
+
if (field.type === "link") {
|
|
172
|
+
result[key] = linkSource(result[key], locale);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (field.type !== "blocks") continue;
|
|
130
176
|
const visible = publicBlocks(field, result[key]);
|
|
131
|
-
|
|
177
|
+
const localized = hasTranslatableBlockFields(field) ? localizeBlocks(field, visible, locale, defaultLocale) : visible;
|
|
178
|
+
result[key] = hasBlockLinks(field) ? blockLinks(field, localized, locale) : localized;
|
|
132
179
|
}
|
|
133
180
|
return result;
|
|
134
181
|
}
|
|
@@ -235,8 +282,8 @@ async function resolveMedia(ctx, key) {
|
|
|
235
282
|
if (isLocalMedia()) {
|
|
236
283
|
const file = (await useMediaIndex()).get(key);
|
|
237
284
|
if (!file) return null;
|
|
238
|
-
const
|
|
239
|
-
return mediaObject(key, { ...file,
|
|
285
|
+
const meta = await loadMediaMeta(ctx).load(key);
|
|
286
|
+
return mediaObject(key, { ...file, ...meta });
|
|
240
287
|
}
|
|
241
288
|
const row = await loadMedia(ctx).load(key);
|
|
242
289
|
return row ? mediaObject(key, row) : null;
|
|
@@ -262,9 +309,9 @@ function loadManyToMany(ctx, name, key, field) {
|
|
|
262
309
|
`m2m:${name}:${key}`,
|
|
263
310
|
async (sourceIds) => {
|
|
264
311
|
const config = cmsConfig;
|
|
265
|
-
const targetEntry = config[field
|
|
312
|
+
const targetEntry = config[relationTarget(field)];
|
|
266
313
|
const join = cmsTables[`${name}_${key}`];
|
|
267
|
-
const target = tableFor(field
|
|
314
|
+
const target = tableFor(relationTarget(field));
|
|
268
315
|
const joinCols = tableColumns(join);
|
|
269
316
|
const rows = await useDb().select({ sourceId: joinCols.sourceId, target }).from(join).innerJoin(target, eq(joinCols.targetId, tableColumns(target).id)).where(
|
|
270
317
|
and(inArray(joinCols.sourceId, sourceIds), ...publishedFilter(targetEntry, target))
|
|
@@ -294,14 +341,47 @@ function loadMedia(ctx) {
|
|
|
294
341
|
);
|
|
295
342
|
});
|
|
296
343
|
}
|
|
297
|
-
function
|
|
298
|
-
return getLoader(ctx, "
|
|
344
|
+
function loadMediaMeta(ctx) {
|
|
345
|
+
return getLoader(ctx, "mediaMeta", async (keys) => {
|
|
299
346
|
const media = cmsTables.cms_media;
|
|
300
347
|
const columns = tableColumns(media);
|
|
301
|
-
const rows = await useDb().select({
|
|
302
|
-
|
|
348
|
+
const rows = await useDb().select({
|
|
349
|
+
key: columns.key,
|
|
350
|
+
alt: columns.alt,
|
|
351
|
+
focalX: columns.focalX,
|
|
352
|
+
focalY: columns.focalY
|
|
353
|
+
}).from(media).where(inArray(columns.key, keys));
|
|
354
|
+
return new Map(
|
|
355
|
+
rows.map((row) => [
|
|
356
|
+
row.key,
|
|
357
|
+
{
|
|
358
|
+
alt: row.alt ?? null,
|
|
359
|
+
focalX: row.focalX ?? null,
|
|
360
|
+
focalY: row.focalY ?? null
|
|
361
|
+
}
|
|
362
|
+
])
|
|
363
|
+
);
|
|
303
364
|
});
|
|
304
365
|
}
|
|
366
|
+
async function resolveLinkHref(ctx, source) {
|
|
367
|
+
const config = cmsConfig;
|
|
368
|
+
const { link, locale } = source;
|
|
369
|
+
const entry = link.entry ? config[link.entry] : void 0;
|
|
370
|
+
if (link.url || !link.entry || !link.id || !isRoutedContent(entry))
|
|
371
|
+
return linkHref(config, link, locale);
|
|
372
|
+
const target = await loadById(ctx, link.entry).load(link.id);
|
|
373
|
+
return target ? linkHref(config, link, locale, target) : null;
|
|
374
|
+
}
|
|
375
|
+
function linkResolvers() {
|
|
376
|
+
return {
|
|
377
|
+
href: (source, _args, ctx) => resolveLinkHref(ctx, source),
|
|
378
|
+
label: (source) => linkLabelText(source.link.label, source.locale, getContentI18n().defaultLocale),
|
|
379
|
+
newTab: (source) => !!source.link.newTab,
|
|
380
|
+
external: (source) => isExternalLinkUrl(source.link.url),
|
|
381
|
+
entry: (source) => source.link.url ? null : source.link.entry ?? null,
|
|
382
|
+
id: (source) => source.link.url ? null : source.link.id ?? null
|
|
383
|
+
};
|
|
384
|
+
}
|
|
305
385
|
function mediaObject(key, row) {
|
|
306
386
|
return {
|
|
307
387
|
key,
|
|
@@ -312,6 +392,8 @@ function mediaObject(key, row) {
|
|
|
312
392
|
size: row?.size ?? null,
|
|
313
393
|
width: row?.width ?? null,
|
|
314
394
|
height: row?.height ?? null,
|
|
395
|
+
focalX: row?.focalX ?? null,
|
|
396
|
+
focalY: row?.focalY ?? null,
|
|
315
397
|
type: mediaTypeFor(row?.mime, key)
|
|
316
398
|
};
|
|
317
399
|
}
|
|
@@ -322,25 +404,33 @@ function entryResolvers(config, name, entry) {
|
|
|
322
404
|
if (field.type === "relation" && field.cardinality === "many-to-many") {
|
|
323
405
|
resolvers[key] = async (parent, _args, ctx) => {
|
|
324
406
|
const rows = await loadManyToMany(ctx, name, key, field).load(parent.id);
|
|
325
|
-
return (rows ?? []).map(
|
|
407
|
+
return (rows ?? []).map(
|
|
408
|
+
(row) => localizeRow(config[relationTarget(field)], row, localeOf(parent))
|
|
409
|
+
);
|
|
326
410
|
};
|
|
327
411
|
} else if (field.type === "relation") {
|
|
328
412
|
resolvers[key] = async (parent, _args, ctx) => {
|
|
329
413
|
const id = parent[key];
|
|
330
414
|
if (id == null) return null;
|
|
331
|
-
const row = await loadById(ctx, field
|
|
332
|
-
return row ? localizeRow(config[field
|
|
415
|
+
const row = await loadById(ctx, relationTarget(field)).load(id);
|
|
416
|
+
return row ? localizeRow(config[relationTarget(field)], row, localeOf(parent)) : null;
|
|
333
417
|
};
|
|
334
418
|
} else if (field.type === "media") {
|
|
335
419
|
resolvers[key] = (parent, _args, ctx) => resolveMedia(ctx, parent[key]);
|
|
420
|
+
} else if (isStringListField(field)) {
|
|
421
|
+
resolvers[key] = (parent) => Array.isArray(parent[key]) ? parent[key] : [];
|
|
336
422
|
}
|
|
337
423
|
}
|
|
424
|
+
if (isRoutedContent(entry)) {
|
|
425
|
+
resolvers.path = (parent) => contentItemPath(entry, parent, localeOf(parent));
|
|
426
|
+
resolvers.paths = (parent) => contentItemPaths(entry, parent, contentRouteLocales(getContentI18n()));
|
|
427
|
+
}
|
|
338
428
|
return resolvers;
|
|
339
429
|
}
|
|
340
|
-
function blockResolvers(name,
|
|
430
|
+
function blockResolvers(name, schemaPath, field) {
|
|
341
431
|
const resolvers = {};
|
|
342
|
-
resolvers[blocksFieldTypeName(name,
|
|
343
|
-
__resolveType: (value) => blockTypeName(name,
|
|
432
|
+
resolvers[blocksFieldTypeName(name, schemaPath)] = {
|
|
433
|
+
__resolveType: (value) => blockTypeName(name, schemaPath, String(value.type))
|
|
344
434
|
};
|
|
345
435
|
for (const [blockName, block] of Object.entries(field.blocks ?? {})) {
|
|
346
436
|
const mediaFields = Object.entries(block.fields).filter(([, f]) => f.type === "media");
|
|
@@ -349,7 +439,7 @@ function blockResolvers(name, key, field) {
|
|
|
349
439
|
for (const [blockFieldKey] of mediaFields) {
|
|
350
440
|
fieldLevel[blockFieldKey] = (parent, _args, ctx) => resolveMedia(ctx, parent[blockFieldKey]);
|
|
351
441
|
}
|
|
352
|
-
resolvers[blockTypeName(name,
|
|
442
|
+
resolvers[blockTypeName(name, schemaPath, blockName)] = fieldLevel;
|
|
353
443
|
}
|
|
354
444
|
return resolvers;
|
|
355
445
|
}
|
|
@@ -358,14 +448,24 @@ export function buildCmsSchema() {
|
|
|
358
448
|
const entries = Object.entries(config);
|
|
359
449
|
const queryResolvers = {};
|
|
360
450
|
const typeResolvers = {};
|
|
451
|
+
queryResolvers[SITE_QUERY_FIELD] = async (_, args, ctx) => {
|
|
452
|
+
const locale = resolveLocaleArg(args.locale);
|
|
453
|
+
const { defaultLocale } = getContentI18n();
|
|
454
|
+
const settings = await readSiteSettings();
|
|
455
|
+
return {
|
|
456
|
+
name: localeText(settings.name, locale, defaultLocale),
|
|
457
|
+
titleTemplate: settings.titleTemplate,
|
|
458
|
+
description: localeText(settings.description, locale, defaultLocale),
|
|
459
|
+
image: await resolveMedia(ctx, settings.image),
|
|
460
|
+
hidden: settings.hidden
|
|
461
|
+
};
|
|
462
|
+
};
|
|
361
463
|
for (const [name, entry] of entries) {
|
|
362
464
|
const gqlType = typeName(name);
|
|
363
465
|
const fieldLevel = entryResolvers(config, name, entry);
|
|
364
466
|
if (Object.keys(fieldLevel).length) typeResolvers[gqlType] = fieldLevel;
|
|
365
|
-
for (const
|
|
366
|
-
|
|
367
|
-
Object.assign(typeResolvers, blockResolvers(name, key, field));
|
|
368
|
-
}
|
|
467
|
+
for (const { field, schemaPath } of blocksFieldPaths(entryFieldsFor(entry)))
|
|
468
|
+
Object.assign(typeResolvers, blockResolvers(name, schemaPath, field));
|
|
369
469
|
if (entry.kind === "page") {
|
|
370
470
|
queryResolvers[name] = async (_, args) => {
|
|
371
471
|
const locale = resolveLocaleArg(args.locale);
|
|
@@ -401,18 +501,32 @@ export function buildCmsSchema() {
|
|
|
401
501
|
const rows = await useDb().select().from(table).where(where.length ? and(...where) : void 0).orderBy(...sortOrder(entry, table, args.sort)).limit(limit).offset(offset);
|
|
402
502
|
return rows.map((row) => localizeRow(entry, row, locale));
|
|
403
503
|
};
|
|
404
|
-
queryResolvers[`${name}ById`] = async (_, args) => {
|
|
504
|
+
queryResolvers[`${name}ById`] = async (_, args, ctx) => {
|
|
405
505
|
const locale = resolveLocaleArg(args.locale);
|
|
406
506
|
const table = tableFor(name);
|
|
407
|
-
const [row] = await useDb().select().from(table).where(
|
|
507
|
+
const [row] = await useDb().select().from(table).where(
|
|
508
|
+
and(
|
|
509
|
+
eq(tableColumns(table).id, args.id),
|
|
510
|
+
...singleItemFilter(entry, table, ctx, name)
|
|
511
|
+
)
|
|
512
|
+
).limit(1);
|
|
408
513
|
return row ? localizeRow(entry, row, locale) : null;
|
|
409
514
|
};
|
|
410
515
|
if (entry.kind === "content") {
|
|
411
|
-
queryResolvers[`${name}BySlug`] = async (_, args) => {
|
|
516
|
+
queryResolvers[`${name}BySlug`] = async (_, args, ctx) => {
|
|
412
517
|
const locale = resolveLocaleArg(args.locale);
|
|
413
518
|
const table = tableFor(name);
|
|
414
519
|
const slug = tableColumns(table)[CONTENT_SLUG_FIELD];
|
|
415
|
-
const [row] = await useDb().select().from(table).where(and(eq(slug, args.slug), ...
|
|
520
|
+
const [row] = await useDb().select().from(table).where(and(eq(slug, args.slug), ...singleItemFilter(entry, table, ctx, name))).limit(1);
|
|
521
|
+
return row ? localizeRow(entry, row, locale) : null;
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
if (isRoutedContent(entry)) {
|
|
525
|
+
queryResolvers[`${name}ByPath`] = async (_, args, ctx) => {
|
|
526
|
+
const locale = resolveLocaleArg(args.locale);
|
|
527
|
+
const row = await findContentByPath(name, args.path, locale, {
|
|
528
|
+
previewId: previewItemId(ctx, name)
|
|
529
|
+
});
|
|
416
530
|
return row ? localizeRow(entry, row, locale) : null;
|
|
417
531
|
};
|
|
418
532
|
}
|
|
@@ -436,11 +550,12 @@ export function buildCmsSchema() {
|
|
|
436
550
|
typeDefs: renderGraphqlSdl(config),
|
|
437
551
|
resolvers: {
|
|
438
552
|
JSON: jsonScalar,
|
|
553
|
+
CmsLink: linkResolvers(),
|
|
439
554
|
Query: queryResolvers,
|
|
440
555
|
...typeResolvers
|
|
441
556
|
}
|
|
442
557
|
});
|
|
443
558
|
}
|
|
444
|
-
export function createGraphqlContext() {
|
|
445
|
-
return { loaders: /* @__PURE__ */ new Map() };
|
|
559
|
+
export function createGraphqlContext(preview = null) {
|
|
560
|
+
return { loaders: /* @__PURE__ */ new Map(), preview };
|
|
446
561
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { CmsEntryChange, CmsHookName, CmsHookUser, CmsMediaChange, CmsRuntimeHooks } from '../../shared/hooks.js';
|
|
3
|
+
import type { CmsSessionUser } from '../../shared/users.js';
|
|
4
|
+
type HookCallback = (...args: unknown[]) => unknown;
|
|
5
|
+
export interface CmsHookHost {
|
|
6
|
+
callHookWith: (caller: (hooks: HookCallback[], args: unknown[]) => Promise<void>, name: string, ...args: unknown[]) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type CmsHookLogger = (message: string, error: unknown) => void;
|
|
9
|
+
export declare function hookUser(user: CmsSessionUser): CmsHookUser;
|
|
10
|
+
export declare function runHooksIsolated(name: string, hooks: HookCallback[], args: unknown[], log?: CmsHookLogger): Promise<void>;
|
|
11
|
+
export declare function dispatchCmsHook<Name extends CmsHookName>(host: () => CmsHookHost, event: H3Event, name: Name, payload: Parameters<CmsRuntimeHooks[Name]>[0], log?: CmsHookLogger): Promise<void>;
|
|
12
|
+
export declare function emitEntryChange(event: H3Event, user: CmsSessionUser, change: Omit<CmsEntryChange, 'user' | 'at' | 'path' | 'status' | 'item'> & Partial<Pick<CmsEntryChange, 'path' | 'status' | 'item'>>): Promise<void>;
|
|
13
|
+
export declare function emitMediaChange(event: H3Event, user: CmsSessionUser, change: Pick<CmsMediaChange, 'action' | 'keys'> & Partial<Pick<CmsMediaChange, 'folder' | 'from'>>): Promise<void>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useNitroApp } from "nitropack/runtime";
|
|
2
|
+
import { CMS_ENTRY_CHANGE_HOOK, CMS_MEDIA_CHANGE_HOOK } from "../../shared/hooks.js";
|
|
3
|
+
const logHookError = (message, error) => console.error(message, error);
|
|
4
|
+
export function hookUser(user) {
|
|
5
|
+
return {
|
|
6
|
+
id: user.id ?? null,
|
|
7
|
+
email: user.email,
|
|
8
|
+
name: user.name ?? null,
|
|
9
|
+
role: user.role ?? (user.id ? "admin" : "superadmin")
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function runHooksIsolated(name, hooks, args, log = logHookError) {
|
|
13
|
+
return Promise.all(
|
|
14
|
+
hooks.map(
|
|
15
|
+
(hook) => Promise.resolve().then(() => hook(...args)).catch((error) => {
|
|
16
|
+
log(`[nuxt-cms] a '${name}' hook failed:`, error);
|
|
17
|
+
})
|
|
18
|
+
)
|
|
19
|
+
).then(() => void 0);
|
|
20
|
+
}
|
|
21
|
+
export function dispatchCmsHook(host, event, name, payload, log = logHookError) {
|
|
22
|
+
const context = { event };
|
|
23
|
+
const task = Promise.resolve().then(
|
|
24
|
+
() => host().callHookWith(
|
|
25
|
+
(hooks, args) => runHooksIsolated(name, hooks, args, log),
|
|
26
|
+
name,
|
|
27
|
+
payload,
|
|
28
|
+
context
|
|
29
|
+
)
|
|
30
|
+
).catch((error) => {
|
|
31
|
+
log(`[nuxt-cms] the '${name}' hooks failed:`, error);
|
|
32
|
+
});
|
|
33
|
+
const waitUntil = event.waitUntil;
|
|
34
|
+
if (typeof waitUntil === "function") {
|
|
35
|
+
try {
|
|
36
|
+
waitUntil.call(event, task);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
log(`[nuxt-cms] waitUntil failed for '${name}':`, error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return task;
|
|
42
|
+
}
|
|
43
|
+
function cloneItem(item) {
|
|
44
|
+
try {
|
|
45
|
+
return structuredClone(item);
|
|
46
|
+
} catch {
|
|
47
|
+
return { ...item };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function hookHost() {
|
|
51
|
+
return useNitroApp().hooks;
|
|
52
|
+
}
|
|
53
|
+
export function emitEntryChange(event, user, change) {
|
|
54
|
+
return dispatchCmsHook(hookHost, event, CMS_ENTRY_CHANGE_HOOK, {
|
|
55
|
+
path: null,
|
|
56
|
+
status: null,
|
|
57
|
+
...change,
|
|
58
|
+
item: change.item ? cloneItem(change.item) : null,
|
|
59
|
+
user: hookUser(user),
|
|
60
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function emitMediaChange(event, user, change) {
|
|
64
|
+
return dispatchCmsHook(hookHost, event, CMS_MEDIA_CHANGE_HOOK, {
|
|
65
|
+
folder: null,
|
|
66
|
+
from: null,
|
|
67
|
+
...change,
|
|
68
|
+
user: hookUser(user),
|
|
69
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
2
2
|
import type { CmsDb } from '#cms-db';
|
|
3
|
-
import type { CmsEntry, CmsI18n } from '../../shared/index.js';
|
|
3
|
+
import type { CmsConfig, CmsEntry, CmsI18n } from '../../shared/index.js';
|
|
4
4
|
import type { CmsImportAction, CmsImportIssue, CmsImportMode, CmsImportPreview, CmsImportSourceRow, CmsTransferFormat } from '../../shared/transfer.js';
|
|
5
5
|
import type { MediaLookup } from './media-references.js';
|
|
6
|
+
import type { CmsRedirectPlan } from '../../shared/index.js';
|
|
6
7
|
type Row = Record<string, unknown>;
|
|
7
8
|
export interface PreparedImportRow {
|
|
8
9
|
index: number;
|
|
@@ -18,8 +19,10 @@ export interface ImportContext {
|
|
|
18
19
|
table: SQLiteTable;
|
|
19
20
|
mode: CmsImportMode;
|
|
20
21
|
media: MediaLookup | null;
|
|
22
|
+
canPublish?: boolean;
|
|
21
23
|
}
|
|
22
|
-
export declare function
|
|
24
|
+
export declare function existingImportIds(db: CmsDb, table: SQLiteTable, rows: CmsImportSourceRow[]): Promise<Set<string>>;
|
|
25
|
+
export declare function validateImportRows(entry: CmsEntry, rows: CmsImportSourceRow[], i18n: CmsI18n, isCreate?: (id: string | null) => boolean, config?: CmsConfig): PreparedImportRow[];
|
|
23
26
|
export declare function checkImportRows(rows: PreparedImportRow[], context: ImportContext): Promise<PreparedImportRow[]>;
|
|
24
27
|
export declare function importPreview(entry: CmsEntry, rows: PreparedImportRow[], options: {
|
|
25
28
|
format: CmsTransferFormat;
|
|
@@ -27,5 +30,7 @@ export declare function importPreview(entry: CmsEntry, rows: PreparedImportRow[]
|
|
|
27
30
|
defaultLocale: string;
|
|
28
31
|
errors?: string[];
|
|
29
32
|
}): CmsImportPreview;
|
|
33
|
+
export declare function assignImportIds(entry: CmsEntry, rows: PreparedImportRow[]): PreparedImportRow[];
|
|
30
34
|
export declare function importWriteStatements(db: CmsDb, name: string, entry: CmsEntry, table: SQLiteTable, rows: PreparedImportRow[]): PromiseLike<unknown>[];
|
|
35
|
+
export declare function planImportRedirects(db: CmsDb, entry: CmsEntry, table: SQLiteTable, rows: PreparedImportRow[]): Promise<CmsRedirectPlan | null>;
|
|
31
36
|
export {};
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import { eq, inArray } from "drizzle-orm";
|
|
1
|
+
import { eq, inArray, sql } from "drizzle-orm";
|
|
2
|
+
import {
|
|
3
|
+
isRoutedContent,
|
|
4
|
+
mergeContentMoves,
|
|
5
|
+
relationTarget,
|
|
6
|
+
rowAfterUpdate
|
|
7
|
+
} from "../../shared/index.js";
|
|
2
8
|
import { uniqueFieldKeys } from "../../shared/duplicate.js";
|
|
9
|
+
import {
|
|
10
|
+
changedImmutableKeys,
|
|
11
|
+
immutableFieldKeys,
|
|
12
|
+
isCaseInsensitiveUnique,
|
|
13
|
+
uniqueCompareValue,
|
|
14
|
+
withEntryDefaults
|
|
15
|
+
} from "../../shared/field-options.js";
|
|
3
16
|
import { IMPORT_ID_PATTERN, importIssuePath, importRowTitle } from "../../shared/transfer.js";
|
|
4
|
-
import { buildEntrySchema } from "../../shared/validation.js";
|
|
17
|
+
import { buildEntrySchema, validationMessages } from "../../shared/validation.js";
|
|
5
18
|
import { customId } from "./custom-id.js";
|
|
6
19
|
import { mediaReferences } from "./media-references.js";
|
|
7
20
|
import {
|
|
@@ -11,11 +24,18 @@ import {
|
|
|
11
24
|
withTimestamps,
|
|
12
25
|
withUpdatedAt
|
|
13
26
|
} from "./registry.js";
|
|
27
|
+
import { contentMovesOf, planContentRedirects } from "./redirects.js";
|
|
14
28
|
import { manyToManyStatements, splitRelationValues } from "./relations.js";
|
|
15
29
|
import { chunks, KEY_CHUNK } from "./write-statements.js";
|
|
16
30
|
const META_KEYS = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt"]);
|
|
17
|
-
export function
|
|
18
|
-
const
|
|
31
|
+
export async function existingImportIds(db, table, rows) {
|
|
32
|
+
const ids = rows.flatMap(
|
|
33
|
+
(row) => typeof row.values.id === "string" && IMPORT_ID_PATTERN.test(row.values.id) ? [row.values.id] : []
|
|
34
|
+
);
|
|
35
|
+
return ids.length ? existingIds(db, table, ids) : /* @__PURE__ */ new Set();
|
|
36
|
+
}
|
|
37
|
+
export function validateImportRows(entry, rows, i18n, isCreate = () => true, config) {
|
|
38
|
+
const schema = buildEntrySchema({ ...entry, config }, i18n);
|
|
19
39
|
return rows.map((row) => {
|
|
20
40
|
const errors = [...row.errors];
|
|
21
41
|
const rawId = row.values.id;
|
|
@@ -28,9 +48,10 @@ export function validateImportRows(entry, rows, i18n) {
|
|
|
28
48
|
message: "Invalid id: use letters, numbers, _ and -, up to 128 characters"
|
|
29
49
|
});
|
|
30
50
|
}
|
|
31
|
-
const
|
|
51
|
+
const given = Object.fromEntries(
|
|
32
52
|
Object.entries(row.values).filter(([key]) => !META_KEYS.has(key))
|
|
33
53
|
);
|
|
54
|
+
const input = isCreate(id) ? withEntryDefaults(entry.fields, given, i18n) : given;
|
|
34
55
|
const result = schema.safeParse(input);
|
|
35
56
|
if (!result.success) {
|
|
36
57
|
const reported = new Set(errors.map((issue) => issue.path));
|
|
@@ -82,16 +103,17 @@ async function checkIds(rows, context) {
|
|
|
82
103
|
async function checkRelations(rows, context) {
|
|
83
104
|
const fileIds = new Set(rows.flatMap((row) => row.id ? [row.id] : []));
|
|
84
105
|
for (const [key, field] of Object.entries(context.entry.fields)) {
|
|
85
|
-
|
|
86
|
-
|
|
106
|
+
const to = relationTarget(field);
|
|
107
|
+
if (field.type !== "relation" || !to) continue;
|
|
108
|
+
const target = resolveTable(to);
|
|
87
109
|
if (!target) continue;
|
|
88
110
|
const wanted = rows.flatMap((row) => relationIds(row.values?.[key]));
|
|
89
111
|
if (!wanted.length) continue;
|
|
90
112
|
const found = await existingIds(context.db, target, wanted);
|
|
91
|
-
if (
|
|
113
|
+
if (to === context.name) for (const id of fileIds) found.add(id);
|
|
92
114
|
for (const row of rows) {
|
|
93
115
|
const missing = relationIds(row.values?.[key]).filter((id) => !found.has(id));
|
|
94
|
-
if (missing.length) addError(row, key, `Unknown ${
|
|
116
|
+
if (missing.length) addError(row, key, `Unknown ${to} id: ${missing.join(", ")}`);
|
|
95
117
|
}
|
|
96
118
|
}
|
|
97
119
|
}
|
|
@@ -99,25 +121,45 @@ async function checkUnique(rows, context) {
|
|
|
99
121
|
const columns = context.table;
|
|
100
122
|
for (const key of uniqueFieldKeys(context.entry)) {
|
|
101
123
|
const column = columns[key];
|
|
124
|
+
const field = context.entry.fields[key];
|
|
102
125
|
if (!column) continue;
|
|
103
126
|
const owners = /* @__PURE__ */ new Map();
|
|
104
127
|
for (const row of rows) {
|
|
105
128
|
const value = row.values?.[key];
|
|
106
|
-
if (typeof value
|
|
107
|
-
const
|
|
129
|
+
if (!(typeof value === "string" && value || typeof value === "number")) continue;
|
|
130
|
+
const compared = uniqueCompareValue(field, value);
|
|
131
|
+
const owner = owners.get(compared);
|
|
108
132
|
if (owner) addError(row, key, `Same value as row ${owner.index}`);
|
|
109
|
-
else owners.set(
|
|
133
|
+
else owners.set(compared, row);
|
|
110
134
|
}
|
|
135
|
+
const lower = isCaseInsensitiveUnique(field);
|
|
111
136
|
for (const chunk of chunks([...owners.keys()], KEY_CHUNK)) {
|
|
112
|
-
const found = await context.db.select({ id: columns.id, value: column }).from(context.table).where(inArray(column, chunk));
|
|
137
|
+
const found = await context.db.select({ id: columns.id, value: column }).from(context.table).where(lower ? inArray(sql`lower(${column})`, chunk) : inArray(column, chunk));
|
|
113
138
|
for (const match of found) {
|
|
114
|
-
const row = owners.get(
|
|
139
|
+
const row = owners.get(uniqueCompareValue(field, match.value));
|
|
115
140
|
if (row && row.id !== String(match.id))
|
|
116
141
|
addError(row, key, "This value is already used by another entry");
|
|
117
142
|
}
|
|
118
143
|
}
|
|
119
144
|
}
|
|
120
145
|
}
|
|
146
|
+
async function checkImmutable(rows, context) {
|
|
147
|
+
const columns = context.table;
|
|
148
|
+
const keys = immutableFieldKeys(context.entry.fields).filter((key) => columns[key]);
|
|
149
|
+
const updated = rows.filter((row) => row.action === "update" && row.id && row.values);
|
|
150
|
+
if (!keys.length || !updated.length) return;
|
|
151
|
+
const saved = /* @__PURE__ */ new Map();
|
|
152
|
+
const selection = Object.fromEntries(keys.map((key) => [key, columns[key]]));
|
|
153
|
+
for (const chunk of chunks([...new Set(updated.map((row) => row.id))], KEY_CHUNK)) {
|
|
154
|
+
const found = await context.db.select({ ...selection, id: columns.id }).from(context.table).where(inArray(columns.id, chunk));
|
|
155
|
+
for (const match of found) saved.set(String(match.id), match);
|
|
156
|
+
}
|
|
157
|
+
const message = validationMessages().immutable;
|
|
158
|
+
for (const row of updated) {
|
|
159
|
+
for (const key of changedImmutableKeys(context.entry.fields, saved.get(row.id), row.values))
|
|
160
|
+
addError(row, key, message);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
121
163
|
async function checkMedia(rows, context) {
|
|
122
164
|
if (!context.media) return;
|
|
123
165
|
const references = new Map(
|
|
@@ -133,11 +175,37 @@ async function checkMedia(rows, context) {
|
|
|
133
175
|
}
|
|
134
176
|
}
|
|
135
177
|
}
|
|
178
|
+
async function publishedIds(db, table, ids) {
|
|
179
|
+
const columns = table;
|
|
180
|
+
const found = /* @__PURE__ */ new Set();
|
|
181
|
+
for (const chunk of chunks([...new Set(ids)], KEY_CHUNK)) {
|
|
182
|
+
const rows = await db.select({ id: columns.id, status: columns.status }).from(table).where(inArray(columns.id, chunk));
|
|
183
|
+
for (const row of rows) if (row.status === "published") found.add(String(row.id));
|
|
184
|
+
}
|
|
185
|
+
return found;
|
|
186
|
+
}
|
|
187
|
+
async function checkPublish(rows, context) {
|
|
188
|
+
if (context.canPublish !== false || !context.entry.drafts) return;
|
|
189
|
+
const permission = `${context.name}.publish`;
|
|
190
|
+
for (const row of rows) {
|
|
191
|
+
if (row.values?.status === "published")
|
|
192
|
+
addError(row, "status", `Publishing needs the ${permission} permission`);
|
|
193
|
+
}
|
|
194
|
+
const updated = rows.flatMap((row) => row.action === "update" && row.id ? [row.id] : []);
|
|
195
|
+
if (!updated.length) return;
|
|
196
|
+
const published = await publishedIds(context.db, context.table, updated);
|
|
197
|
+
for (const row of rows) {
|
|
198
|
+
if (row.action === "update" && row.id && published.has(row.id))
|
|
199
|
+
addError(row, "id", `Editing a published item needs the ${permission} permission`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
136
202
|
export async function checkImportRows(rows, context) {
|
|
137
203
|
await checkIds(rows, context);
|
|
138
204
|
await checkRelations(rows, context);
|
|
139
205
|
await checkUnique(rows, context);
|
|
206
|
+
await checkImmutable(rows, context);
|
|
140
207
|
await checkMedia(rows, context);
|
|
208
|
+
await checkPublish(rows, context);
|
|
141
209
|
return rows;
|
|
142
210
|
}
|
|
143
211
|
export function importPreview(entry, rows, options) {
|
|
@@ -165,6 +233,9 @@ function deferredSelfRelations(entry, name) {
|
|
|
165
233
|
([, field]) => field.type === "relation" && field.to === name && field.cardinality !== "many-to-many" && !field.required
|
|
166
234
|
).map(([key]) => key);
|
|
167
235
|
}
|
|
236
|
+
export function assignImportIds(entry, rows) {
|
|
237
|
+
return rows.map((row) => row.values && !row.id ? { ...row, id: customId(entry.id) } : row);
|
|
238
|
+
}
|
|
168
239
|
export function importWriteStatements(db, name, entry, table, rows) {
|
|
169
240
|
const idCol = table.id;
|
|
170
241
|
const created = new Set(
|
|
@@ -200,3 +271,26 @@ export function importWriteStatements(db, name, entry, table, rows) {
|
|
|
200
271
|
}
|
|
201
272
|
return [...main, ...later];
|
|
202
273
|
}
|
|
274
|
+
export async function planImportRedirects(db, entry, table, rows) {
|
|
275
|
+
if (!isRoutedContent(entry)) return null;
|
|
276
|
+
const updates = rows.filter((row) => row.action === "update" && row.values && row.id);
|
|
277
|
+
if (!updates.length) return null;
|
|
278
|
+
const idCol = table.id;
|
|
279
|
+
const before = /* @__PURE__ */ new Map();
|
|
280
|
+
for (const ids of chunks(
|
|
281
|
+
updates.map((row) => row.id),
|
|
282
|
+
KEY_CHUNK
|
|
283
|
+
)) {
|
|
284
|
+
const found = await db.select().from(table).where(inArray(idCol, ids));
|
|
285
|
+
for (const row of found) before.set(String(row.id), row);
|
|
286
|
+
}
|
|
287
|
+
const moves = [];
|
|
288
|
+
for (const row of updates) {
|
|
289
|
+
const existing = before.get(row.id);
|
|
290
|
+
if (!existing) continue;
|
|
291
|
+
const { values } = splitRelationValues(entry, encodeColumnValues(entry, row.values));
|
|
292
|
+
const after = rowAfterUpdate(existing, withContentDefaults(entry, values));
|
|
293
|
+
moves.push(contentMovesOf(entry, existing, after));
|
|
294
|
+
}
|
|
295
|
+
return planContentRedirects(mergeContentMoves(moves));
|
|
296
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MediaUsage } from '../../shared/index.js';
|
|
2
|
+
import type { MediaUsageSource } from './media-references.js';
|
|
3
|
+
export declare function collectLinkUsage(sources: MediaUsageSource[], keys: string[]): Record<string, MediaUsage[]>;
|
|
4
|
+
export declare function loadLinkUsage(keys: string[], include?: (name: string) => boolean): Promise<Record<string, MediaUsage[]>>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { linkReferences, linkTargetKey } from "../../shared/index.js";
|
|
2
|
+
import { usageFields, usageTitle } from "./media-references.js";
|
|
3
|
+
import { usageSources } from "./media-usage.js";
|
|
4
|
+
export function collectLinkUsage(sources, keys) {
|
|
5
|
+
const usage = Object.fromEntries(keys.map((key) => [key, []]));
|
|
6
|
+
for (const { name, entry, rows } of sources) {
|
|
7
|
+
for (const row of rows) {
|
|
8
|
+
const id = entry.kind === "single" ? null : String(row.id);
|
|
9
|
+
for (const reference of linkReferences(usageFields(entry, row), row)) {
|
|
10
|
+
if (reference.entry === name && reference.id === id) continue;
|
|
11
|
+
const list = usage[linkTargetKey(reference.entry, reference.id)];
|
|
12
|
+
if (!list) continue;
|
|
13
|
+
list.push({
|
|
14
|
+
collection: name,
|
|
15
|
+
label: entry.label,
|
|
16
|
+
kind: entry.kind,
|
|
17
|
+
id,
|
|
18
|
+
title: usageTitle(entry, row),
|
|
19
|
+
field: reference.field
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return usage;
|
|
25
|
+
}
|
|
26
|
+
export async function loadLinkUsage(keys, include) {
|
|
27
|
+
return collectLinkUsage(await usageSources(include), keys);
|
|
28
|
+
}
|