includio-cms 0.16.0 → 0.19.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 +122 -0
- package/DOCS.md +1 -1
- package/README.md +62 -0
- package/dist/admin/api/rest/routes/collections.js +1 -1
- package/dist/admin/api/rest/routes/entries.js +1 -1
- package/dist/admin/api/rest/routes/singletons.js +1 -1
- package/dist/admin/remote/entry.remote.js +20 -3
- package/dist/admin/remote/invite.d.ts +1 -1
- package/dist/admin/remote/preview.remote.js +10 -2
- package/dist/admin/remote/reorder.js +1 -1
- package/dist/admin/remote/shop.remote.d.ts +18 -18
- package/dist/ai-claude/index.d.ts +9 -0
- package/dist/ai-claude/index.js +23 -7
- package/dist/ai-openai/index.d.ts +9 -0
- package/dist/ai-openai/index.js +28 -9
- package/dist/cms/runtime/api.d.ts +10 -6
- package/dist/cms/runtime/api.js +7 -7
- 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 +1 -1
- 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 +1 -1
- 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/server/entries/operations/create.js +1 -1
- package/dist/core/server/entries/operations/get.d.ts +20 -16
- package/dist/core/server/entries/operations/get.js +45 -214
- package/dist/core/server/entries/operations/resolveEntry.d.ts +94 -0
- package/dist/core/server/entries/operations/resolveEntry.js +210 -0
- package/dist/core/server/entries/operations/update.js +1 -1
- package/dist/core/server/fields/populateEntry.d.ts +9 -1
- package/dist/core/server/fields/populateEntry.js +22 -18
- package/dist/core/server/fields/resolveRelationFields.d.ts +2 -1
- package/dist/core/server/fields/resolveRelationFields.js +140 -34
- package/dist/core/server/fields/resolveRichtextLinks.d.ts +2 -1
- package/dist/core/server/fields/resolveRichtextLinks.js +2 -1
- package/dist/core/server/fields/resolveUrlFields.d.ts +2 -1
- package/dist/core/server/fields/resolveUrlFields.js +6 -5
- package/dist/core/server/generator/generator.js +17 -14
- package/dist/db-postgres/index.d.ts +4 -0
- package/dist/db-postgres/index.js +4 -0
- package/dist/email-nodemailer/index.d.ts +9 -0
- package/dist/email-nodemailer/index.js +28 -6
- package/dist/entity/index.js +1 -1
- package/dist/files-local/index.d.ts +4 -0
- package/dist/files-local/index.js +4 -0
- package/dist/paraglide/messages/_index.d.ts +3 -36
- package/dist/paraglide/messages/_index.js +3 -71
- package/dist/paraglide/messages/hello_world.d.ts +5 -0
- package/dist/paraglide/messages/hello_world.js +33 -0
- package/dist/paraglide/messages/login_hello.d.ts +16 -0
- package/dist/paraglide/messages/login_hello.js +34 -0
- package/dist/paraglide/messages/login_please_login.d.ts +16 -0
- package/dist/paraglide/messages/login_please_login.js +34 -0
- package/dist/shop/server/populate.d.ts +2 -1
- package/dist/shop/server/populate.js +2 -1
- package/dist/sveltekit/server/index.d.ts +1 -1
- package/dist/sveltekit/server/index.js +1 -1
- package/dist/types/adapters/ai.d.ts +8 -0
- package/dist/types/adapters/db.d.ts +9 -0
- package/dist/types/adapters/email.d.ts +6 -0
- package/dist/types/adapters/files.d.ts +5 -0
- package/dist/types/plugins.d.ts +6 -2
- package/dist/updates/0.18.0/index.d.ts +2 -0
- package/dist/updates/0.18.0/index.js +78 -0
- package/dist/updates/0.19.0/index.d.ts +2 -0
- package/dist/updates/0.19.0/index.js +40 -0
- package/dist/updates/index.js +3 -1
- package/package.json +14 -5
- package/dist/paraglide/messages/en.d.ts +0 -5
- package/dist/paraglide/messages/en.js +0 -14
- package/dist/paraglide/messages/pl.d.ts +0 -5
- package/dist/paraglide/messages/pl.js +0 -14
|
@@ -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, {}, "ref" | "value">;
|
|
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" | "ref" | "value">;
|
|
12
12
|
type Input = ReturnType<typeof Input>;
|
|
13
13
|
export default Input;
|
|
@@ -6,6 +6,6 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
|
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
9
|
-
}, {}, "
|
|
9
|
+
}, {}, "ref" | "value">;
|
|
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">, {}, "ref" | "value">;
|
|
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, {}, "ref" | "value">;
|
|
3
3
|
type RadioGroup = ReturnType<typeof RadioGroup>;
|
|
4
4
|
export default RadioGroup;
|
|
@@ -6,6 +6,6 @@ declare const SidebarInput: import("svelte").Component<(Omit<import("svelte/elem
|
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
9
|
-
}, {}, "
|
|
9
|
+
}, {}, "ref" | "value">;
|
|
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, {}, "ref" | "value">;
|
|
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">, {}, "ref" | "value">;
|
|
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, {}, "ref" | "value">;
|
|
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, {}, "ref" | "value">;
|
|
7
7
|
type ToggleGroup = ReturnType<typeof ToggleGroup>;
|
|
8
8
|
export default ToggleGroup;
|
|
@@ -2,7 +2,7 @@ import { requireAuth } from '../../../../admin/remote/middleware/auth.js';
|
|
|
2
2
|
import { getCMS } from '../../../cms.js';
|
|
3
3
|
import { entryTypes } from '../../../../types/entries.js';
|
|
4
4
|
import z from 'zod';
|
|
5
|
-
import { getDbEntryOrThrow } from './get.js';
|
|
5
|
+
import { _getDbEntryOrThrow as getDbEntryOrThrow } from './get.js';
|
|
6
6
|
import { generateZodSchemaFromFields } from '../../../fields/fieldSchemaToTs.js';
|
|
7
7
|
import { getFieldsFromConfig } from '../../../fields/layoutUtils.js';
|
|
8
8
|
export const createEntrySchema = z.object({
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { DbEntry, DbEntryVersion, Entry, GetDbEntriesOptions, GetDbEntryOptions, GetDbEntryVersionOptions, GetDbEntryVersionsOptions,
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
8
|
-
export declare const
|
|
9
|
-
export declare const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import type { DbEntry, DbEntryVersion, Entry, GetDbEntriesOptions, GetDbEntryOptions, GetDbEntryVersionOptions, GetDbEntryVersionsOptions, GetRawEntriesOptions, getRawEntryOptions, RawEntry } from '../../../../types/entries.js';
|
|
2
|
+
export declare const _getDbEntries: (options: GetDbEntriesOptions) => Promise<DbEntry[]>;
|
|
3
|
+
export declare const _countDbEntries: (options: Omit<GetDbEntriesOptions, "limit" | "offset" | "orderBy">) => Promise<number>;
|
|
4
|
+
export declare const _getDbEntry: (options: GetDbEntryOptions) => Promise<DbEntry | null>;
|
|
5
|
+
export declare const _getDbEntryOrThrow: (options: GetDbEntryOptions) => Promise<DbEntry>;
|
|
6
|
+
export declare const _countRawEntries: (options: Omit<GetRawEntriesOptions, "limit" | "offset" | "orderBy">) => Promise<number>;
|
|
7
|
+
export declare const _getRawEntries: (options: GetRawEntriesOptions) => Promise<RawEntry[]>;
|
|
8
|
+
export declare const _getRawEntry: (options: getRawEntryOptions) => Promise<RawEntry | null>;
|
|
9
|
+
export declare const _getRawEntryOrThrow: (options: getRawEntryOptions) => Promise<RawEntry>;
|
|
10
|
+
/**
|
|
11
|
+
* Admin helper: returns dropdown labels for collection entries.
|
|
12
|
+
* Not part of the public resolver API — admin UI use only.
|
|
13
|
+
*/
|
|
14
14
|
export declare const getEntryLabels: (options: {
|
|
15
15
|
slug: string;
|
|
16
16
|
ids?: string[];
|
|
@@ -22,9 +22,13 @@ export declare const getEntryLabels: (options: {
|
|
|
22
22
|
label: string;
|
|
23
23
|
total: number;
|
|
24
24
|
}[]>;
|
|
25
|
-
export declare const
|
|
26
|
-
export declare const
|
|
27
|
-
export declare const
|
|
25
|
+
export declare const _getDbEntryVersions: (options: GetDbEntryVersionsOptions) => Promise<DbEntryVersion[]>;
|
|
26
|
+
export declare const _getDbEntryVersion: (options: GetDbEntryVersionOptions) => Promise<DbEntryVersion | null>;
|
|
27
|
+
export declare const _getDbEntryVersionOrThrow: (options: GetDbEntryVersionOptions) => Promise<DbEntryVersion>;
|
|
28
|
+
/**
|
|
29
|
+
* Admin helper: fetches a specific version of an entry by version id and populates it.
|
|
30
|
+
* Used by version-history admin UI; not part of public resolver API.
|
|
31
|
+
*/
|
|
28
32
|
export declare const getEntryVersion: (options: {
|
|
29
33
|
id: string;
|
|
30
34
|
language: string;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { getCMS } from '../../../cms.js';
|
|
2
2
|
import { getAtPath } from '../../../../admin/utils/objectPath.js';
|
|
3
|
-
import { populateEntryData } from '../../fields/populateEntry.js';
|
|
4
3
|
import { getFieldsFromConfig } from '../../../fields/layoutUtils.js';
|
|
5
4
|
import { getEntrySlugPath, getSlugFromEntryData, getEntryPath } from '../../fields/slugResolver.js';
|
|
6
|
-
export const
|
|
5
|
+
export const _getDbEntries = async (options) => {
|
|
7
6
|
return getCMS().databaseAdapter.getEntries(options);
|
|
8
7
|
};
|
|
9
|
-
export const
|
|
8
|
+
export const _countDbEntries = async (options) => {
|
|
10
9
|
return getCMS().databaseAdapter.countEntries(options);
|
|
11
10
|
};
|
|
12
|
-
export const
|
|
11
|
+
export const _getDbEntry = async (options) => {
|
|
13
12
|
const { id, ...rest } = options;
|
|
14
13
|
const [entry] = await getCMS().databaseAdapter.getEntries({
|
|
15
14
|
...rest,
|
|
@@ -17,15 +16,15 @@ export const getDbEntry = async (options) => {
|
|
|
17
16
|
});
|
|
18
17
|
return entry || null;
|
|
19
18
|
};
|
|
20
|
-
export const
|
|
21
|
-
const entry = await
|
|
19
|
+
export const _getDbEntryOrThrow = async (options) => {
|
|
20
|
+
const entry = await _getDbEntry(options);
|
|
22
21
|
if (!entry) {
|
|
23
22
|
throw new Error('Entry not found');
|
|
24
23
|
}
|
|
25
24
|
return entry;
|
|
26
25
|
};
|
|
27
|
-
export const
|
|
28
|
-
return
|
|
26
|
+
export const _countRawEntries = async (options) => {
|
|
27
|
+
return _countDbEntries(options);
|
|
29
28
|
};
|
|
30
29
|
/** Helper: group versions by lang into per-lang published/scheduled/draft maps */
|
|
31
30
|
function buildPerLangVersionMaps(versions) {
|
|
@@ -33,7 +32,6 @@ function buildPerLangVersionMaps(versions) {
|
|
|
33
32
|
const publishedVersions = {};
|
|
34
33
|
const scheduledVersions = {};
|
|
35
34
|
const draftVersions = {};
|
|
36
|
-
// Group versions by lang
|
|
37
35
|
const byLang = new Map();
|
|
38
36
|
for (const v of versions) {
|
|
39
37
|
const arr = byLang.get(v.lang) || [];
|
|
@@ -42,11 +40,8 @@ function buildPerLangVersionMaps(versions) {
|
|
|
42
40
|
}
|
|
43
41
|
for (const [lang, langVersions] of byLang) {
|
|
44
42
|
const sorted = langVersions.sort((a, b) => b.versionNumber - a.versionNumber);
|
|
45
|
-
// Find latest published (publishedAt <= now)
|
|
46
43
|
const published = sorted.find((v) => v.publishedAt != null && v.publishedAt <= now) || null;
|
|
47
|
-
// Find scheduled (publishedAt > now)
|
|
48
44
|
const scheduled = sorted.find((v) => v.publishedAt != null && v.publishedAt > now) || null;
|
|
49
|
-
// Draft = latest version without publishedAt, or latest version newer than published
|
|
50
45
|
const draft = sorted.find((v) => v.publishedAt == null) || null;
|
|
51
46
|
publishedVersions[lang] = published;
|
|
52
47
|
scheduledVersions[lang] = scheduled;
|
|
@@ -54,8 +49,8 @@ function buildPerLangVersionMaps(versions) {
|
|
|
54
49
|
}
|
|
55
50
|
return { publishedVersions, scheduledVersions, draftVersions };
|
|
56
51
|
}
|
|
57
|
-
export const
|
|
58
|
-
const dbEntries = await
|
|
52
|
+
export const _getRawEntries = async (options) => {
|
|
53
|
+
const dbEntries = await _getDbEntries(options);
|
|
59
54
|
const entries = await Promise.all(dbEntries.map(async (entry) => {
|
|
60
55
|
try {
|
|
61
56
|
const versions = await getCMS().databaseAdapter.getEntryVersions({
|
|
@@ -78,203 +73,31 @@ export const getRawEntries = async (options) => {
|
|
|
78
73
|
}));
|
|
79
74
|
return entries.filter((e) => e !== null);
|
|
80
75
|
};
|
|
81
|
-
export const
|
|
82
|
-
const [entry] = await
|
|
76
|
+
export const _getRawEntry = async (options) => {
|
|
77
|
+
const [entry] = await _getRawEntries({
|
|
83
78
|
...options,
|
|
84
79
|
ids: options.id ? [options.id] : undefined,
|
|
85
80
|
includeArchived: options.includeArchived
|
|
86
81
|
});
|
|
87
82
|
return entry || null;
|
|
88
83
|
};
|
|
89
|
-
export const
|
|
90
|
-
const entry = await
|
|
91
|
-
if (!entry) {
|
|
92
|
-
throw new Error('Entry not found');
|
|
93
|
-
}
|
|
94
|
-
return entry;
|
|
95
|
-
};
|
|
96
|
-
export const getEntries = async (options = {}) => {
|
|
97
|
-
const cms = getCMS();
|
|
98
|
-
const language = options.language || cms.languages[0];
|
|
99
|
-
const status = options.status || 'published';
|
|
100
|
-
// Fast path: DB-level pagination when limit/offset provided and adapter supports it
|
|
101
|
-
if (options.limit != null && cms.databaseAdapter.getPaginatedEntries) {
|
|
102
|
-
const rows = await cms.databaseAdapter.getPaginatedEntries({
|
|
103
|
-
slug: options.slug,
|
|
104
|
-
ids: options.ids,
|
|
105
|
-
language,
|
|
106
|
-
status,
|
|
107
|
-
dataValues: options.dataValues,
|
|
108
|
-
dataLike: options.dataLike,
|
|
109
|
-
dataILikeOr: options.dataILikeOr,
|
|
110
|
-
orderBy: options.orderBy,
|
|
111
|
-
dataOrderBy: options.dataOrderBy,
|
|
112
|
-
limit: options.limit,
|
|
113
|
-
offset: options.offset ?? 0
|
|
114
|
-
});
|
|
115
|
-
const entries = await Promise.all(rows.map(async ({ entry, version }) => {
|
|
116
|
-
try {
|
|
117
|
-
const config = cms.getBySlug(entry.slug);
|
|
118
|
-
const fields = getFieldsFromConfig(config);
|
|
119
|
-
const populatedData = await populateEntryData(version.data, fields, language, entry.id);
|
|
120
|
-
const slugPath = getEntrySlugPath(entry.slug);
|
|
121
|
-
const slug = getSlugFromEntryData(version.data, slugPath, language);
|
|
122
|
-
const _url = slug ? getEntryPath(entry.slug, slug) : undefined;
|
|
123
|
-
const result = {
|
|
124
|
-
_id: entry.id,
|
|
125
|
-
_slug: entry.slug,
|
|
126
|
-
_type: entry.type,
|
|
127
|
-
_publishedAt: version.publishedAt,
|
|
128
|
-
_url,
|
|
129
|
-
...populatedData
|
|
130
|
-
};
|
|
131
|
-
if (config.type === 'collection' && config.orderable) {
|
|
132
|
-
result._sortOrder = entry.sortOrder;
|
|
133
|
-
}
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
console.error(`[CMS] Failed to populate entry ${entry.id} (${entry.slug}):`, error);
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
}));
|
|
141
|
-
return entries.filter((e) => e !== null);
|
|
142
|
-
}
|
|
143
|
-
// Slow path: in-memory pagination (backward compat)
|
|
144
|
-
const ids = options.ids;
|
|
145
|
-
const slug = options.slug;
|
|
146
|
-
const dataValues = options.dataValues;
|
|
147
|
-
const dataLike = options.dataLike;
|
|
148
|
-
const dataILikeOr = options.dataILikeOr;
|
|
149
|
-
const dbEntries = await cms.databaseAdapter.getEntries({
|
|
150
|
-
ids,
|
|
151
|
-
slug,
|
|
152
|
-
orderBy: options.orderBy
|
|
153
|
-
});
|
|
154
|
-
if (dbEntries.length === 0) {
|
|
155
|
-
return [];
|
|
156
|
-
}
|
|
157
|
-
const filteredEntries = status === 'archived'
|
|
158
|
-
? dbEntries.filter((e) => e.archivedAt != null)
|
|
159
|
-
: dbEntries.filter((e) => e.archivedAt == null);
|
|
160
|
-
if (filteredEntries.length === 0) {
|
|
161
|
-
return [];
|
|
162
|
-
}
|
|
163
|
-
const entriesMap = new Map(filteredEntries.map((entry) => [entry.id, entry]));
|
|
164
|
-
const entryIds = filteredEntries.map((entry) => entry.id);
|
|
165
|
-
const allVersions = await cms.databaseAdapter.getEntryVersions({
|
|
166
|
-
entryIds,
|
|
167
|
-
lang: language,
|
|
168
|
-
dataValues,
|
|
169
|
-
dataLike,
|
|
170
|
-
dataILikeOr
|
|
171
|
-
});
|
|
172
|
-
const now = new Date();
|
|
173
|
-
const versionEntries = [];
|
|
174
|
-
const versionsByEntry = new Map();
|
|
175
|
-
for (const v of allVersions) {
|
|
176
|
-
const arr = versionsByEntry.get(v.entryId) || [];
|
|
177
|
-
arr.push(v);
|
|
178
|
-
versionsByEntry.set(v.entryId, arr);
|
|
179
|
-
}
|
|
180
|
-
for (const [entryId, versions] of versionsByEntry) {
|
|
181
|
-
const dbEntry = entriesMap.get(entryId);
|
|
182
|
-
if (!dbEntry)
|
|
183
|
-
continue;
|
|
184
|
-
const sorted = versions.sort((a, b) => b.versionNumber - a.versionNumber);
|
|
185
|
-
let picked = null;
|
|
186
|
-
switch (status) {
|
|
187
|
-
case 'published':
|
|
188
|
-
picked = sorted.find((v) => v.publishedAt != null && v.publishedAt <= now) || null;
|
|
189
|
-
break;
|
|
190
|
-
case 'scheduled':
|
|
191
|
-
picked = sorted.find((v) => v.publishedAt != null && v.publishedAt > now) || null;
|
|
192
|
-
break;
|
|
193
|
-
case 'draft':
|
|
194
|
-
picked = sorted.find((v) => v.publishedAt == null) || null;
|
|
195
|
-
break;
|
|
196
|
-
case 'archived':
|
|
197
|
-
picked = sorted[0] || null;
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
if (picked) {
|
|
201
|
-
versionEntries.push({ version: picked, dbEntry });
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const entryOrder = new Map(filteredEntries.map((e, i) => [e.id, i]));
|
|
205
|
-
versionEntries.sort((a, b) => (entryOrder.get(a.dbEntry.id) ?? 0) - (entryOrder.get(b.dbEntry.id) ?? 0));
|
|
206
|
-
const entries = await Promise.all(versionEntries.map(async ({ version, dbEntry }) => {
|
|
207
|
-
try {
|
|
208
|
-
const config = cms.getBySlug(dbEntry.slug);
|
|
209
|
-
const fields = getFieldsFromConfig(config);
|
|
210
|
-
const populatedData = await populateEntryData(version.data, fields, language, dbEntry.id);
|
|
211
|
-
const slugPath = getEntrySlugPath(dbEntry.slug);
|
|
212
|
-
const slug = getSlugFromEntryData(version.data, slugPath, language);
|
|
213
|
-
const _url = slug ? getEntryPath(dbEntry.slug, slug) : undefined;
|
|
214
|
-
const result = {
|
|
215
|
-
_id: dbEntry.id,
|
|
216
|
-
_slug: dbEntry.slug,
|
|
217
|
-
_type: dbEntry.type,
|
|
218
|
-
_publishedAt: version.publishedAt,
|
|
219
|
-
_url,
|
|
220
|
-
...populatedData
|
|
221
|
-
};
|
|
222
|
-
if (config.type === 'collection' && config.orderable) {
|
|
223
|
-
result._sortOrder = dbEntry.sortOrder;
|
|
224
|
-
}
|
|
225
|
-
return result;
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
console.error(`[CMS] Failed to populate entry ${dbEntry.id} (${dbEntry.slug}):`, error);
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
}));
|
|
232
|
-
let results = entries.filter((e) => e !== null);
|
|
233
|
-
if (options.offset)
|
|
234
|
-
results = results.slice(options.offset);
|
|
235
|
-
if (options.limit)
|
|
236
|
-
results = results.slice(0, options.limit);
|
|
237
|
-
return results;
|
|
238
|
-
};
|
|
239
|
-
export const countEntries = async (options) => {
|
|
240
|
-
const cms = getCMS();
|
|
241
|
-
const language = options.language || cms.languages[0];
|
|
242
|
-
const status = options.status || 'published';
|
|
243
|
-
if (cms.databaseAdapter.countPaginatedEntries) {
|
|
244
|
-
return cms.databaseAdapter.countPaginatedEntries({
|
|
245
|
-
slug: options.slug,
|
|
246
|
-
ids: options.ids,
|
|
247
|
-
language,
|
|
248
|
-
status,
|
|
249
|
-
dataValues: options.dataValues,
|
|
250
|
-
dataLike: options.dataLike,
|
|
251
|
-
dataILikeOr: options.dataILikeOr
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
// Fallback: use old getEntries without limit/offset (counts all)
|
|
255
|
-
const entries = await getEntries({ ...options });
|
|
256
|
-
return entries.length;
|
|
257
|
-
};
|
|
258
|
-
export const getEntry = async (options = {}) => {
|
|
259
|
-
const [entry] = await getEntries({
|
|
260
|
-
...options,
|
|
261
|
-
ids: options.id ? [options.id] : undefined
|
|
262
|
-
});
|
|
263
|
-
return entry || null;
|
|
264
|
-
};
|
|
265
|
-
export const getEntryOrThrow = async (options = {}) => {
|
|
266
|
-
const entry = await getEntry(options);
|
|
84
|
+
export const _getRawEntryOrThrow = async (options) => {
|
|
85
|
+
const entry = await _getRawEntry(options);
|
|
267
86
|
if (!entry) {
|
|
268
87
|
throw new Error('Entry not found');
|
|
269
88
|
}
|
|
270
89
|
return entry;
|
|
271
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Admin helper: returns dropdown labels for collection entries.
|
|
93
|
+
* Not part of the public resolver API — admin UI use only.
|
|
94
|
+
*/
|
|
272
95
|
export const getEntryLabels = async (options) => {
|
|
273
96
|
const cms = getCMS();
|
|
274
97
|
const config = cms.getBySlug(options.slug);
|
|
275
98
|
if (!config || config.type !== 'collection')
|
|
276
99
|
return [];
|
|
277
|
-
const dbEntries = await
|
|
100
|
+
const dbEntries = await _getDbEntries({
|
|
278
101
|
slug: options.slug,
|
|
279
102
|
ids: options.ids
|
|
280
103
|
});
|
|
@@ -282,7 +105,6 @@ export const getEntryLabels = async (options) => {
|
|
|
282
105
|
return [];
|
|
283
106
|
const entryIds = dbEntries.map((e) => e.id);
|
|
284
107
|
const language = cms.languages[0];
|
|
285
|
-
// Get versions for default language
|
|
286
108
|
const allVersions = await cms.databaseAdapter.getEntryVersions({
|
|
287
109
|
entryIds,
|
|
288
110
|
lang: language
|
|
@@ -290,13 +112,12 @@ export const getEntryLabels = async (options) => {
|
|
|
290
112
|
const now = new Date();
|
|
291
113
|
const statusFilter = options.status ?? 'all';
|
|
292
114
|
const entryAdminTitle = config.entryAdminTitle;
|
|
293
|
-
let results = dbEntries
|
|
115
|
+
let results = dbEntries
|
|
116
|
+
.map((entry) => {
|
|
294
117
|
const entryVersions = allVersions.filter((v) => v.entryId === entry.id);
|
|
295
118
|
const sorted = entryVersions.sort((a, b) => b.versionNumber - a.versionNumber);
|
|
296
|
-
// Determine status from versions
|
|
297
119
|
const publishedVersion = sorted.find((v) => v.publishedAt != null && v.publishedAt <= now) || null;
|
|
298
120
|
const hasPublished = publishedVersion != null;
|
|
299
|
-
// Filter by status
|
|
300
121
|
if (statusFilter === 'published' && !hasPublished)
|
|
301
122
|
return null;
|
|
302
123
|
if (statusFilter === 'draft' && hasPublished)
|
|
@@ -310,50 +131,60 @@ export const getEntryLabels = async (options) => {
|
|
|
310
131
|
}
|
|
311
132
|
}
|
|
312
133
|
return { id: entry.id, label };
|
|
313
|
-
})
|
|
314
|
-
|
|
134
|
+
})
|
|
135
|
+
.filter((r) => r != null);
|
|
315
136
|
if (options.search) {
|
|
316
137
|
const searchLower = options.search.toLowerCase();
|
|
317
138
|
results = results.filter((r) => r.label.toLowerCase().includes(searchLower));
|
|
318
139
|
}
|
|
319
140
|
const total = results.length;
|
|
320
|
-
// Apply limit
|
|
321
141
|
if (options.limit && options.limit > 0) {
|
|
322
142
|
results = results.slice(0, options.limit);
|
|
323
143
|
}
|
|
324
144
|
return results.map((r) => ({ ...r, total }));
|
|
325
145
|
};
|
|
326
|
-
export const
|
|
146
|
+
export const _getDbEntryVersions = async (options) => {
|
|
327
147
|
return getCMS().databaseAdapter.getEntryVersions(options);
|
|
328
148
|
};
|
|
329
|
-
export const
|
|
330
|
-
const [version] = await
|
|
149
|
+
export const _getDbEntryVersion = async (options) => {
|
|
150
|
+
const [version] = await _getDbEntryVersions({
|
|
331
151
|
...options,
|
|
332
152
|
ids: options.id ? [options.id] : undefined
|
|
333
153
|
});
|
|
334
154
|
return version || null;
|
|
335
155
|
};
|
|
336
|
-
export const
|
|
337
|
-
const version = await
|
|
156
|
+
export const _getDbEntryVersionOrThrow = async (options) => {
|
|
157
|
+
const version = await _getDbEntryVersion(options);
|
|
338
158
|
if (!version) {
|
|
339
159
|
throw new Error('Entry version not found');
|
|
340
160
|
}
|
|
341
161
|
return version;
|
|
342
162
|
};
|
|
163
|
+
/**
|
|
164
|
+
* Admin helper: fetches a specific version of an entry by version id and populates it.
|
|
165
|
+
* Used by version-history admin UI; not part of public resolver API.
|
|
166
|
+
*/
|
|
343
167
|
export const getEntryVersion = async (options) => {
|
|
344
168
|
const language = options.language || getCMS().languages[0];
|
|
345
|
-
const dbEntryVersion = await
|
|
346
|
-
if (!dbEntryVersion)
|
|
169
|
+
const dbEntryVersion = await _getDbEntryVersion({ id: options.id });
|
|
170
|
+
if (!dbEntryVersion)
|
|
347
171
|
return null;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (!dbEntry) {
|
|
172
|
+
const dbEntry = await _getDbEntry({ id: dbEntryVersion.entryId });
|
|
173
|
+
if (!dbEntry)
|
|
351
174
|
return null;
|
|
352
|
-
}
|
|
353
175
|
try {
|
|
354
176
|
const config = getCMS().getBySlug(dbEntry.slug);
|
|
355
177
|
const fields = getFieldsFromConfig(config);
|
|
356
|
-
const
|
|
178
|
+
const { _populate } = await import('../../fields/populateEntry.js');
|
|
179
|
+
const populatedData = await _populate(dbEntryVersion.data, fields, {
|
|
180
|
+
locale: language,
|
|
181
|
+
status: 'published',
|
|
182
|
+
depth: 0,
|
|
183
|
+
maxDepth: 5,
|
|
184
|
+
visited: new Set([dbEntry.id]),
|
|
185
|
+
populate: {},
|
|
186
|
+
entryId: dbEntry.id
|
|
187
|
+
});
|
|
357
188
|
const slugPath = getEntrySlugPath(dbEntry.slug);
|
|
358
189
|
const entrySlug = getSlugFromEntryData(dbEntryVersion.data, slugPath, language);
|
|
359
190
|
const _url = entrySlug ? getEntryPath(dbEntry.slug, entrySlug) : undefined;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Entry } from '../../../../types/entries.js';
|
|
2
|
+
/**
|
|
3
|
+
* Status filter for `resolveEntry`/`resolveEntries`/`countEntries`.
|
|
4
|
+
* - `published` (default): newest version with `publishedAt <= now`
|
|
5
|
+
* - `draft`: newest version without `publishedAt`
|
|
6
|
+
* - `scheduled`: newest version with `publishedAt > now` (use case: countdown to launch)
|
|
7
|
+
*
|
|
8
|
+
* `archived` is intentionally excluded from public API (admin uses internal `_getRawEntries`).
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export type ResolveStatus = 'published' | 'draft' | 'scheduled';
|
|
12
|
+
/**
|
|
13
|
+
* Recursion + per-field opt-out config for relation population.
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export type PopulateConfig = {
|
|
17
|
+
/** Hard cap on relation depth. Default 5. Use `0` to keep all relations as raw IDs. */
|
|
18
|
+
maxDepth?: number;
|
|
19
|
+
/** Per-field opt-out. `{ author: false }` keeps `author` as raw ID instead of populated Entry. */
|
|
20
|
+
fields?: Record<string, false>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Internal context passed through the populate chain.
|
|
24
|
+
* Carries locale/status cascade, recursion guards, and the current entry id (for shop populate).
|
|
25
|
+
*/
|
|
26
|
+
export interface PopulateCtx {
|
|
27
|
+
locale: string;
|
|
28
|
+
status: ResolveStatus;
|
|
29
|
+
depth: number;
|
|
30
|
+
maxDepth: number;
|
|
31
|
+
visited: Set<string>;
|
|
32
|
+
populate: PopulateConfig;
|
|
33
|
+
entryId: string;
|
|
34
|
+
}
|
|
35
|
+
/** @public */
|
|
36
|
+
export interface ResolveEntryOptions {
|
|
37
|
+
id?: string;
|
|
38
|
+
collection?: string;
|
|
39
|
+
locale?: string;
|
|
40
|
+
status?: ResolveStatus;
|
|
41
|
+
populate?: PopulateConfig;
|
|
42
|
+
}
|
|
43
|
+
/** @public */
|
|
44
|
+
export interface ResolveEntriesOptions {
|
|
45
|
+
collection: string;
|
|
46
|
+
locale?: string;
|
|
47
|
+
status?: ResolveStatus;
|
|
48
|
+
ids?: string[];
|
|
49
|
+
filter?: {
|
|
50
|
+
dataValues?: Record<string, unknown>;
|
|
51
|
+
dataLike?: Record<string, unknown>;
|
|
52
|
+
dataILikeOr?: Record<string, unknown>;
|
|
53
|
+
};
|
|
54
|
+
orderBy?: {
|
|
55
|
+
column: 'createdAt' | 'updatedAt' | 'sortOrder';
|
|
56
|
+
direction: 'asc' | 'desc';
|
|
57
|
+
};
|
|
58
|
+
dataOrderBy?: {
|
|
59
|
+
field: string;
|
|
60
|
+
direction: 'asc' | 'desc';
|
|
61
|
+
};
|
|
62
|
+
limit?: number;
|
|
63
|
+
offset?: number;
|
|
64
|
+
populate?: PopulateConfig;
|
|
65
|
+
}
|
|
66
|
+
/** @public */
|
|
67
|
+
export type CountEntriesOptions = Omit<ResolveEntriesOptions, 'limit' | 'offset' | 'populate' | 'orderBy' | 'dataOrderBy'>;
|
|
68
|
+
/**
|
|
69
|
+
* Fetch a single populated Entry.
|
|
70
|
+
*
|
|
71
|
+
* - At least one of `id` or `collection` must be provided.
|
|
72
|
+
* - `locale` defaults to `cms.languages[0]`. Strict — returns `null` when no version exists in the requested locale.
|
|
73
|
+
* - `status` defaults to `'published'`. See {@link ResolveStatus}.
|
|
74
|
+
* - `populate` controls relation depth + per-field opt-out. See {@link PopulateConfig}.
|
|
75
|
+
*
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveEntry(opts: ResolveEntryOptions): Promise<Entry | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Fetch a list of populated Entries from a collection (or singleton with multiple instances).
|
|
81
|
+
*
|
|
82
|
+
* - `collection` is required.
|
|
83
|
+
* - `locale` strict (excludes entries without a version in requested locale).
|
|
84
|
+
* - `filter.{dataValues, dataLike, dataILikeOr}` map onto adapter `getEntryVersions` filters.
|
|
85
|
+
* - `limit`/`offset` applied after status pick.
|
|
86
|
+
*
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveEntries(opts: ResolveEntriesOptions): Promise<Entry[]>;
|
|
90
|
+
/**
|
|
91
|
+
* Count entries matching the same filters as `resolveEntries`, without populating.
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
export declare function countEntries(opts: CountEntriesOptions): Promise<number>;
|