includio-cms 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/ROADMAP.md +8 -0
- package/dist/admin/client/admin/admin-layout.svelte +5 -1
- package/dist/admin/client/admin/admin-layout.svelte.d.ts +2 -0
- package/dist/admin/client/index.d.ts +1 -0
- package/dist/admin/client/index.js +1 -0
- package/dist/admin/components/fields/field-renderer.svelte +9 -0
- package/dist/admin/helpers/build-custom-fields-map.d.ts +7 -0
- package/dist/admin/helpers/build-custom-fields-map.js +13 -0
- package/dist/admin/helpers/index.d.ts +7 -0
- package/dist/admin/helpers/index.js +7 -0
- package/dist/admin/helpers/use-field.d.ts +26 -0
- package/dist/admin/helpers/use-field.js +9 -0
- package/dist/admin/state/custom-fields.svelte.d.ts +3 -0
- package/dist/admin/state/custom-fields.svelte.js +10 -0
- package/dist/admin/ui/index.d.ts +12 -0
- package/dist/admin/ui/index.js +14 -0
- package/dist/components/ui/accordion/accordion.svelte.d.ts +1 -1
- package/dist/components/ui/calendar/calendar.svelte.d.ts +1 -1
- package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
- package/dist/components/ui/command/command-input.svelte.d.ts +1 -1
- package/dist/components/ui/command/command.svelte.d.ts +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +2 -2
- package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +1 -1
- package/dist/components/ui/radio-group/radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +2 -2
- package/dist/components/ui/tabs/tabs.svelte.d.ts +1 -1
- package/dist/components/ui/textarea/textarea.svelte.d.ts +1 -1
- package/dist/components/ui/toggle-group/toggle-group-item.svelte.d.ts +1 -1
- package/dist/components/ui/toggle-group/toggle-group.svelte.d.ts +1 -1
- package/dist/core/cms.d.ts +2 -1
- package/dist/core/cms.js +11 -0
- package/dist/core/fields/fieldSchemaToTs.d.ts +11 -0
- package/dist/core/fields/fieldSchemaToTs.js +30 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/server/fields/populateEntry.js +50 -0
- package/dist/core/server/fields/resolveRichtextLinks.js +3 -2
- package/dist/core/server/fields/resolveUrlFields.js +2 -2
- package/dist/core/server/fields/slugResolver.d.ts +5 -0
- package/dist/core/server/fields/slugResolver.js +16 -0
- package/dist/core/server/fields/utils/resolveMedia.d.ts +12 -0
- package/dist/core/server/fields/utils/resolveMedia.js +23 -0
- package/dist/core/server/generator/fields.d.ts +2 -0
- package/dist/core/server/generator/fields.js +8 -0
- package/dist/core/server/generator/generator.js +9 -1
- package/dist/sveltekit/index.d.ts +1 -0
- package/dist/sveltekit/index.js +1 -0
- package/dist/types/cms.d.ts +2 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/fields.d.ts +9 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/plugins.d.ts +19 -1
- package/dist/updates/0.7.1/index.d.ts +2 -0
- package/dist/updates/0.7.1/index.js +16 -0
- package/dist/updates/index.js +2 -1
- package/package.json +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to includio-cms are documented here.
|
|
4
4
|
Generated from `src/lib/updates/` — do not edit manually.
|
|
5
5
|
|
|
6
|
+
## 0.7.1 — 2026-03-13
|
|
7
|
+
|
|
8
|
+
Custom fields plugin system, pathTemplate, admin public API
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Custom fields plugin system — type definition, zod/TS codegen, field renderer, populate resolver
|
|
12
|
+
- pathTemplate support — configurable URL paths for collections/singles
|
|
13
|
+
- Admin public exports: admin/helpers (useField, contexts) & admin/ui (shadcn primitives, MediaSelector)
|
|
14
|
+
- resolveMediaWithStyles utility — resolve media files with image styles
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Schema serialization type cast fix (stricter TS)
|
|
18
|
+
- Core svelte export condition added to package.json
|
|
19
|
+
|
|
6
20
|
## 0.7.0 — 2026-03-11
|
|
7
21
|
|
|
8
22
|
Auth core refactor & design system alignment
|
package/ROADMAP.md
CHANGED
|
@@ -155,6 +155,14 @@
|
|
|
155
155
|
- [x] `[fix]` `[P2]` Blocks field accordion: rounded borders, card background
|
|
156
156
|
- [x] `[fix]` `[P2]` Media file miniature: reset img/svg margins
|
|
157
157
|
|
|
158
|
+
## 0.7.1 — Custom fields & admin DX
|
|
159
|
+
|
|
160
|
+
- [x] `[feature]` `[P0]` Custom fields plugin system — type, zod/TS codegen, field renderer, populate resolver
|
|
161
|
+
- [x] `[feature]` `[P1]` pathTemplate — configurable URL paths for collections/singles
|
|
162
|
+
- [x] `[feature]` `[P1]` Admin public exports: admin/helpers & admin/ui for plugin authors
|
|
163
|
+
- [x] `[feature]` `[P2]` resolveMediaWithStyles utility & core svelte export
|
|
164
|
+
- [x] `[fix]` `[P1]` Schema serialization type cast (stricter TS compatibility)
|
|
165
|
+
|
|
158
166
|
## 0.8.0 — SEO module
|
|
159
167
|
|
|
160
168
|
- [ ] `[feature]` `[P1]` SERP preview + character limits for title/description <!-- files: src/lib/admin/components/fields/seo-field.svelte -->
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
import '../../styles/admin.css';
|
|
4
4
|
import { Toaster } from '../../../components/ui/sonner/index.js';
|
|
5
5
|
import { MediaSort, setMediaSort } from '../../state/media-sort.svelte.js';
|
|
6
|
+
import { setCustomFields } from '../../state/custom-fields.svelte.js';
|
|
7
|
+
import type { CustomFieldDefinition } from '../../../types/plugins.js';
|
|
6
8
|
import AdminPreloader from './admin-preloader.svelte';
|
|
7
9
|
import { onMount } from 'svelte';
|
|
8
10
|
|
|
9
11
|
setMediaSort(new MediaSort());
|
|
10
12
|
|
|
11
|
-
let { children }: { children: Snippet } = $props();
|
|
13
|
+
let { children, customFields }: { children: Snippet; customFields?: Map<string, CustomFieldDefinition> } = $props();
|
|
14
|
+
|
|
15
|
+
setCustomFields(customFields ?? new Map());
|
|
12
16
|
|
|
13
17
|
let mounted = $state(false);
|
|
14
18
|
onMount(() => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import '../../styles/admin.css';
|
|
3
|
+
import type { CustomFieldDefinition } from '../../../types/plugins.js';
|
|
3
4
|
type $$ComponentProps = {
|
|
4
5
|
children: Snippet;
|
|
6
|
+
customFields?: Map<string, CustomFieldDefinition>;
|
|
5
7
|
};
|
|
6
8
|
declare const AdminLayout: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
9
|
type AdminLayout = ReturnType<typeof AdminLayout>;
|
|
@@ -10,3 +10,4 @@ export { default as FormSubmissionPage } from './form/form-submission/form-submi
|
|
|
10
10
|
export { default as UsersPage } from './users/users-page.svelte';
|
|
11
11
|
export { default as AcceptInvitePage } from './users/accept-invite-page.svelte';
|
|
12
12
|
export { default as MaintenancePage } from './maintenance/maintenance-page.svelte';
|
|
13
|
+
export { default as MediaSelector } from '../components/media/media-selector.svelte';
|
|
@@ -10,3 +10,4 @@ export { default as FormSubmissionPage } from './form/form-submission/form-submi
|
|
|
10
10
|
export { default as UsersPage } from './users/users-page.svelte';
|
|
11
11
|
export { default as AcceptInvitePage } from './users/accept-invite-page.svelte';
|
|
12
12
|
export { default as MaintenancePage } from './maintenance/maintenance-page.svelte';
|
|
13
|
+
export { default as MediaSelector } from '../components/media/media-selector.svelte';
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import LazyField from './lazy-field.svelte';
|
|
17
17
|
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
18
18
|
import { getLocalizedLabel } from '../../utils/collectionLabel.js';
|
|
19
|
+
import { getCustomFields } from '../../state/custom-fields.svelte.js';
|
|
19
20
|
|
|
20
21
|
type Props = {
|
|
21
22
|
objectFieldType?: 'default' | 'inline';
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
let { field, form, path, objectFieldType = 'default', focusedPath = null, flashingPath = null, depth = 0, distributed = false, ...props }: Props = $props();
|
|
32
33
|
|
|
33
34
|
const interfaceLanguage = useInterfaceLanguage();
|
|
35
|
+
const customFieldDefs = getCustomFields();
|
|
34
36
|
|
|
35
37
|
// Bridge: SuperForm store → $bindable for leaf components
|
|
36
38
|
// Cast to any: runtime types enforced by child components via $bindable
|
|
@@ -159,6 +161,13 @@
|
|
|
159
161
|
<DateTimeField {field} bind:value={$value} />
|
|
160
162
|
{:else if field.type === 'select'}
|
|
161
163
|
<SelectField {field} bind:value={$value} />
|
|
164
|
+
{:else if field.type === 'custom'}
|
|
165
|
+
{@const customDef = customFieldDefs.get(field.fieldType)}
|
|
166
|
+
{#if customDef}
|
|
167
|
+
<LazyField loader={customDef.component} props={{ field, form, path }} skeletonClass="h-20" />
|
|
168
|
+
{:else}
|
|
169
|
+
<p>Nieznany custom field: {field.fieldType}</p>
|
|
170
|
+
{/if}
|
|
162
171
|
{:else}
|
|
163
172
|
<p>Nieobsługiwany typ pola: {field.type}</p>
|
|
164
173
|
{/if}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PluginConfig } from '../../types/plugins.js';
|
|
2
|
+
import type { CustomFieldDefinition } from '../../types/plugins.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build a Map<fieldType, CustomFieldDefinition> from plugin configs.
|
|
5
|
+
* Use in consumer's admin layout to pass customFields to AdminLayout.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildCustomFieldsMap(...plugins: PluginConfig[]): Map<string, CustomFieldDefinition>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a Map<fieldType, CustomFieldDefinition> from plugin configs.
|
|
3
|
+
* Use in consumer's admin layout to pass customFields to AdminLayout.
|
|
4
|
+
*/
|
|
5
|
+
export function buildCustomFieldsMap(...plugins) {
|
|
6
|
+
const map = new Map();
|
|
7
|
+
for (const p of plugins) {
|
|
8
|
+
for (const f of p.fields ?? []) {
|
|
9
|
+
map.set(f.fieldType, f);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return map;
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { useField } from './use-field.js';
|
|
2
|
+
export { buildCustomFieldsMap } from './build-custom-fields-map.js';
|
|
3
|
+
export { useInterfaceLanguage } from '../state/interface-language.svelte.js';
|
|
4
|
+
export { getContentLanguage } from '../state/content-language.svelte.js';
|
|
5
|
+
export { getRemotes } from '../context/remotes.js';
|
|
6
|
+
export { getCustomFields } from '../state/custom-fields.svelte.js';
|
|
7
|
+
export { getLocalizedLabel } from '../utils/collectionLabel.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { useField } from './use-field.js';
|
|
2
|
+
export { buildCustomFieldsMap } from './build-custom-fields-map.js';
|
|
3
|
+
export { useInterfaceLanguage } from '../state/interface-language.svelte.js';
|
|
4
|
+
export { getContentLanguage } from '../state/content-language.svelte.js';
|
|
5
|
+
export { getRemotes } from '../context/remotes.js';
|
|
6
|
+
export { getCustomFields } from '../state/custom-fields.svelte.js';
|
|
7
|
+
export { getLocalizedLabel } from '../utils/collectionLabel.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type SuperForm, type FormPathLeaves } from 'sveltekit-superforms';
|
|
2
|
+
/**
|
|
3
|
+
* Simplified wrapper around sveltekit-superforms' formFieldProxy.
|
|
4
|
+
* Plugin authors don't need to know the superforms API.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useField(form: SuperForm<Record<string, unknown>>, path: FormPathLeaves<Record<string, unknown>>): {
|
|
7
|
+
value: {
|
|
8
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<unknown>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
9
|
+
set(this: void, value: unknown, options?: {
|
|
10
|
+
taint?: import("sveltekit-superforms").TaintOption;
|
|
11
|
+
}): void;
|
|
12
|
+
update(this: void, updater: import("svelte/store").Updater<unknown>, options?: {
|
|
13
|
+
taint?: import("sveltekit-superforms").TaintOption;
|
|
14
|
+
}): void;
|
|
15
|
+
};
|
|
16
|
+
errors: import("svelte/store").Writable<string[] | undefined>;
|
|
17
|
+
constraints: import("svelte/store").Writable<Partial<{
|
|
18
|
+
pattern: string;
|
|
19
|
+
min: number | string;
|
|
20
|
+
max: number | string;
|
|
21
|
+
required: boolean;
|
|
22
|
+
step: number | "any";
|
|
23
|
+
minlength: number;
|
|
24
|
+
maxlength: number;
|
|
25
|
+
}> | undefined>;
|
|
26
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { formFieldProxy } from 'sveltekit-superforms';
|
|
2
|
+
/**
|
|
3
|
+
* Simplified wrapper around sveltekit-superforms' formFieldProxy.
|
|
4
|
+
* Plugin authors don't need to know the superforms API.
|
|
5
|
+
*/
|
|
6
|
+
export function useField(form, path) {
|
|
7
|
+
const { value, errors, constraints } = formFieldProxy(form, path);
|
|
8
|
+
return { value, errors, constraints };
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { setSchemaCustomFields } from '../../core/fields/fieldSchemaToTs.js';
|
|
3
|
+
const contextKey = Symbol('customFields');
|
|
4
|
+
export function setCustomFields(defs) {
|
|
5
|
+
setContext(contextKey, defs);
|
|
6
|
+
setSchemaCustomFields(defs);
|
|
7
|
+
}
|
|
8
|
+
export function getCustomFields() {
|
|
9
|
+
return getContext(contextKey) ?? new Map();
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Button, type ButtonProps } from '../../components/ui/button/index.js';
|
|
2
|
+
export * as Dialog from '../../components/ui/dialog/index.js';
|
|
3
|
+
export { Input } from '../../components/ui/input/index.js';
|
|
4
|
+
export * as Card from '../../components/ui/card/index.js';
|
|
5
|
+
export { Label } from '../../components/ui/label/index.js';
|
|
6
|
+
export { Separator } from '../../components/ui/separator/index.js';
|
|
7
|
+
export { Badge } from '../../components/ui/badge/index.js';
|
|
8
|
+
export { Skeleton } from '../../components/ui/skeleton/index.js';
|
|
9
|
+
export * as Tabs from '../../components/ui/tabs/index.js';
|
|
10
|
+
export * as Tooltip from '../../components/ui/tooltip/index.js';
|
|
11
|
+
export { default as FileMiniature } from '../components/media/file/file-miniature.svelte';
|
|
12
|
+
export { default as MediaSelector } from '../components/media/media-selector.svelte';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// shadcn-svelte UI primitives
|
|
2
|
+
export { Button } from '../../components/ui/button/index.js';
|
|
3
|
+
export * as Dialog from '../../components/ui/dialog/index.js';
|
|
4
|
+
export { Input } from '../../components/ui/input/index.js';
|
|
5
|
+
export * as Card from '../../components/ui/card/index.js';
|
|
6
|
+
export { Label } from '../../components/ui/label/index.js';
|
|
7
|
+
export { Separator } from '../../components/ui/separator/index.js';
|
|
8
|
+
export { Badge } from '../../components/ui/badge/index.js';
|
|
9
|
+
export { Skeleton } from '../../components/ui/skeleton/index.js';
|
|
10
|
+
export * as Tabs from '../../components/ui/tabs/index.js';
|
|
11
|
+
export * as Tooltip from '../../components/ui/tooltip/index.js';
|
|
12
|
+
// Admin components
|
|
13
|
+
export { default as FileMiniature } from '../components/media/file/file-miniature.svelte';
|
|
14
|
+
export { default as MediaSelector } from '../components/media/media-selector.svelte';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
2
|
-
declare const Accordion: import("svelte").Component<AccordionPrimitive.RootProps, {}, "
|
|
2
|
+
declare const Accordion: import("svelte").Component<AccordionPrimitive.RootProps, {}, "value" | "ref">;
|
|
3
3
|
type Accordion = ReturnType<typeof Accordion>;
|
|
4
4
|
export default Accordion;
|
|
@@ -16,6 +16,6 @@ type $$ComponentProps = WithoutChildrenOrChild<CalendarPrimitive.RootProps> & {
|
|
|
16
16
|
outsideMonth: boolean;
|
|
17
17
|
}]>;
|
|
18
18
|
};
|
|
19
|
-
declare const Calendar: import("svelte").Component<$$ComponentProps, {}, "
|
|
19
|
+
declare const Calendar: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "placeholder">;
|
|
20
20
|
type Calendar = ReturnType<typeof Calendar>;
|
|
21
21
|
export default Calendar;
|
|
@@ -7,6 +7,6 @@ type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.RootProps> & With
|
|
|
7
7
|
title?: string;
|
|
8
8
|
description?: string;
|
|
9
9
|
};
|
|
10
|
-
declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "
|
|
10
|
+
declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "open">;
|
|
11
11
|
type CommandDialog = ReturnType<typeof CommandDialog>;
|
|
12
12
|
export default CommandDialog;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Command as CommandPrimitive } from "bits-ui";
|
|
2
|
-
declare const CommandInput: import("svelte").Component<CommandPrimitive.InputProps, {}, "
|
|
2
|
+
declare const CommandInput: import("svelte").Component<CommandPrimitive.InputProps, {}, "value" | "ref">;
|
|
3
3
|
type CommandInput = ReturnType<typeof CommandInput>;
|
|
4
4
|
export default CommandInput;
|
|
@@ -3,6 +3,6 @@ export type CommandRootApi = CommandPrimitive.Root;
|
|
|
3
3
|
type $$ComponentProps = CommandPrimitive.RootProps & {
|
|
4
4
|
api?: CommandRootApi | null;
|
|
5
5
|
};
|
|
6
|
-
declare const Command: import("svelte").Component<$$ComponentProps, {}, "
|
|
6
|
+
declare const Command: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "api">;
|
|
7
7
|
type Command = ReturnType<typeof Command>;
|
|
8
8
|
export default Command;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
2
|
-
declare const DropdownMenuRadioGroup: import("svelte").Component<DropdownMenuPrimitive.RadioGroupProps, {}, "
|
|
2
|
+
declare const DropdownMenuRadioGroup: import("svelte").Component<DropdownMenuPrimitive.RadioGroupProps, {}, "value" | "ref">;
|
|
3
3
|
type DropdownMenuRadioGroup = ReturnType<typeof DropdownMenuRadioGroup>;
|
|
4
4
|
export default DropdownMenuRadioGroup;
|
|
@@ -8,6 +8,6 @@ type Props = WithElementRef<Omit<HTMLInputAttributes, "type"> & ({
|
|
|
8
8
|
type?: InputType;
|
|
9
9
|
files?: undefined;
|
|
10
10
|
})>;
|
|
11
|
-
declare const Input: import("svelte").Component<Props, {}, "files" | "
|
|
11
|
+
declare const Input: import("svelte").Component<Props, {}, "files" | "value" | "ref">;
|
|
12
12
|
type Input = ReturnType<typeof Input>;
|
|
13
13
|
export default Input;
|
|
@@ -2,10 +2,10 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
|
|
|
2
2
|
type: "file";
|
|
3
3
|
files?: FileList;
|
|
4
4
|
} | {
|
|
5
|
-
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "
|
|
5
|
+
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "time" | "password" | "hidden" | "reset" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
9
|
-
}, {}, "
|
|
9
|
+
}, {}, "value" | "ref">;
|
|
10
10
|
type InputGroupInput = ReturnType<typeof InputGroupInput>;
|
|
11
11
|
export default InputGroupInput;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
declare const InputGroupTextarea: import("svelte").Component<Omit<import("../../../utils.js").WithElementRef<import("svelte/elements").HTMLTextareaAttributes>, "children">, {}, "
|
|
1
|
+
declare const InputGroupTextarea: import("svelte").Component<Omit<import("../../../utils.js").WithElementRef<import("svelte/elements").HTMLTextareaAttributes>, "children">, {}, "value" | "ref">;
|
|
2
2
|
type InputGroupTextarea = ReturnType<typeof InputGroupTextarea>;
|
|
3
3
|
export default InputGroupTextarea;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
|
2
|
-
declare const RadioGroup: import("svelte").Component<RadioGroupPrimitive.RootProps, {}, "
|
|
2
|
+
declare const RadioGroup: import("svelte").Component<RadioGroupPrimitive.RootProps, {}, "value" | "ref">;
|
|
3
3
|
type RadioGroup = ReturnType<typeof RadioGroup>;
|
|
4
4
|
export default RadioGroup;
|
|
@@ -2,10 +2,10 @@ declare const SidebarInput: import("svelte").Component<(Omit<import("svelte/elem
|
|
|
2
2
|
type: "file";
|
|
3
3
|
files?: FileList;
|
|
4
4
|
} | {
|
|
5
|
-
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "
|
|
5
|
+
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "time" | "password" | "hidden" | "reset" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
9
|
-
}, {}, "
|
|
9
|
+
}, {}, "value" | "ref">;
|
|
10
10
|
type SidebarInput = ReturnType<typeof SidebarInput>;
|
|
11
11
|
export default SidebarInput;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Tabs as TabsPrimitive } from "bits-ui";
|
|
2
|
-
declare const Tabs: import("svelte").Component<TabsPrimitive.RootProps, {}, "
|
|
2
|
+
declare const Tabs: import("svelte").Component<TabsPrimitive.RootProps, {}, "value" | "ref">;
|
|
3
3
|
type Tabs = ReturnType<typeof Tabs>;
|
|
4
4
|
export default Tabs;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type WithElementRef } from "../../../utils.js";
|
|
2
2
|
import type { HTMLTextareaAttributes } from "svelte/elements";
|
|
3
|
-
declare const Textarea: import("svelte").Component<Omit<WithElementRef<HTMLTextareaAttributes>, "children">, {}, "
|
|
3
|
+
declare const Textarea: import("svelte").Component<Omit<WithElementRef<HTMLTextareaAttributes>, "children">, {}, "value" | "ref">;
|
|
4
4
|
type Textarea = ReturnType<typeof Textarea>;
|
|
5
5
|
export default Textarea;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui";
|
|
2
2
|
import { type ToggleVariants } from "../toggle/index.js";
|
|
3
3
|
type $$ComponentProps = ToggleGroupPrimitive.ItemProps & ToggleVariants;
|
|
4
|
-
declare const ToggleGroupItem: import("svelte").Component<$$ComponentProps, {}, "
|
|
4
|
+
declare const ToggleGroupItem: import("svelte").Component<$$ComponentProps, {}, "value" | "ref">;
|
|
5
5
|
type ToggleGroupItem = ReturnType<typeof ToggleGroupItem>;
|
|
6
6
|
export default ToggleGroupItem;
|
|
@@ -3,6 +3,6 @@ export declare function setToggleGroupCtx(props: ToggleVariants): void;
|
|
|
3
3
|
export declare function getToggleGroupCtx(): ToggleVariants;
|
|
4
4
|
import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui";
|
|
5
5
|
type $$ComponentProps = ToggleGroupPrimitive.RootProps & ToggleVariants;
|
|
6
|
-
declare const ToggleGroup: import("svelte").Component<$$ComponentProps, {}, "
|
|
6
|
+
declare const ToggleGroup: import("svelte").Component<$$ComponentProps, {}, "value" | "ref">;
|
|
7
7
|
type ToggleGroup = ReturnType<typeof ToggleGroup>;
|
|
8
8
|
export default ToggleGroup;
|
package/dist/core/cms.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ApiKeyConfig, AuthConfig, CMSConfig, ICMS, MediaConfig } from '../
|
|
|
4
4
|
import type { CollectionConfigWithType } from '../types/collections.js';
|
|
5
5
|
import type { Language } from '../types/languages.js';
|
|
6
6
|
import type { SingleConfigWithType } from '../types/singles.js';
|
|
7
|
-
import type { PluginConfig } from '../types/plugins.js';
|
|
7
|
+
import type { CustomFieldDefinition, PluginConfig } from '../types/plugins.js';
|
|
8
8
|
import type { FormConfig } from '../types/forms.js';
|
|
9
9
|
import type { AIAdapter } from '../types/adapters/ai.js';
|
|
10
10
|
import type { EmailAdapter } from '../types/adapters/email.js';
|
|
@@ -23,6 +23,7 @@ export declare class CMS implements ICMS {
|
|
|
23
23
|
languages: Language[];
|
|
24
24
|
mediaConfig: MediaConfig;
|
|
25
25
|
plugins: PluginConfig[];
|
|
26
|
+
customFields: Map<string, CustomFieldDefinition>;
|
|
26
27
|
apiKeys: ApiKeyConfig[];
|
|
27
28
|
constructor(config: CMSConfig);
|
|
28
29
|
get auth(): ReturnType<typeof betterAuth>;
|
package/dist/core/cms.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setSchemaGetCMS } from './fields/fieldSchemaToTs.js';
|
|
1
2
|
import { betterAuth } from 'better-auth';
|
|
2
3
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
|
|
3
4
|
import { admin } from 'better-auth/plugins';
|
|
@@ -17,6 +18,7 @@ export class CMS {
|
|
|
17
18
|
languages;
|
|
18
19
|
mediaConfig;
|
|
19
20
|
plugins = [];
|
|
21
|
+
customFields = new Map();
|
|
20
22
|
apiKeys = [];
|
|
21
23
|
constructor(config) {
|
|
22
24
|
this.config = config;
|
|
@@ -50,7 +52,16 @@ export class CMS {
|
|
|
50
52
|
this.apiKeys = config.apiKeys || [];
|
|
51
53
|
if (config.plugins) {
|
|
52
54
|
this.plugins = config.plugins;
|
|
55
|
+
for (const plugin of this.plugins) {
|
|
56
|
+
for (const def of plugin.fields ?? []) {
|
|
57
|
+
if (this.customFields.has(def.fieldType)) {
|
|
58
|
+
throw new Error(`Duplicate custom field type: "${def.fieldType}" (plugin: "${plugin.slug}")`);
|
|
59
|
+
}
|
|
60
|
+
this.customFields.set(def.fieldType, def);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
53
63
|
}
|
|
64
|
+
setSchemaGetCMS(() => this);
|
|
54
65
|
}
|
|
55
66
|
get auth() {
|
|
56
67
|
if (!this._betterAuth) {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { Field } from '../../types/fields.js';
|
|
2
|
+
import type { CustomFieldDefinition } from '../../types/plugins.js';
|
|
2
3
|
import { z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Set custom fields map for client-side use (where getCMS() is unavailable).
|
|
6
|
+
*/
|
|
7
|
+
export declare function setSchemaCustomFields(customFields: Map<string, CustomFieldDefinition>): void;
|
|
8
|
+
/**
|
|
9
|
+
* Set CMS getter for server-side use (avoids static import of server-only cms.ts).
|
|
10
|
+
*/
|
|
11
|
+
export declare function setSchemaGetCMS(getter: () => {
|
|
12
|
+
customFields: Map<string, CustomFieldDefinition>;
|
|
13
|
+
}): void;
|
|
3
14
|
type AnyZodObject = z.ZodObject<any, any>;
|
|
4
15
|
interface GenerateZodSchemaOptions {
|
|
5
16
|
parentRequired?: boolean;
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
let _customFieldsOverride = null;
|
|
3
|
+
/**
|
|
4
|
+
* Set custom fields map for client-side use (where getCMS() is unavailable).
|
|
5
|
+
*/
|
|
6
|
+
export function setSchemaCustomFields(customFields) {
|
|
7
|
+
_customFieldsOverride = customFields;
|
|
8
|
+
}
|
|
9
|
+
let _getCMS = null;
|
|
10
|
+
/**
|
|
11
|
+
* Set CMS getter for server-side use (avoids static import of server-only cms.ts).
|
|
12
|
+
*/
|
|
13
|
+
export function setSchemaGetCMS(getter) {
|
|
14
|
+
_getCMS = getter;
|
|
15
|
+
}
|
|
16
|
+
function getCustomFieldDef(fieldType) {
|
|
17
|
+
if (_customFieldsOverride)
|
|
18
|
+
return _customFieldsOverride.get(fieldType);
|
|
19
|
+
try {
|
|
20
|
+
return _getCMS?.().customFields.get(fieldType);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
2
26
|
export function generateZodSchemaFromField(field, languages, options = {
|
|
3
27
|
parentRequired: true
|
|
4
28
|
}) {
|
|
@@ -283,6 +307,12 @@ export function generateZodSchemaFromField(field, languages, options = {
|
|
|
283
307
|
}
|
|
284
308
|
return schema;
|
|
285
309
|
}
|
|
310
|
+
case 'custom': {
|
|
311
|
+
const customDef = getCustomFieldDef(field.fieldType);
|
|
312
|
+
if (!customDef)
|
|
313
|
+
return z.any();
|
|
314
|
+
return customDef.zodSchema(field, languages);
|
|
315
|
+
}
|
|
286
316
|
default:
|
|
287
317
|
return z.any();
|
|
288
318
|
}
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { resolveMediaFields } from './resolveImageFields.js';
|
|
|
4
4
|
import { resolveRelationFields } from './resolveRelationFields.js';
|
|
5
5
|
import { resolveRichtextLinks } from './resolveRichtextLinks.js';
|
|
6
6
|
import { resolveUrlFields } from './resolveUrlFields.js';
|
|
7
|
+
import { getCMS } from '../../cms.js';
|
|
7
8
|
function translateInlineBlockData(data, fields, language) {
|
|
8
9
|
for (const field of fields) {
|
|
9
10
|
const val = data[field.slug];
|
|
@@ -45,11 +46,60 @@ function translateInlineBlockData(data, fields, language) {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
async function resolveCustomFields(data, fields) {
|
|
50
|
+
// Check if any custom fields exist before accessing CMS
|
|
51
|
+
const hasCustom = fields.some((f) => f.type === 'custom' ||
|
|
52
|
+
f.type === 'object' ||
|
|
53
|
+
f.type === 'blocks');
|
|
54
|
+
if (!hasCustom)
|
|
55
|
+
return data;
|
|
56
|
+
let cms;
|
|
57
|
+
try {
|
|
58
|
+
cms = getCMS();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
63
|
+
const result = { ...data };
|
|
64
|
+
for (const field of fields) {
|
|
65
|
+
const val = data[field.slug];
|
|
66
|
+
switch (field.type) {
|
|
67
|
+
case 'custom': {
|
|
68
|
+
const def = cms.customFields.get(field.fieldType);
|
|
69
|
+
if (def?.populateResolver && val != null) {
|
|
70
|
+
result[field.slug] = await def.populateResolver(val, field);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'object':
|
|
75
|
+
if (val && typeof val === 'object' && 'data' in val) {
|
|
76
|
+
result[field.slug] = {
|
|
77
|
+
...val,
|
|
78
|
+
data: await resolveCustomFields(val.data, field.fields)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case 'blocks':
|
|
83
|
+
if (Array.isArray(val)) {
|
|
84
|
+
result[field.slug] = await Promise.all(val.map(async (item) => {
|
|
85
|
+
const blockDef = field.of.find((d) => d.slug === item.slug);
|
|
86
|
+
if (blockDef) {
|
|
87
|
+
return { ...item, data: await resolveCustomFields(item.data, blockDef.fields) };
|
|
88
|
+
}
|
|
89
|
+
return item;
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
48
97
|
export async function populateEntryData(data, fields, language) {
|
|
49
98
|
let populatedData = await resolveRelationFields(data, fields, language);
|
|
50
99
|
populatedData = await resolveUrlFields(populatedData, fields, language);
|
|
51
100
|
populatedData = await resolveMediaFields(populatedData, fields);
|
|
52
101
|
populatedData = await resolveRichtextLinks(populatedData, fields, language);
|
|
102
|
+
populatedData = (await resolveCustomFields(populatedData, fields));
|
|
53
103
|
populatedData = translateObject(populatedData, language);
|
|
54
104
|
translateInlineBlockData(populatedData, fields, language);
|
|
55
105
|
return populatedData;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getCMS } from '../../cms.js';
|
|
2
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';
|
|
3
|
+
import { getEntrySlugPath, getSlugFromEntryData, getEntryPath } from './slugResolver.js';
|
|
4
4
|
const ENTRY_ID_RE = /data-entry-id="([0-9a-f-]{36})"/g;
|
|
5
5
|
function extractEntryIds(html) {
|
|
6
6
|
const ids = [];
|
|
@@ -130,7 +130,8 @@ export async function resolveRichtextLinks(data, fields, language) {
|
|
|
130
130
|
const slugPath = configSlug ? getEntrySlugPath(configSlug) : 'seo.slug';
|
|
131
131
|
const slug = getSlugFromEntryData(rawData, slugPath, language);
|
|
132
132
|
if (slug) {
|
|
133
|
-
|
|
133
|
+
const configSlug = entryConfigSlugMap[version.entryId];
|
|
134
|
+
slugMap[version.entryId] = configSlug ? getEntryPath(configSlug, slug) : slug;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { urlFieldDataSchema, urlFieldDataWithRelationSchema } from '../../../schemas/field/url.js';
|
|
2
2
|
import { walkInlineBlockNodes, cloneDoc } from '../../../admin/components/tiptap/structured-content-utils.js';
|
|
3
3
|
import { getDbEntries, getDbEntryVersions } from '../entries/operations/get.js';
|
|
4
|
-
import { getEntrySlugPath, getSlugFromEntryData } from './slugResolver.js';
|
|
4
|
+
import { getEntrySlugPath, getSlugFromEntryData, getEntryPath } from './slugResolver.js';
|
|
5
5
|
import { isExternalUrl, mergeRel } from '../../fields/urlUtils.js';
|
|
6
6
|
const FLAT_KEY = '_flat';
|
|
7
7
|
/**
|
|
@@ -121,7 +121,7 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
121
121
|
const slugPath = getEntrySlugPath(entry.slug);
|
|
122
122
|
const slug = getSlugFromEntryData(version.data, slugPath, language);
|
|
123
123
|
if (slug)
|
|
124
|
-
slugMap[entry.id] = slug;
|
|
124
|
+
slugMap[entry.id] = getEntryPath(entry.slug, slug);
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
@@ -8,3 +8,8 @@ export declare function getEntrySlugPath(configSlug: string): string;
|
|
|
8
8
|
* Handles both plain strings and localized objects { lang: string }.
|
|
9
9
|
*/
|
|
10
10
|
export declare function getSlugFromEntryData(data: Record<string, unknown>, slugPath: string, language?: string): string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Build full entry path using collection's pathTemplate.
|
|
13
|
+
* If no template configured, returns the raw slug.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEntryPath(configSlug: string, slug: string): string;
|
|
@@ -32,3 +32,19 @@ export function getSlugFromEntryData(data, slugPath, language) {
|
|
|
32
32
|
}
|
|
33
33
|
return undefined;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Build full entry path using collection's pathTemplate.
|
|
37
|
+
* If no template configured, returns the raw slug.
|
|
38
|
+
*/
|
|
39
|
+
export function getEntryPath(configSlug, slug) {
|
|
40
|
+
try {
|
|
41
|
+
const config = getCMS().getBySlug(configSlug);
|
|
42
|
+
if (config.pathTemplate) {
|
|
43
|
+
return config.pathTemplate.replace('{slug}', slug);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Config not found — fall through
|
|
48
|
+
}
|
|
49
|
+
return slug;
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ImageFieldStyle } from '../../../../types/fields.js';
|
|
2
|
+
import type { ImageStyle, MediaFile } from '../../../../types/media.js';
|
|
3
|
+
export interface ResolvedMedia {
|
|
4
|
+
data: MediaFile;
|
|
5
|
+
styles: Record<string, ImageStyle>;
|
|
6
|
+
blurDataUrl: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve media files by IDs and generate image styles.
|
|
10
|
+
* Useful for plugins that need to resolve media references (e.g. photo-grid).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveMediaWithStyles(mediaIds: string[], styles?: ImageFieldStyle[]): Promise<Record<string, ResolvedMedia>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getCMS } from '../../../cms.js';
|
|
2
|
+
import { getImageStyles } from './imageStyles.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve media files by IDs and generate image styles.
|
|
5
|
+
* Useful for plugins that need to resolve media references (e.g. photo-grid).
|
|
6
|
+
*/
|
|
7
|
+
export async function resolveMediaWithStyles(mediaIds, styles) {
|
|
8
|
+
if (!mediaIds.length)
|
|
9
|
+
return {};
|
|
10
|
+
const cms = getCMS();
|
|
11
|
+
const files = await cms.databaseAdapter.getMediaFiles({ data: { ids: mediaIds } });
|
|
12
|
+
// Synthetic ImageField to pass to getImageStyles
|
|
13
|
+
const syntheticField = {
|
|
14
|
+
type: 'image',
|
|
15
|
+
slug: '_resolved',
|
|
16
|
+
styles: styles ?? []
|
|
17
|
+
};
|
|
18
|
+
const entries = await Promise.all(files.map(async (file) => {
|
|
19
|
+
const { styles: resolvedStyles, blurDataUrl } = await getImageStyles(syntheticField, file);
|
|
20
|
+
return [file.id, { data: file, styles: resolvedStyles, blurDataUrl }];
|
|
21
|
+
}));
|
|
22
|
+
return Object.fromEntries(entries);
|
|
23
|
+
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { Field } from '../../../types/fields.js';
|
|
2
|
+
import type { CustomFieldDefinition } from '../../../types/plugins.js';
|
|
3
|
+
export declare function setGeneratorCustomFields(customFields: Map<string, CustomFieldDefinition>): void;
|
|
2
4
|
export declare function generateTsTypeFromFields(fields: Field[]): string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { toPascalCase } from './utils.js';
|
|
2
|
+
let _customFields = new Map();
|
|
3
|
+
export function setGeneratorCustomFields(customFields) {
|
|
4
|
+
_customFields = customFields;
|
|
5
|
+
}
|
|
2
6
|
function getFieldTypeAsString(field) {
|
|
3
7
|
switch (field.type) {
|
|
4
8
|
case 'text':
|
|
@@ -82,6 +86,10 @@ function getFieldTypeAsString(field) {
|
|
|
82
86
|
urlParts.push('newTab?: boolean');
|
|
83
87
|
return `{ ${urlParts.join('; ')} }`;
|
|
84
88
|
}
|
|
89
|
+
case 'custom': {
|
|
90
|
+
const customDef = _customFields.get(field.fieldType);
|
|
91
|
+
return customDef?.tsType ?? 'unknown';
|
|
92
|
+
}
|
|
85
93
|
default:
|
|
86
94
|
return 'any';
|
|
87
95
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { generateTsTypeFromFields } from './fields.js';
|
|
3
|
+
import { generateTsTypeFromFields, setGeneratorCustomFields } from './fields.js';
|
|
4
4
|
import { generateTsTypeFromFormFields } from './formFields.js';
|
|
5
5
|
import { generateZodSchemaStringFromFormFieldsAsString } from './formFieldSchemaToString.js';
|
|
6
6
|
import { toPascalCase } from './utils.js';
|
|
@@ -203,6 +203,14 @@ function generateRemote(config) {
|
|
|
203
203
|
writeFileSync(filePath, code);
|
|
204
204
|
}
|
|
205
205
|
export function generateRuntime(config) {
|
|
206
|
+
// Build custom fields map from plugins for type generation
|
|
207
|
+
const customFields = new Map();
|
|
208
|
+
for (const plugin of config.plugins ?? []) {
|
|
209
|
+
for (const field of plugin.fields ?? []) {
|
|
210
|
+
customFields.set(field.fieldType, field);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
setGeneratorCustomFields(customFields);
|
|
206
214
|
createCmsRuntimeDir();
|
|
207
215
|
generateTypes(config);
|
|
208
216
|
generateAPI(config);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './config.js';
|
|
2
2
|
export { setRemotes, getRemotes } from '../admin/context/remotes.js';
|
|
3
|
+
export { setCustomFields, getCustomFields } from '../admin/state/custom-fields.svelte.js';
|
|
3
4
|
export { default as Preview } from './components/preview.svelte';
|
|
4
5
|
export { default as HybridTarget } from './components/hybrid-target.svelte';
|
|
5
6
|
export { default as Image } from './components/image.svelte';
|
package/dist/sveltekit/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './config.js';
|
|
2
2
|
export { setRemotes, getRemotes } from '../admin/context/remotes.js';
|
|
3
|
+
export { setCustomFields, getCustomFields } from '../admin/state/custom-fields.svelte.js';
|
|
3
4
|
export { default as Preview } from './components/preview.svelte';
|
|
4
5
|
export { default as HybridTarget } from './components/hybrid-target.svelte';
|
|
5
6
|
export { default as Image } from './components/image.svelte';
|
package/dist/types/cms.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { FilesAdapter } from './adapters/files.js';
|
|
|
3
3
|
import type { CollectionConfig, CollectionConfigWithType } from './collections.js';
|
|
4
4
|
import type { Language } from './languages.js';
|
|
5
5
|
import type { SingleConfig, SingleConfigWithType } from './singles.js';
|
|
6
|
-
import type { PluginConfig } from './plugins.js';
|
|
6
|
+
import type { CustomFieldDefinition, PluginConfig } from './plugins.js';
|
|
7
7
|
import type { FormConfig } from './forms.js';
|
|
8
8
|
import type { AIAdapter } from './adapters/ai.js';
|
|
9
9
|
import type { EmailAdapter } from './adapters/email.js';
|
|
@@ -60,6 +60,7 @@ export interface ICMS {
|
|
|
60
60
|
emailAdapter: EmailAdapter | null;
|
|
61
61
|
authConfig: AuthConfig | null;
|
|
62
62
|
plugins: PluginConfig[];
|
|
63
|
+
customFields: Map<string, CustomFieldDefinition>;
|
|
63
64
|
aiAdapter: AIAdapter | null;
|
|
64
65
|
mediaConfig: MediaConfig;
|
|
65
66
|
apiKeys: ApiKeyConfig[];
|
package/dist/types/config.d.ts
CHANGED
package/dist/types/fields.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { FormatEnum } from 'sharp';
|
|
|
2
2
|
import type { ImageStyle, MediaFile } from './media.js';
|
|
3
3
|
import type { Localized } from './languages.js';
|
|
4
4
|
import type { StructuredContentDoc } from './structured-content.js';
|
|
5
|
-
export type FieldType = 'text' | 'richtext' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'image' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'url';
|
|
5
|
+
export type FieldType = 'text' | 'richtext' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'image' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'url' | 'custom';
|
|
6
6
|
export interface FieldCondition {
|
|
7
7
|
field: string;
|
|
8
8
|
equals?: string | string[];
|
|
@@ -204,4 +204,11 @@ export type UrlFieldData = {
|
|
|
204
204
|
newTab?: boolean;
|
|
205
205
|
rel?: string;
|
|
206
206
|
};
|
|
207
|
-
export
|
|
207
|
+
export interface CustomField extends BaseField {
|
|
208
|
+
type: 'custom';
|
|
209
|
+
/** Matches CustomFieldDefinition.fieldType from a plugin */
|
|
210
|
+
fieldType: string;
|
|
211
|
+
/** Plugin-specific configuration, opaque to core */
|
|
212
|
+
config?: Record<string, unknown>;
|
|
213
|
+
}
|
|
214
|
+
export type Field = TextField | RichtextField | ContentField | NumberField | BooleanField | DateField | DateTimeField | FileField | ImageField | MediaField | SelectField | RadioField | CheckboxesField | RelationField | ObjectField | ArrayField | BlocksField | SlugField | SeoField | UrlField | CustomField;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,5 +10,6 @@ export { type SingleConfig } from './singles.js';
|
|
|
10
10
|
export { type FormConfig, type FormSubmission } from './forms.js';
|
|
11
11
|
export { type FormField, type FormFieldType, type FormBaseField, type FormTextField, type FormEmailField, type FormTextareaField, type FormCheckboxField, type FormSelectField } from './formFields.js';
|
|
12
12
|
export { type CMSConfig, type ApiKeyConfig, type AuthConfig } from './cms.js';
|
|
13
|
+
export { type PluginConfig, type CustomFieldDefinition } from './plugins.js';
|
|
13
14
|
export { type Language, type Localized } from './languages.js';
|
|
14
15
|
export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode } from './layout.js';
|
package/dist/types/index.js
CHANGED
package/dist/types/plugins.d.ts
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { z } from 'zod';
|
|
1
3
|
import type { RawEntry } from './entries.js';
|
|
4
|
+
import type { CustomField } from './fields.js';
|
|
5
|
+
export interface CustomFieldDefinition {
|
|
6
|
+
/** Unique field type slug, e.g. 'photo-grid' */
|
|
7
|
+
fieldType: string;
|
|
8
|
+
/** Lazy-loaded Svelte component for admin UI */
|
|
9
|
+
component: () => Promise<{
|
|
10
|
+
default: Component<any>;
|
|
11
|
+
}>;
|
|
12
|
+
/** Zod schema factory — called with (field, languages) */
|
|
13
|
+
zodSchema: (field: CustomField, languages: string[]) => z.ZodType;
|
|
14
|
+
/** TypeScript type string for codegen */
|
|
15
|
+
tsType?: string;
|
|
16
|
+
/** Populate/resolve step for API output */
|
|
17
|
+
populateResolver?: (value: unknown, field: CustomField) => Promise<unknown>;
|
|
18
|
+
}
|
|
2
19
|
export interface PluginConfig {
|
|
3
20
|
slug: string;
|
|
4
|
-
|
|
21
|
+
fields?: CustomFieldDefinition[];
|
|
22
|
+
hooks?: {
|
|
5
23
|
beforeCreate?: (data: Record<string, unknown>) => Promise<void>;
|
|
6
24
|
afterCreate?: (entry: RawEntry) => Promise<void>;
|
|
7
25
|
beforeUpdate?: (id: string, data: Record<string, unknown>) => Promise<void>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.7.1',
|
|
3
|
+
date: '2026-03-13',
|
|
4
|
+
description: 'Custom fields plugin system, pathTemplate, admin public API',
|
|
5
|
+
features: [
|
|
6
|
+
'Custom fields plugin system — type definition, zod/TS codegen, field renderer, populate resolver',
|
|
7
|
+
'pathTemplate support — configurable URL paths for collections/singles',
|
|
8
|
+
'Admin public exports: admin/helpers (useField, contexts) & admin/ui (shadcn primitives, MediaSelector)',
|
|
9
|
+
'resolveMediaWithStyles utility — resolve media files with image styles'
|
|
10
|
+
],
|
|
11
|
+
fixes: [
|
|
12
|
+
'Schema serialization type cast fix (stricter TS)',
|
|
13
|
+
'Core svelte export condition added to package.json'
|
|
14
|
+
],
|
|
15
|
+
breakingChanges: []
|
|
16
|
+
};
|
package/dist/updates/index.js
CHANGED
|
@@ -24,7 +24,8 @@ import { update as update060 } from './0.6.0/index.js';
|
|
|
24
24
|
import { update as update061 } from './0.6.1/index.js';
|
|
25
25
|
import { update as update062 } from './0.6.2/index.js';
|
|
26
26
|
import { update as update070 } from './0.7.0/index.js';
|
|
27
|
-
|
|
27
|
+
import { update as update071 } from './0.7.1/index.js';
|
|
28
|
+
export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013, update014, update015, update020, update022, update050, update051, update052, update053, update054, update055, update056, update057, update058, update060, update061, update062, update070, update071];
|
|
28
29
|
export const getUpdatesFrom = (fromVersion) => {
|
|
29
30
|
const fromParts = fromVersion.split('.').map(Number);
|
|
30
31
|
return updates.filter((update) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "includio-cms",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "vite build && npm run prepack",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"exports": {
|
|
48
48
|
"./core": {
|
|
49
49
|
"types": "./dist/core/index.d.ts",
|
|
50
|
+
"svelte": "./dist/core/index.js",
|
|
50
51
|
"node": "./dist/core/index.js"
|
|
51
52
|
},
|
|
52
53
|
"./entity": {
|
|
@@ -58,6 +59,14 @@
|
|
|
58
59
|
"svelte": "./dist/types/index.js",
|
|
59
60
|
"node": "./dist/types/index.js"
|
|
60
61
|
},
|
|
62
|
+
"./admin/helpers": {
|
|
63
|
+
"types": "./dist/admin/helpers/index.d.ts",
|
|
64
|
+
"svelte": "./dist/admin/helpers/index.js"
|
|
65
|
+
},
|
|
66
|
+
"./admin/ui": {
|
|
67
|
+
"types": "./dist/admin/ui/index.d.ts",
|
|
68
|
+
"svelte": "./dist/admin/ui/index.js"
|
|
69
|
+
},
|
|
61
70
|
"./admin": {
|
|
62
71
|
"types": "./dist/admin/index.d.ts",
|
|
63
72
|
"svelte": "./dist/admin/index.js"
|