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.
Files changed (81) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/DOCS.md +1 -1
  3. package/README.md +62 -0
  4. package/dist/admin/api/rest/routes/collections.js +1 -1
  5. package/dist/admin/api/rest/routes/entries.js +1 -1
  6. package/dist/admin/api/rest/routes/singletons.js +1 -1
  7. package/dist/admin/remote/entry.remote.js +20 -3
  8. package/dist/admin/remote/invite.d.ts +1 -1
  9. package/dist/admin/remote/preview.remote.js +10 -2
  10. package/dist/admin/remote/reorder.js +1 -1
  11. package/dist/admin/remote/shop.remote.d.ts +18 -18
  12. package/dist/ai-claude/index.d.ts +9 -0
  13. package/dist/ai-claude/index.js +23 -7
  14. package/dist/ai-openai/index.d.ts +9 -0
  15. package/dist/ai-openai/index.js +28 -9
  16. package/dist/cms/runtime/api.d.ts +10 -6
  17. package/dist/cms/runtime/api.js +7 -7
  18. package/dist/components/ui/accordion/accordion.svelte.d.ts +1 -1
  19. package/dist/components/ui/calendar/calendar.svelte.d.ts +1 -1
  20. package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
  21. package/dist/components/ui/command/command-input.svelte.d.ts +1 -1
  22. package/dist/components/ui/command/command.svelte.d.ts +1 -1
  23. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  24. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  25. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  26. package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +1 -1
  27. package/dist/components/ui/radio-group/radio-group.svelte.d.ts +1 -1
  28. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  29. package/dist/components/ui/tabs/tabs.svelte.d.ts +1 -1
  30. package/dist/components/ui/textarea/textarea.svelte.d.ts +1 -1
  31. package/dist/components/ui/toggle-group/toggle-group-item.svelte.d.ts +1 -1
  32. package/dist/components/ui/toggle-group/toggle-group.svelte.d.ts +1 -1
  33. package/dist/core/server/entries/operations/create.js +1 -1
  34. package/dist/core/server/entries/operations/get.d.ts +20 -16
  35. package/dist/core/server/entries/operations/get.js +45 -214
  36. package/dist/core/server/entries/operations/resolveEntry.d.ts +94 -0
  37. package/dist/core/server/entries/operations/resolveEntry.js +210 -0
  38. package/dist/core/server/entries/operations/update.js +1 -1
  39. package/dist/core/server/fields/populateEntry.d.ts +9 -1
  40. package/dist/core/server/fields/populateEntry.js +22 -18
  41. package/dist/core/server/fields/resolveRelationFields.d.ts +2 -1
  42. package/dist/core/server/fields/resolveRelationFields.js +140 -34
  43. package/dist/core/server/fields/resolveRichtextLinks.d.ts +2 -1
  44. package/dist/core/server/fields/resolveRichtextLinks.js +2 -1
  45. package/dist/core/server/fields/resolveUrlFields.d.ts +2 -1
  46. package/dist/core/server/fields/resolveUrlFields.js +6 -5
  47. package/dist/core/server/generator/generator.js +17 -14
  48. package/dist/db-postgres/index.d.ts +4 -0
  49. package/dist/db-postgres/index.js +4 -0
  50. package/dist/email-nodemailer/index.d.ts +9 -0
  51. package/dist/email-nodemailer/index.js +28 -6
  52. package/dist/entity/index.js +1 -1
  53. package/dist/files-local/index.d.ts +4 -0
  54. package/dist/files-local/index.js +4 -0
  55. package/dist/paraglide/messages/_index.d.ts +3 -36
  56. package/dist/paraglide/messages/_index.js +3 -71
  57. package/dist/paraglide/messages/hello_world.d.ts +5 -0
  58. package/dist/paraglide/messages/hello_world.js +33 -0
  59. package/dist/paraglide/messages/login_hello.d.ts +16 -0
  60. package/dist/paraglide/messages/login_hello.js +34 -0
  61. package/dist/paraglide/messages/login_please_login.d.ts +16 -0
  62. package/dist/paraglide/messages/login_please_login.js +34 -0
  63. package/dist/shop/server/populate.d.ts +2 -1
  64. package/dist/shop/server/populate.js +2 -1
  65. package/dist/sveltekit/server/index.d.ts +1 -1
  66. package/dist/sveltekit/server/index.js +1 -1
  67. package/dist/types/adapters/ai.d.ts +8 -0
  68. package/dist/types/adapters/db.d.ts +9 -0
  69. package/dist/types/adapters/email.d.ts +6 -0
  70. package/dist/types/adapters/files.d.ts +5 -0
  71. package/dist/types/plugins.d.ts +6 -2
  72. package/dist/updates/0.18.0/index.d.ts +2 -0
  73. package/dist/updates/0.18.0/index.js +78 -0
  74. package/dist/updates/0.19.0/index.d.ts +2 -0
  75. package/dist/updates/0.19.0/index.js +40 -0
  76. package/dist/updates/index.js +3 -1
  77. package/package.json +14 -5
  78. package/dist/paraglide/messages/en.d.ts +0 -5
  79. package/dist/paraglide/messages/en.js +0 -14
  80. package/dist/paraglide/messages/pl.d.ts +0 -5
  81. 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, {}, "value" | "ref">;
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" | "value" | "ref">;
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
- }, {}, "value" | "ref">;
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">, {}, "value" | "ref">;
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, {}, "value" | "ref">;
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
- }, {}, "value" | "ref">;
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, {}, "value" | "ref">;
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">, {}, "value" | "ref">;
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, {}, "value" | "ref">;
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, {}, "value" | "ref">;
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, GetEntriesOptions, GetEntryOptions, 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
- export declare const getEntries: (options?: GetEntriesOptions) => Promise<Entry[]>;
11
- export declare const countEntries: (options: Omit<GetEntriesOptions, "limit" | "offset" | "orderBy">) => Promise<number>;
12
- export declare const getEntry: (options?: GetEntryOptions) => Promise<Entry | null>;
13
- export declare const getEntryOrThrow: (options?: GetEntryOptions) => Promise<Entry>;
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 getDbEntryVersions: (options: GetDbEntryVersionsOptions) => Promise<DbEntryVersion[]>;
26
- export declare const getDbEntryVersion: (options: GetDbEntryVersionOptions) => Promise<DbEntryVersion | null>;
27
- export declare const getDbEntryVersionOrThrow: (options: GetDbEntryVersionOptions) => Promise<DbEntryVersion>;
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 getDbEntries = async (options) => {
5
+ export const _getDbEntries = async (options) => {
7
6
  return getCMS().databaseAdapter.getEntries(options);
8
7
  };
9
- export const countDbEntries = async (options) => {
8
+ export const _countDbEntries = async (options) => {
10
9
  return getCMS().databaseAdapter.countEntries(options);
11
10
  };
12
- export const getDbEntry = async (options) => {
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 getDbEntryOrThrow = async (options) => {
21
- const entry = await getDbEntry(options);
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 countRawEntries = async (options) => {
28
- return countDbEntries(options);
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 getRawEntries = async (options) => {
58
- const dbEntries = await getDbEntries(options);
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 getRawEntry = async (options) => {
82
- const [entry] = await getRawEntries({
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 getRawEntryOrThrow = async (options) => {
90
- const entry = await getRawEntry(options);
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 getDbEntries({
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.map((entry) => {
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
- }).filter((r) => r != null);
314
- // Post-query search filtering (case-insensitive includes)
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 getDbEntryVersions = async (options) => {
146
+ export const _getDbEntryVersions = async (options) => {
327
147
  return getCMS().databaseAdapter.getEntryVersions(options);
328
148
  };
329
- export const getDbEntryVersion = async (options) => {
330
- const [version] = await getDbEntryVersions({
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 getDbEntryVersionOrThrow = async (options) => {
337
- const version = await getDbEntryVersion(options);
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 getDbEntryVersion({ id: options.id });
346
- if (!dbEntryVersion) {
169
+ const dbEntryVersion = await _getDbEntryVersion({ id: options.id });
170
+ if (!dbEntryVersion)
347
171
  return null;
348
- }
349
- const dbEntry = await getDbEntry({ id: dbEntryVersion.entryId });
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 populatedData = await populateEntryData(dbEntryVersion.data, fields, language, dbEntry.id);
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>;