includio-cms 0.1.3 → 0.5.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/CHANGELOG.md +76 -0
- package/ROADMAP.md +23 -13
- package/dist/admin/api/accept-invite.js +1 -5
- package/dist/admin/api/invite.js +7 -16
- package/dist/admin/client/account/account-page.svelte +20 -50
- package/dist/admin/client/account/lang.d.ts +15 -23
- package/dist/admin/client/account/lang.js +51 -67
- package/dist/admin/client/account/preferences-section.svelte +26 -84
- package/dist/admin/client/account/profile-section.svelte +60 -40
- package/dist/admin/client/account/schema.d.ts +11 -3
- package/dist/admin/client/account/schema.js +25 -16
- package/dist/admin/client/account/security-section.svelte +139 -105
- package/dist/admin/client/account/sessions-section.svelte +35 -34
- package/dist/admin/client/admin/admin-after-login-layout-content.svelte +3 -5
- package/dist/admin/client/admin/admin-layout.svelte +3 -2
- package/dist/admin/client/admin/admin-preloader.svelte +36 -0
- package/dist/admin/client/admin/admin-preloader.svelte.d.ts +18 -0
- package/dist/admin/client/admin/dashboard-page.svelte +55 -41
- package/dist/admin/client/collection/a11y-score-cell.svelte +45 -0
- package/dist/admin/client/collection/a11y-score-cell.svelte.d.ts +6 -0
- package/dist/admin/client/collection/bulk-actions-bar.svelte +83 -0
- package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +9 -0
- package/dist/admin/client/collection/collection-entries.svelte +255 -256
- package/dist/admin/client/collection/collection-view.svelte.d.ts +4 -3
- package/dist/admin/client/collection/collection-view.svelte.js +9 -5
- package/dist/admin/client/collection/collection.svelte +22 -12
- package/dist/admin/client/collection/data-table.svelte +50 -39
- package/dist/admin/client/collection/data-table.svelte.d.ts +1 -0
- package/dist/admin/client/collection/date-cell.svelte +7 -5
- package/dist/admin/client/collection/date-cell.svelte.d.ts +1 -1
- package/dist/admin/client/collection/empty-state.svelte +28 -0
- package/dist/admin/client/collection/empty-state.svelte.d.ts +9 -0
- package/dist/admin/client/collection/entry-link.svelte +10 -4
- package/dist/admin/client/collection/entry-link.svelte.d.ts +1 -0
- package/dist/admin/client/collection/grid-view.svelte +21 -23
- package/dist/admin/client/collection/grid-view.svelte.d.ts +1 -2
- package/dist/admin/client/collection/row-actions.svelte +60 -0
- package/dist/admin/client/collection/row-actions.svelte.d.ts +9 -0
- package/dist/admin/client/collection/status-badge.svelte +7 -8
- package/dist/admin/client/collection/table-pagination.svelte +122 -79
- package/dist/admin/client/collection/table-pagination.svelte.d.ts +1 -0
- package/dist/admin/client/collection/table-toolbar.svelte +108 -88
- package/dist/admin/client/collection/table-toolbar.svelte.d.ts +8 -9
- package/dist/admin/client/entry/entry-form.svelte +109 -1
- package/dist/admin/client/entry/entry-header.svelte +96 -37
- package/dist/admin/client/entry/entry-header.svelte.d.ts +5 -0
- package/dist/admin/client/entry/entry.svelte +171 -60
- package/dist/admin/client/entry/header/a11y-validator.d.ts +46 -0
- package/dist/admin/client/entry/header/a11y-validator.js +311 -0
- package/dist/admin/client/entry/header/publish-panel.svelte +373 -131
- package/dist/admin/client/entry/header/publish-panel.svelte.d.ts +4 -0
- package/dist/admin/client/entry/header/save-indicator.svelte +33 -23
- package/dist/admin/client/entry/header/schedule-popover.svelte +1 -1
- package/dist/admin/client/entry/header/status-badge.svelte +25 -118
- package/dist/admin/client/entry/header/version-history-sheet.svelte +314 -98
- package/dist/admin/client/form/form-submission/form-submission.svelte +271 -83
- package/dist/admin/client/form/form-submission/submission-field.svelte +12 -12
- package/dist/admin/client/form/form-submissions.svelte +421 -139
- package/dist/admin/client/form/submission-link.svelte +8 -2
- package/dist/admin/client/form/submission-link.svelte.d.ts +1 -0
- package/dist/admin/client/form/submission-status-badge.svelte +18 -4
- package/dist/admin/client/form/submission-status-badge.svelte.d.ts +1 -0
- package/dist/admin/client/login/lang.d.ts +32 -0
- package/dist/admin/client/login/lang.js +66 -2
- package/dist/admin/client/login/login-form.svelte +237 -95
- package/dist/admin/client/login/login-form.svelte.d.ts +2 -17
- package/dist/admin/client/login/login-page.svelte +34 -98
- package/dist/admin/client/login/reset-password-page.svelte +235 -0
- package/dist/admin/client/login/reset-password-page.svelte.d.ts +4 -0
- package/dist/admin/client/login/schema.d.ts +15 -0
- package/dist/admin/client/login/schema.js +21 -0
- package/dist/admin/client/users/accept-invite-page.svelte +166 -37
- package/dist/admin/client/users/create-user-dialog.svelte +15 -7
- package/dist/admin/client/users/delete-user-dialog.svelte +81 -16
- package/dist/admin/client/users/delete-user-dialog.svelte.d.ts +4 -1
- package/dist/admin/client/users/edit-user-dialog.svelte +3 -0
- package/dist/admin/client/users/invite-user-dialog.svelte +16 -3
- package/dist/admin/client/users/lang.d.ts +27 -0
- package/dist/admin/client/users/lang.js +64 -10
- package/dist/admin/client/users/pending-invitations.svelte +59 -23
- package/dist/admin/client/users/users-page.svelte +471 -72
- package/dist/admin/components/accessibility/accessibility-overview.svelte +2 -7
- package/dist/admin/components/dashboard/a11y-gauge.svelte +90 -0
- package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +18 -0
- package/dist/admin/components/dashboard/accessibility-hub.svelte +13 -12
- package/dist/admin/components/dashboard/form-submissions-widget.svelte +71 -113
- package/dist/admin/components/dashboard/index.d.ts +4 -2
- package/dist/admin/components/dashboard/index.js +4 -2
- package/dist/admin/components/dashboard/recent-activity.svelte +53 -75
- package/dist/admin/components/dashboard/recent-entries.svelte +94 -0
- package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +18 -0
- package/dist/admin/components/dashboard/stat-card.svelte +2 -2
- package/dist/admin/components/dashboard/tip-of-the-day.svelte +109 -0
- package/dist/admin/components/dashboard/tip-of-the-day.svelte.d.ts +3 -0
- package/dist/admin/components/dashboard/welcome-header.svelte +45 -0
- package/dist/admin/components/dashboard/welcome-header.svelte.d.ts +3 -0
- package/dist/admin/components/fields/{array-field.svelte → blocks-field.svelte} +4 -4
- package/dist/admin/components/fields/{array-field.svelte.d.ts → blocks-field.svelte.d.ts} +5 -5
- package/dist/admin/components/fields/content-field.svelte +27 -0
- package/dist/admin/components/fields/content-field.svelte.d.ts +31 -0
- package/dist/admin/components/fields/field-renderer.svelte +9 -7
- package/dist/admin/components/fields/image-field.svelte +2 -2
- package/dist/admin/components/fields/media-field.svelte +2 -2
- package/dist/admin/components/fields/seo-field.svelte +205 -25
- package/dist/admin/components/fields/simple-array-field.svelte +289 -0
- package/dist/admin/components/fields/simple-array-field.svelte.d.ts +30 -0
- package/dist/admin/components/fields/slug-field.svelte +3 -2
- package/dist/admin/components/fields/standalone-field-renderer.svelte +148 -0
- package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +9 -0
- package/dist/admin/components/fields/text-field-wrapper.svelte +13 -1
- package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
- package/dist/admin/components/fields/url-field.svelte +5 -4
- package/dist/admin/components/layout/app-sidebar.svelte +27 -24
- package/dist/admin/components/layout/lang.d.ts +6 -0
- package/dist/admin/components/layout/lang.js +13 -1
- package/dist/admin/components/layout/layout-renderer.svelte +352 -0
- package/dist/admin/components/layout/layout-renderer.svelte.d.ts +14 -0
- package/dist/admin/components/layout/nav-breadcrumbs.svelte +4 -4
- package/dist/admin/components/layout/nav-collections.svelte +65 -36
- package/dist/admin/components/layout/nav-footer.svelte +31 -0
- package/dist/admin/components/layout/nav-footer.svelte.d.ts +18 -0
- package/dist/admin/components/layout/nav-forms.svelte +55 -30
- package/dist/admin/components/layout/nav-main.svelte +14 -52
- package/dist/admin/components/layout/nav-search.svelte +4 -3
- package/dist/admin/components/layout/nav-singletons.svelte +59 -17
- package/dist/admin/components/layout/nav-singletons.svelte.d.ts +17 -8
- package/dist/admin/components/layout/site-header.svelte +74 -13
- package/dist/admin/components/media/alt-input.svelte +32 -22
- package/dist/admin/components/media/bulk-action-bar.svelte +139 -150
- package/dist/admin/components/media/file/file-details.svelte +299 -217
- package/dist/admin/components/media/file/file-miniature.svelte +54 -41
- package/dist/admin/components/media/file/file-miniature.svelte.d.ts +1 -0
- package/dist/admin/components/media/file/file-preview.svelte +1 -1
- package/dist/admin/components/media/file-upload.svelte +24 -26
- package/dist/admin/components/media/files-list.svelte +112 -40
- package/dist/admin/components/media/files-list.svelte.d.ts +2 -0
- package/dist/admin/components/media/focal-point-input.svelte +122 -26
- package/dist/admin/components/media/media-library.svelte +127 -70
- package/dist/admin/components/media/media-search.svelte +6 -6
- package/dist/admin/components/media/media-sort.svelte +3 -1
- package/dist/admin/components/media/multi-file-summary.svelte +88 -68
- package/dist/admin/components/media/tag-combobox.svelte +141 -66
- package/dist/admin/components/media/tag-combobox.svelte.d.ts +1 -0
- package/dist/admin/components/media/tag-sidebar.svelte +139 -121
- package/dist/admin/components/tiptap/FigureNodeView.svelte +144 -15
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +254 -0
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte.d.ts +4 -0
- package/dist/admin/components/tiptap/SlashCommandPopup.svelte +212 -0
- package/dist/admin/components/tiptap/SlashCommandPopup.svelte.d.ts +8 -0
- package/dist/admin/components/tiptap/content-editor.svelte +280 -0
- package/dist/admin/components/tiptap/content-editor.svelte.d.ts +9 -0
- package/dist/admin/components/tiptap/editor-toolbar.svelte +230 -0
- package/dist/admin/components/tiptap/editor-toolbar.svelte.d.ts +16 -0
- package/dist/admin/components/tiptap/heading-a11y-plugin.d.ts +2 -0
- package/dist/admin/components/tiptap/heading-a11y-plugin.js +67 -0
- package/dist/admin/components/tiptap/image-dialog.svelte +172 -11
- package/dist/admin/components/tiptap/inline-block-node.d.ts +19 -0
- package/dist/admin/components/tiptap/inline-block-node.js +98 -0
- package/dist/admin/components/tiptap/link-dialog.svelte +9 -4
- package/dist/admin/components/tiptap/slash-command.d.ts +17 -0
- package/dist/admin/components/tiptap/slash-command.js +181 -0
- package/dist/admin/components/tiptap/structured-content-utils.d.ts +21 -0
- package/dist/admin/components/tiptap/structured-content-utils.js +150 -0
- package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -190
- package/dist/admin/email/invite-template.d.ts +8 -0
- package/dist/admin/email/invite-template.js +99 -0
- package/dist/admin/email/reset-password-template.d.ts +7 -0
- package/dist/admin/email/reset-password-template.js +96 -0
- package/dist/admin/remote/ai.remote.d.ts +1 -0
- package/dist/admin/remote/ai.remote.js +4 -1
- package/dist/admin/remote/entry.remote.d.ts +8 -0
- package/dist/admin/remote/entry.remote.js +53 -4
- package/dist/admin/remote/preview.remote.js +2 -1
- package/dist/admin/shared/password-schema.d.ts +5 -0
- package/dist/admin/shared/password-schema.js +10 -0
- package/dist/admin/styles/admin.css +1530 -151
- package/dist/admin/utils/formatDate.d.ts +1 -0
- package/dist/admin/utils/formatDate.js +8 -0
- package/dist/admin/utils/roleLabel.d.ts +2 -0
- package/dist/admin/utils/roleLabel.js +13 -0
- package/dist/ai-claude/index.d.ts +2 -0
- package/dist/ai-claude/index.js +56 -0
- package/dist/cms/runtime/api.d.ts +6 -1
- package/dist/cms/runtime/api.js +3 -0
- package/dist/cms/runtime/schemas.d.ts +9 -1
- package/dist/cms/runtime/schemas.js +8 -0
- package/dist/cms/runtime/types.d.ts +82 -10
- package/dist/cms/runtime/types.js +4 -0
- package/dist/components/ui/accordion/accordion.stories.svelte +39 -0
- package/dist/components/ui/accordion/accordion.stories.svelte.d.ts +27 -0
- package/dist/components/ui/alert/alert.stories.svelte +53 -0
- package/dist/components/ui/alert/alert.stories.svelte.d.ts +27 -0
- package/dist/components/ui/alert/alert.svelte +5 -0
- package/dist/components/ui/alert/alert.svelte.d.ts +9 -0
- package/dist/components/ui/avatar/avatar.stories.svelte +16 -0
- package/dist/components/ui/avatar/avatar.stories.svelte.d.ts +27 -0
- package/dist/components/ui/badge/badge.stories.svelte +33 -0
- package/dist/components/ui/badge/badge.stories.svelte.d.ts +27 -0
- package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +33 -0
- package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte.d.ts +27 -0
- package/dist/components/ui/button/button.stories.svelte +43 -0
- package/dist/components/ui/button/button.stories.svelte.d.ts +27 -0
- package/dist/components/ui/button/button.svelte +1 -2
- package/dist/components/ui/button/button.svelte.d.ts +0 -3
- package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
- package/dist/components/ui/card/card.stories.svelte +42 -0
- package/dist/components/ui/card/card.stories.svelte.d.ts +27 -0
- package/dist/components/ui/command/command.stories.svelte +51 -0
- package/dist/components/ui/command/command.stories.svelte.d.ts +27 -0
- package/dist/components/ui/dialog/dialog.stories.svelte +29 -0
- package/dist/components/ui/dialog/dialog.stories.svelte.d.ts +27 -0
- package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
- package/dist/components/ui/field/field.stories.svelte +21 -0
- package/dist/components/ui/field/field.stories.svelte.d.ts +27 -0
- package/dist/components/ui/input/input.stories.svelte +40 -0
- package/dist/components/ui/input/input.stories.svelte.d.ts +27 -0
- package/dist/components/ui/input/input.svelte +2 -4
- package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
- package/dist/components/ui/label/label.stories.svelte +20 -0
- package/dist/components/ui/label/label.stories.svelte.d.ts +27 -0
- package/dist/components/ui/popover/popover.stories.svelte +29 -0
- package/dist/components/ui/popover/popover.stories.svelte.d.ts +27 -0
- package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
- package/dist/components/ui/select/select.stories.svelte +23 -0
- package/dist/components/ui/select/select.stories.svelte.d.ts +27 -0
- package/dist/components/ui/separator/separator.stories.svelte +24 -0
- package/dist/components/ui/separator/separator.stories.svelte.d.ts +27 -0
- package/dist/components/ui/sheet/sheet.stories.svelte +29 -0
- package/dist/components/ui/sheet/sheet.stories.svelte.d.ts +27 -0
- package/dist/components/ui/sidebar/sidebar-group.svelte +3 -3
- package/dist/components/ui/sidebar/sidebar-group.svelte.d.ts +2 -2
- package/dist/components/ui/sidebar/sidebar-menu-button.svelte +28 -30
- package/dist/components/ui/sidebar/sidebar-menu-button.svelte.d.ts +7 -7
- package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-trigger.svelte +4 -4
- package/dist/components/ui/sonner/sonner.stories.svelte +22 -0
- package/dist/components/ui/sonner/sonner.stories.svelte.d.ts +26 -0
- package/dist/components/ui/sonner/sonner.svelte +8 -2
- package/dist/components/ui/sonner/toast-demo.svelte +29 -0
- package/dist/components/ui/sonner/toast-demo.svelte.d.ts +6 -0
- package/dist/components/ui/textarea/textarea.stories.svelte +22 -0
- package/dist/components/ui/textarea/textarea.stories.svelte.d.ts +27 -0
- package/dist/components/ui/textarea/textarea.svelte +0 -2
- package/dist/components/ui/toggle/toggle.stories.svelte +22 -0
- package/dist/components/ui/toggle/toggle.stories.svelte.d.ts +27 -0
- package/dist/components/ui/toggle-group/toggle-group.stories.svelte +17 -0
- package/dist/components/ui/toggle-group/toggle-group.stories.svelte.d.ts +27 -0
- package/dist/components/ui/tooltip/tooltip.stories.svelte +26 -0
- package/dist/components/ui/tooltip/tooltip.stories.svelte.d.ts +27 -0
- package/dist/core/fields/fieldSchemaToTs.d.ts +1 -0
- package/dist/core/fields/fieldSchemaToTs.js +133 -1
- package/dist/core/fields/layoutUtils.d.ts +17 -0
- package/dist/core/fields/layoutUtils.js +149 -0
- package/dist/core/fields/structuredToHtml.d.ts +9 -0
- package/dist/core/fields/structuredToHtml.js +161 -0
- package/dist/core/server/entries/operations/create.js +2 -1
- package/dist/core/server/entries/operations/get.js +8 -6
- package/dist/core/server/entries/operations/update.d.ts +3 -0
- package/dist/core/server/entries/operations/update.js +30 -2
- package/dist/core/server/fields/queryStructuredContent.d.ts +15 -0
- package/dist/core/server/fields/queryStructuredContent.js +65 -0
- package/dist/core/server/fields/resolveImageFields.js +51 -2
- package/dist/core/server/fields/resolveRelationFields.js +2 -2
- package/dist/core/server/fields/resolveRichtextLinks.js +80 -13
- package/dist/core/server/fields/resolveUrlFields.js +57 -6
- package/dist/core/server/fields/slugResolver.d.ts +10 -0
- package/dist/core/server/fields/slugResolver.js +34 -0
- package/dist/core/server/generator/fields.js +15 -4
- package/dist/core/server/generator/generator.js +3 -2
- package/dist/files-local/index.js +126 -64
- package/dist/paraglide/.prettierignore +3 -0
- package/dist/paraglide/messages/_index.d.ts +36 -0
- package/dist/paraglide/messages/_index.js +72 -0
- package/dist/paraglide/messages/en.d.ts +5 -0
- package/dist/paraglide/messages/en.js +14 -0
- package/dist/paraglide/messages/pl.d.ts +5 -0
- package/dist/paraglide/messages/pl.js +14 -0
- package/dist/paraglide/messages.d.ts +2 -0
- package/dist/paraglide/messages.js +4 -0
- package/dist/paraglide/registry.d.ts +21 -0
- package/dist/paraglide/registry.js +31 -0
- package/dist/paraglide/runtime.d.ts +583 -0
- package/dist/paraglide/runtime.js +1402 -0
- package/dist/paraglide/server.d.ts +67 -0
- package/dist/paraglide/server.js +175 -0
- package/dist/server/auth.d.ts +5 -0
- package/dist/server/auth.js +12 -1
- package/dist/sveltekit/components/structured-content.svelte +204 -0
- package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
- package/dist/sveltekit/config.d.ts +13 -3
- package/dist/sveltekit/index.d.ts +3 -0
- package/dist/sveltekit/index.js +3 -0
- package/dist/sveltekit/server/handle.js +1 -0
- package/dist/types/config.d.ts +3 -0
- package/dist/types/fields.d.ts +19 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/layout.d.ts +54 -0
- package/dist/types/layout.js +6 -0
- package/dist/types/structured-content.d.ts +63 -0
- package/dist/types/structured-content.js +1 -0
- package/dist/updates/0.1.4/index.d.ts +2 -0
- package/dist/updates/0.1.4/index.js +11 -0
- package/dist/updates/0.1.5/index.d.ts +2 -0
- package/dist/updates/0.1.5/index.js +18 -0
- package/dist/updates/0.2.0/index.d.ts +2 -0
- package/dist/updates/0.2.0/index.js +11 -0
- package/dist/updates/0.2.2/index.d.ts +2 -0
- package/dist/updates/0.2.2/index.js +13 -0
- package/dist/updates/0.5.0/index.d.ts +2 -0
- package/dist/updates/0.5.0/index.js +14 -0
- package/dist/updates/index.js +6 -1
- package/package.json +17 -10
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getCMS } from '../../../cms.js';
|
|
2
2
|
import { generateZodSchemaFromFields } from '../../../fields/fieldSchemaToTs.js';
|
|
3
|
+
import { getFieldsFromConfig } from '../../../fields/layoutUtils.js';
|
|
3
4
|
import z from 'zod';
|
|
4
|
-
import { getDbEntryOrThrow, getDbEntryVersionOrThrow } from './get.js';
|
|
5
|
+
import { getDbEntryOrThrow, getDbEntryVersionOrThrow, getDbEntryVersions } from './get.js';
|
|
6
|
+
import { createEntryVersion } from './create.js';
|
|
5
7
|
export const updateEntrySchema = z.object({
|
|
6
8
|
availableLocales: z.array(z.string()).optional(),
|
|
7
9
|
archivedAt: z.date().nullable().optional(),
|
|
@@ -57,7 +59,7 @@ export const updateEntryVersion = async (id, data) => {
|
|
|
57
59
|
const entry = await getDbEntryOrThrow({ id: version.entryId });
|
|
58
60
|
const config = getCMS().getBySlug(entry.slug);
|
|
59
61
|
const languages = getCMS().languages;
|
|
60
|
-
const schema = generateZodSchemaFromFields(config
|
|
62
|
+
const schema = generateZodSchemaFromFields(getFieldsFromConfig(config), languages);
|
|
61
63
|
const parsedData = schema.safeParse(filteredData.data);
|
|
62
64
|
if (!parsedData.success) {
|
|
63
65
|
throw Error('Invalid data: ' + parsedData.error.flatten());
|
|
@@ -102,6 +104,32 @@ export const pruneOldDraftVersions = async (entryId) => {
|
|
|
102
104
|
await Promise.all(toDelete.map((v) => getCMS().databaseAdapter.deleteEntryVersion({ id: v.id })));
|
|
103
105
|
}
|
|
104
106
|
};
|
|
107
|
+
export const upsertDraftVersion = async (entryId, data, options) => {
|
|
108
|
+
const entry = await getDbEntryOrThrow({ id: entryId });
|
|
109
|
+
const versions = await getDbEntryVersions({ entryIds: [entryId] });
|
|
110
|
+
// Sort desc by versionNumber, find latest non-published draft
|
|
111
|
+
const sorted = versions.sort((a, b) => b.versionNumber - a.versionNumber);
|
|
112
|
+
const latestDraft = sorted.find((v) => v.id !== entry.publishedVersionId);
|
|
113
|
+
if (latestDraft) {
|
|
114
|
+
// Compare data — if identical, skip
|
|
115
|
+
const existingData = JSON.stringify(latestDraft.data);
|
|
116
|
+
const newData = JSON.stringify(data);
|
|
117
|
+
if (existingData === newData) {
|
|
118
|
+
return latestDraft;
|
|
119
|
+
}
|
|
120
|
+
// Different data — update in place
|
|
121
|
+
const updated = await getCMS().databaseAdapter.updateEntryVersion({
|
|
122
|
+
id: latestDraft.id,
|
|
123
|
+
data: {
|
|
124
|
+
data: data,
|
|
125
|
+
createdAt: new Date()
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return updated;
|
|
129
|
+
}
|
|
130
|
+
// No draft exists — create new version
|
|
131
|
+
return createEntryVersion({ entryId, data }, options);
|
|
132
|
+
};
|
|
105
133
|
export const unpublishEntry = async (entryId) => {
|
|
106
134
|
await updateEntry(entryId, {
|
|
107
135
|
publishedVersionId: null,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SCNode, StructuredContentDoc, SCInlineBlockAttrs } from '../../../types/structured-content.js';
|
|
2
|
+
export interface MediaRef {
|
|
3
|
+
type: 'image' | 'video';
|
|
4
|
+
src: string;
|
|
5
|
+
alt?: string;
|
|
6
|
+
mediaId?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Extract block-level nodes, optionally filtered by type. */
|
|
9
|
+
export declare function extractBlocks(doc: StructuredContentDoc, type?: string): SCNode[];
|
|
10
|
+
/** Extract inline block nodes, optionally filtered by blockType. */
|
|
11
|
+
export declare function extractInlineBlocks(doc: StructuredContentDoc, blockType?: string): SCInlineBlockAttrs[];
|
|
12
|
+
/** Extract all plain text from doc (useful for excerpts, search indexing). */
|
|
13
|
+
export declare function extractText(doc: StructuredContentDoc): string;
|
|
14
|
+
/** Extract media references from figure/video/image nodes. */
|
|
15
|
+
export declare function extractMediaRefs(doc: StructuredContentDoc): MediaRef[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { walkNodes } from '../../../admin/components/tiptap/structured-content-utils.js';
|
|
2
|
+
/** Extract block-level nodes, optionally filtered by type. */
|
|
3
|
+
export function extractBlocks(doc, type) {
|
|
4
|
+
const result = [];
|
|
5
|
+
walkNodes(doc.content, (node) => {
|
|
6
|
+
if (type ? node.type === type : !['doc', 'text'].includes(node.type)) {
|
|
7
|
+
result.push(node);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
/** Extract inline block nodes, optionally filtered by blockType. */
|
|
13
|
+
export function extractInlineBlocks(doc, blockType) {
|
|
14
|
+
const result = [];
|
|
15
|
+
walkNodes(doc.content, (node) => {
|
|
16
|
+
if (node.type !== 'inlineBlock' || !node.attrs)
|
|
17
|
+
return;
|
|
18
|
+
const attrs = node.attrs;
|
|
19
|
+
if (blockType ? attrs.blockType === blockType : true) {
|
|
20
|
+
result.push(attrs);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/** Extract all plain text from doc (useful for excerpts, search indexing). */
|
|
26
|
+
export function extractText(doc) {
|
|
27
|
+
const parts = [];
|
|
28
|
+
walkNodes(doc.content, (node) => {
|
|
29
|
+
if (node.type === 'text' && node.text) {
|
|
30
|
+
parts.push(node.text);
|
|
31
|
+
}
|
|
32
|
+
else if (node.type === 'hardBreak') {
|
|
33
|
+
parts.push('\n');
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return parts.join('');
|
|
37
|
+
}
|
|
38
|
+
/** Extract media references from figure/video/image nodes. */
|
|
39
|
+
export function extractMediaRefs(doc) {
|
|
40
|
+
const refs = [];
|
|
41
|
+
walkNodes(doc.content, (node) => {
|
|
42
|
+
if (node.type === 'figure' || node.type === 'image') {
|
|
43
|
+
const attrs = node.attrs;
|
|
44
|
+
if (attrs?.src) {
|
|
45
|
+
refs.push({
|
|
46
|
+
type: 'image',
|
|
47
|
+
src: attrs.src,
|
|
48
|
+
alt: attrs.alt ?? undefined,
|
|
49
|
+
mediaId: attrs['data-media-id'] ?? undefined
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (node.type === 'video') {
|
|
54
|
+
const attrs = node.attrs;
|
|
55
|
+
if (attrs?.src) {
|
|
56
|
+
refs.push({
|
|
57
|
+
type: 'video',
|
|
58
|
+
src: attrs.src,
|
|
59
|
+
mediaId: attrs['data-media-id'] ?? undefined
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return refs;
|
|
65
|
+
}
|
|
@@ -3,6 +3,7 @@ const PUBLIC_URL = env.PUBLIC_URL ?? '';
|
|
|
3
3
|
import { getCMS } from '../../cms.js';
|
|
4
4
|
import z from 'zod';
|
|
5
5
|
import { getImageStyles } from './utils/imageStyles.js';
|
|
6
|
+
import { extractMediaIds as extractMediaIdsFromDoc, walkMediaNodes, cloneDoc } from '../../../admin/components/tiptap/structured-content-utils.js';
|
|
6
7
|
export async function resolveMediaFields(data, fields) {
|
|
7
8
|
const mediaIds = [];
|
|
8
9
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -39,7 +40,7 @@ export async function resolveMediaFields(data, fields) {
|
|
|
39
40
|
case 'object':
|
|
40
41
|
collectIds(val.data, field.fields);
|
|
41
42
|
break;
|
|
42
|
-
case '
|
|
43
|
+
case 'blocks':
|
|
43
44
|
if (Array.isArray(val)) {
|
|
44
45
|
val.forEach((item) => {
|
|
45
46
|
// Find the object definition for this item based on its slug
|
|
@@ -50,6 +51,17 @@ export async function resolveMediaFields(data, fields) {
|
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
break;
|
|
54
|
+
case 'content': {
|
|
55
|
+
// Content field is localized: { lang: StructuredContentDoc }
|
|
56
|
+
if (typeof val === 'object' && val !== null) {
|
|
57
|
+
for (const doc of Object.values(val)) {
|
|
58
|
+
if (doc && typeof doc === 'object' && doc.type === 'doc') {
|
|
59
|
+
mediaIds.push(...extractMediaIdsFromDoc(doc));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
};
|
|
@@ -178,7 +190,7 @@ export async function resolveMediaFields(data, fields) {
|
|
|
178
190
|
data: await resolveValues(val.data, field.fields)
|
|
179
191
|
};
|
|
180
192
|
break;
|
|
181
|
-
case '
|
|
193
|
+
case 'blocks':
|
|
182
194
|
result[field.slug] = await Promise.all(
|
|
183
195
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
184
196
|
val.map(async (item) => {
|
|
@@ -194,6 +206,43 @@ export async function resolveMediaFields(data, fields) {
|
|
|
194
206
|
return item;
|
|
195
207
|
}));
|
|
196
208
|
break;
|
|
209
|
+
case 'content': {
|
|
210
|
+
// Content field is localized: { lang: StructuredContentDoc }
|
|
211
|
+
if (typeof val === 'object' && val !== null) {
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
213
|
+
const resolved = {};
|
|
214
|
+
for (const [lang, doc] of Object.entries(val)) {
|
|
215
|
+
if (doc && typeof doc === 'object' && doc.type === 'doc') {
|
|
216
|
+
const cloned = cloneDoc(doc);
|
|
217
|
+
walkMediaNodes(cloned, (node) => {
|
|
218
|
+
const mediaId = node.attrs?.['data-media-id'];
|
|
219
|
+
if (!mediaId)
|
|
220
|
+
return;
|
|
221
|
+
const mediaFile = mediaMap[mediaId];
|
|
222
|
+
if (!mediaFile)
|
|
223
|
+
return;
|
|
224
|
+
node.attrs.src = mediaFile.url;
|
|
225
|
+
if (mediaFile.width)
|
|
226
|
+
node.attrs.width = mediaFile.width;
|
|
227
|
+
if (mediaFile.height)
|
|
228
|
+
node.attrs.height = mediaFile.height;
|
|
229
|
+
if (mediaFile.alt && !node.attrs.alt)
|
|
230
|
+
node.attrs.alt = mediaFile.alt;
|
|
231
|
+
node.attrs._media = {
|
|
232
|
+
data: mediaFile,
|
|
233
|
+
...(mediaFile.type === 'image' ? { blurDataUrl: mediaFile.blurDataUrl } : {})
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
resolved[lang] = cloned;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
resolved[lang] = doc;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
result[field.slug] = resolved;
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
197
246
|
default:
|
|
198
247
|
result[field.slug] = val;
|
|
199
248
|
}
|
|
@@ -26,7 +26,7 @@ export async function resolveRelationFields(data, fields, language) {
|
|
|
26
26
|
case 'object':
|
|
27
27
|
collectIds(val.data, field.fields);
|
|
28
28
|
break;
|
|
29
|
-
case '
|
|
29
|
+
case 'blocks':
|
|
30
30
|
if (Array.isArray(val)) {
|
|
31
31
|
val.forEach((item) => {
|
|
32
32
|
// Find the object definition for this item based on its slug
|
|
@@ -78,7 +78,7 @@ export async function resolveRelationFields(data, fields, language) {
|
|
|
78
78
|
data: resolveValues(val.data, field.fields)
|
|
79
79
|
};
|
|
80
80
|
break;
|
|
81
|
-
case '
|
|
81
|
+
case 'blocks':
|
|
82
82
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
83
|
result[field.slug] = val.map((item) => {
|
|
84
84
|
// Find the object definition for this item based on its slug
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getCMS } from '../../cms.js';
|
|
2
|
+
import { extractEntryIds as extractEntryIdsFromDoc, extractMediaIds as extractMediaIdsFromDoc, cloneDoc, walkLinkMarks, walkInlineBlockNodes } from '../../../admin/components/tiptap/structured-content-utils.js';
|
|
3
|
+
import { getEntrySlugPath, getSlugFromEntryData } from './slugResolver.js';
|
|
2
4
|
const ENTRY_ID_RE = /data-entry-id="([0-9a-f-]{36})"/g;
|
|
3
5
|
function extractEntryIds(html) {
|
|
4
6
|
const ids = [];
|
|
@@ -7,15 +9,47 @@ function extractEntryIds(html) {
|
|
|
7
9
|
}
|
|
8
10
|
return ids;
|
|
9
11
|
}
|
|
10
|
-
function resolveHtml(html,
|
|
12
|
+
function resolveHtml(html, slugMap) {
|
|
11
13
|
return html.replace(/<a\s([^>]*data-entry-id="([0-9a-f-]{36})"[^>]*)>/g, (match, _attrs, id) => {
|
|
12
|
-
const
|
|
13
|
-
const slug = entryData?.seo?.slug;
|
|
14
|
+
const slug = slugMap[id];
|
|
14
15
|
if (!slug)
|
|
15
16
|
return match;
|
|
16
17
|
return match.replace(/href="[^"]*"/, `href="${slug}"`);
|
|
17
18
|
});
|
|
18
19
|
}
|
|
20
|
+
/** Extract entry IDs from inlineBlock blockData relation fields. */
|
|
21
|
+
function extractEntryIdsFromInlineBlocks(doc) {
|
|
22
|
+
const ids = [];
|
|
23
|
+
walkInlineBlockNodes(doc, (node) => {
|
|
24
|
+
const data = node.attrs?.blockData;
|
|
25
|
+
if (!data || typeof data !== 'object')
|
|
26
|
+
return;
|
|
27
|
+
for (const val of Object.values(data)) {
|
|
28
|
+
if (typeof val === 'string' && val.length === 36)
|
|
29
|
+
ids.push(val);
|
|
30
|
+
if (Array.isArray(val)) {
|
|
31
|
+
for (const item of val) {
|
|
32
|
+
if (typeof item === 'string' && item.length === 36)
|
|
33
|
+
ids.push(item);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return ids;
|
|
39
|
+
}
|
|
40
|
+
function resolveContentDoc(doc, slugMap) {
|
|
41
|
+
const cloned = cloneDoc(doc);
|
|
42
|
+
walkLinkMarks(cloned, (mark) => {
|
|
43
|
+
const entryId = mark.attrs?.['data-entry-id'];
|
|
44
|
+
if (!entryId)
|
|
45
|
+
return;
|
|
46
|
+
const slug = slugMap[entryId];
|
|
47
|
+
if (slug && mark.attrs) {
|
|
48
|
+
mark.attrs.href = slug;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return cloned;
|
|
52
|
+
}
|
|
19
53
|
export async function resolveRichtextLinks(data, fields, language) {
|
|
20
54
|
const entriesIds = [];
|
|
21
55
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -39,10 +73,22 @@ export async function resolveRichtextLinks(data, fields, language) {
|
|
|
39
73
|
}
|
|
40
74
|
break;
|
|
41
75
|
}
|
|
76
|
+
case 'content': {
|
|
77
|
+
// Content field is localized: { lang: StructuredContentDoc, ... }
|
|
78
|
+
if (typeof val === 'object' && val !== null) {
|
|
79
|
+
for (const doc of Object.values(val)) {
|
|
80
|
+
if (doc && typeof doc === 'object' && doc.type === 'doc') {
|
|
81
|
+
entriesIds.push(...extractEntryIdsFromDoc(doc));
|
|
82
|
+
entriesIds.push(...extractEntryIdsFromInlineBlocks(doc));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
42
88
|
case 'object':
|
|
43
89
|
collectIds(val.data, field.fields);
|
|
44
90
|
break;
|
|
45
|
-
case '
|
|
91
|
+
case 'blocks':
|
|
46
92
|
if (Array.isArray(val)) {
|
|
47
93
|
val.forEach((item) => {
|
|
48
94
|
const objectDef = field.of.find((objDef) => objDef.slug === item.slug);
|
|
@@ -71,15 +117,20 @@ export async function resolveRichtextLinks(data, fields, language) {
|
|
|
71
117
|
const versions = await db.getEntryVersions({
|
|
72
118
|
ids: publishedVersionIds
|
|
73
119
|
});
|
|
74
|
-
// Build map: entryId →
|
|
75
|
-
const
|
|
120
|
+
// Build map: entryId → collectionSlug for config lookup
|
|
121
|
+
const entryConfigSlugMap = {};
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
entryConfigSlugMap[entry.id] = entry.slug;
|
|
124
|
+
}
|
|
125
|
+
// Build map: entryId → resolved slug string
|
|
126
|
+
const slugMap = {};
|
|
76
127
|
for (const version of versions) {
|
|
77
128
|
const rawData = version.data;
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const slug =
|
|
129
|
+
const configSlug = entryConfigSlugMap[version.entryId];
|
|
130
|
+
const slugPath = configSlug ? getEntrySlugPath(configSlug) : 'seo.slug';
|
|
131
|
+
const slug = getSlugFromEntryData(rawData, slugPath, language);
|
|
81
132
|
if (slug) {
|
|
82
|
-
|
|
133
|
+
slugMap[version.entryId] = slug;
|
|
83
134
|
}
|
|
84
135
|
}
|
|
85
136
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -99,12 +150,28 @@ export async function resolveRichtextLinks(data, fields, language) {
|
|
|
99
150
|
const resolved = {};
|
|
100
151
|
for (const [lang, html] of Object.entries(val)) {
|
|
101
152
|
resolved[lang] =
|
|
102
|
-
typeof html === 'string' ? resolveHtml(html,
|
|
153
|
+
typeof html === 'string' ? resolveHtml(html, slugMap) : html;
|
|
103
154
|
}
|
|
104
155
|
result[field.slug] = resolved;
|
|
105
156
|
}
|
|
106
157
|
else if (typeof val === 'string') {
|
|
107
|
-
result[field.slug] = resolveHtml(val,
|
|
158
|
+
result[field.slug] = resolveHtml(val, slugMap);
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case 'content': {
|
|
163
|
+
if (typeof val === 'object' && val !== null) {
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
165
|
+
const resolved = {};
|
|
166
|
+
for (const [lang, doc] of Object.entries(val)) {
|
|
167
|
+
if (doc && typeof doc === 'object' && doc.type === 'doc') {
|
|
168
|
+
resolved[lang] = resolveContentDoc(doc, slugMap);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
resolved[lang] = doc;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
result[field.slug] = resolved;
|
|
108
175
|
}
|
|
109
176
|
break;
|
|
110
177
|
}
|
|
@@ -114,7 +181,7 @@ export async function resolveRichtextLinks(data, fields, language) {
|
|
|
114
181
|
data: resolveValues(val.data, field.fields)
|
|
115
182
|
};
|
|
116
183
|
break;
|
|
117
|
-
case '
|
|
184
|
+
case 'blocks':
|
|
118
185
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
186
|
result[field.slug] = val.map((item) => {
|
|
120
187
|
const objectDef = field.of.find((objDef) => objDef.slug === item.slug);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { urlFieldDataSchema, urlFieldDataWithRelationSchema } from '../../../schemas/field/url.js';
|
|
2
2
|
import { getEntries } from '../entries/operations/get.js';
|
|
3
|
+
import { getEntrySlugPath, getSlugFromEntryData } from './slugResolver.js';
|
|
3
4
|
export async function resolveUrlFields(data, fields, language) {
|
|
4
5
|
const entriesIds = [];
|
|
5
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -26,7 +27,7 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
26
27
|
case 'object':
|
|
27
28
|
collectIds(val.data, field.fields);
|
|
28
29
|
break;
|
|
29
|
-
case '
|
|
30
|
+
case 'blocks':
|
|
30
31
|
if (Array.isArray(val)) {
|
|
31
32
|
val.forEach((item) => {
|
|
32
33
|
// Find the object definition for this item based on its slug
|
|
@@ -37,14 +38,29 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
break;
|
|
41
|
+
case 'array':
|
|
42
|
+
if (field.of === 'url' && Array.isArray(val)) {
|
|
43
|
+
for (const item of val) {
|
|
44
|
+
const parsedVal = urlFieldDataWithRelationSchema.safeParse(item);
|
|
45
|
+
if (parsedVal.success) {
|
|
46
|
+
entriesIds.push(parsedVal.data.id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
};
|
|
43
54
|
collectIds(data, fields);
|
|
44
|
-
|
|
55
|
+
const slugMap = {};
|
|
45
56
|
if (entriesIds.length > 0) {
|
|
46
57
|
const entries = await getEntries({ ids: entriesIds, language });
|
|
47
|
-
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const slugPath = getEntrySlugPath(entry.slug);
|
|
60
|
+
const slug = getSlugFromEntryData(entry.data, slugPath);
|
|
61
|
+
if (slug)
|
|
62
|
+
slugMap[entry.id] = slug;
|
|
63
|
+
}
|
|
48
64
|
}
|
|
49
65
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
66
|
const resolveValues = (value, fields) => {
|
|
@@ -83,8 +99,7 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
83
99
|
extras.newTab = parsed.newTab;
|
|
84
100
|
}
|
|
85
101
|
if (parsedValWithRelation.success) {
|
|
86
|
-
const
|
|
87
|
-
const slug = entryData?.seo?.slug;
|
|
102
|
+
const slug = slugMap[parsedValWithRelation.data.id];
|
|
88
103
|
if (slug) {
|
|
89
104
|
result[field.slug] = { url: slug, ...extras };
|
|
90
105
|
break;
|
|
@@ -102,7 +117,7 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
102
117
|
data: resolveValues(val.data, field.fields)
|
|
103
118
|
};
|
|
104
119
|
break;
|
|
105
|
-
case '
|
|
120
|
+
case 'blocks':
|
|
106
121
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
122
|
result[field.slug] = val.map((item) => {
|
|
108
123
|
// Find the object definition for this item based on its slug
|
|
@@ -117,6 +132,42 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
117
132
|
return item;
|
|
118
133
|
});
|
|
119
134
|
break;
|
|
135
|
+
case 'array':
|
|
136
|
+
if (field.of === 'url' && Array.isArray(val)) {
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
138
|
+
result[field.slug] = val.map((item) => {
|
|
139
|
+
const parsedValWithRelation = urlFieldDataWithRelationSchema.safeParse(item);
|
|
140
|
+
const parsedVal = urlFieldDataSchema.safeParse(item);
|
|
141
|
+
const parsed = parsedValWithRelation.success
|
|
142
|
+
? parsedValWithRelation.data
|
|
143
|
+
: parsedVal.success
|
|
144
|
+
? parsedVal.data
|
|
145
|
+
: null;
|
|
146
|
+
if (!parsed)
|
|
147
|
+
return item;
|
|
148
|
+
const extras = {};
|
|
149
|
+
if (parsed.text) {
|
|
150
|
+
extras.text = language ? parsed.text[language] : parsed.text;
|
|
151
|
+
}
|
|
152
|
+
if (parsed.newTab !== undefined) {
|
|
153
|
+
extras.newTab = parsed.newTab;
|
|
154
|
+
}
|
|
155
|
+
if (parsedValWithRelation.success) {
|
|
156
|
+
const slug = slugMap[parsedValWithRelation.data.id];
|
|
157
|
+
if (slug) {
|
|
158
|
+
return { url: slug, ...extras };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const resolvedUrl = language
|
|
162
|
+
? parsed.url[language]
|
|
163
|
+
: parsed.url;
|
|
164
|
+
return { url: resolvedUrl, ...extras };
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
result[field.slug] = val;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
120
171
|
default:
|
|
121
172
|
result[field.slug] = val;
|
|
122
173
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the dot-path to the slug field for a given collection/single slug.
|
|
3
|
+
* Falls back to 'seo.slug' if no slugField is configured.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getEntrySlugPath(configSlug: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Traverse a dot-path in entry data and return the slug value.
|
|
8
|
+
* Handles both plain strings and localized objects { lang: string }.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getSlugFromEntryData(data: Record<string, unknown>, slugPath: string, language?: string): string | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getCMS } from '../../cms.js';
|
|
2
|
+
const DEFAULT_SLUG_PATH = 'seo.slug';
|
|
3
|
+
/**
|
|
4
|
+
* Get the dot-path to the slug field for a given collection/single slug.
|
|
5
|
+
* Falls back to 'seo.slug' if no slugField is configured.
|
|
6
|
+
*/
|
|
7
|
+
export function getEntrySlugPath(configSlug) {
|
|
8
|
+
try {
|
|
9
|
+
const config = getCMS().getBySlug(configSlug);
|
|
10
|
+
return config.slugField || DEFAULT_SLUG_PATH;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return DEFAULT_SLUG_PATH;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Traverse a dot-path in entry data and return the slug value.
|
|
18
|
+
* Handles both plain strings and localized objects { lang: string }.
|
|
19
|
+
*/
|
|
20
|
+
export function getSlugFromEntryData(data, slugPath, language) {
|
|
21
|
+
const parts = slugPath.split('.');
|
|
22
|
+
let current = data;
|
|
23
|
+
for (const part of parts) {
|
|
24
|
+
if (current == null || typeof current !== 'object')
|
|
25
|
+
return undefined;
|
|
26
|
+
current = current[part];
|
|
27
|
+
}
|
|
28
|
+
if (typeof current === 'string')
|
|
29
|
+
return current;
|
|
30
|
+
if (language && current != null && typeof current === 'object') {
|
|
31
|
+
return current[language];
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
@@ -6,6 +6,8 @@ function getFieldTypeAsString(field) {
|
|
|
6
6
|
case 'date':
|
|
7
7
|
case 'datetime':
|
|
8
8
|
return 'string';
|
|
9
|
+
case 'content':
|
|
10
|
+
return 'StructuredContentDoc';
|
|
9
11
|
case 'radio':
|
|
10
12
|
return field.options.map((opt) => `'${opt.value.trim()}'`).join(' | ');
|
|
11
13
|
case 'image': {
|
|
@@ -39,11 +41,11 @@ function getFieldTypeAsString(field) {
|
|
|
39
41
|
slug: '${field.slug}';
|
|
40
42
|
data: {${field.fields.map((f) => ` ${f.slug}${f.required ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
|
|
41
43
|
}`;
|
|
42
|
-
case '
|
|
44
|
+
case 'blocks':
|
|
43
45
|
return `
|
|
44
46
|
(
|
|
45
47
|
${field.of
|
|
46
|
-
.map((f) => `
|
|
48
|
+
.map((f) => `
|
|
47
49
|
{
|
|
48
50
|
slug: '${f.slug}';
|
|
49
51
|
data: {${f.fields.map((f) => ` ${f.slug}${f.required ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
|
|
@@ -51,13 +53,22 @@ function getFieldTypeAsString(field) {
|
|
|
51
53
|
`)
|
|
52
54
|
.join(' | ')}
|
|
53
55
|
)[]`;
|
|
56
|
+
case 'array':
|
|
57
|
+
switch (field.of) {
|
|
58
|
+
case 'text':
|
|
59
|
+
return field.localized ? 'Record<string, string>[]' : 'string[]';
|
|
60
|
+
case 'number':
|
|
61
|
+
return 'number[]';
|
|
62
|
+
case 'url':
|
|
63
|
+
return 'UrlFieldData[]';
|
|
64
|
+
}
|
|
54
65
|
case 'slug':
|
|
55
66
|
return 'string';
|
|
56
67
|
case 'seo':
|
|
57
68
|
return `{
|
|
58
|
-
slug
|
|
69
|
+
slug: string;
|
|
59
70
|
canonicalUrl?: string;
|
|
60
|
-
title
|
|
71
|
+
title: string;
|
|
61
72
|
description?: string;
|
|
62
73
|
ogImage?: string;
|
|
63
74
|
keywords?: string;
|
|
@@ -4,6 +4,7 @@ import { generateTsTypeFromFields } from './fields.js';
|
|
|
4
4
|
import { generateTsTypeFromFormFields } from './formFields.js';
|
|
5
5
|
import { generateZodSchemaStringFromFormFieldsAsString } from './formFieldSchemaToString.js';
|
|
6
6
|
import { toPascalCase } from './utils.js';
|
|
7
|
+
import { getFieldsFromConfig } from '../../fields/layoutUtils.js';
|
|
7
8
|
function createCmsRuntimeDir() {
|
|
8
9
|
const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
|
|
9
10
|
mkdirSync(cmsDir, { recursive: true });
|
|
@@ -19,7 +20,7 @@ function generateTypesStringForRecords(type, records) {
|
|
|
19
20
|
export interface ${toPascalCase(single.slug)} {
|
|
20
21
|
id: string;
|
|
21
22
|
slug: string;
|
|
22
|
-
data: ${generateTsTypeFromFields(single
|
|
23
|
+
data: ${generateTsTypeFromFields(getFieldsFromConfig(single))}
|
|
23
24
|
publishedAt: Date | null;
|
|
24
25
|
};
|
|
25
26
|
`;
|
|
@@ -68,7 +69,7 @@ function generateTypes(config) {
|
|
|
68
69
|
const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
|
|
69
70
|
const filePath = join(cmsDir, 'types.ts');
|
|
70
71
|
let code = `// This file is auto-generated. Do not edit directly.\n\n`;
|
|
71
|
-
code += `import type { MediaFile, ImageFieldData, VideoFieldData } from 'includio-cms/types';\n\n`;
|
|
72
|
+
code += `import type { MediaFile, ImageFieldData, VideoFieldData, StructuredContentDoc } from 'includio-cms/types';\n\n`;
|
|
72
73
|
if (config.singles && config.singles.length > 0) {
|
|
73
74
|
code += generateTypesStringForRecords('single', Object.values(config.singles));
|
|
74
75
|
}
|