includio-cms 0.7.2 → 0.13.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 (164) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/ROADMAP.md +40 -2
  3. package/dist/admin/api/generate-styles.d.ts +2 -0
  4. package/dist/admin/api/generate-styles.js +32 -0
  5. package/dist/admin/api/handler.js +33 -0
  6. package/dist/admin/api/media-gc.js +10 -4
  7. package/dist/admin/api/rest/handler.js +17 -0
  8. package/dist/admin/api/rest/routes/collections.js +25 -13
  9. package/dist/admin/api/rest/routes/entries.d.ts +1 -1
  10. package/dist/admin/api/rest/routes/entries.js +10 -10
  11. package/dist/admin/api/rest/routes/media.d.ts +2 -0
  12. package/dist/admin/api/rest/routes/media.js +9 -0
  13. package/dist/admin/api/rest/routes/schema.d.ts +5 -0
  14. package/dist/admin/api/rest/routes/schema.js +152 -0
  15. package/dist/admin/api/rest/routes/singletons.d.ts +1 -1
  16. package/dist/admin/api/rest/routes/singletons.js +8 -7
  17. package/dist/admin/api/rest/routes/upload.d.ts +2 -0
  18. package/dist/admin/api/rest/routes/upload.js +28 -0
  19. package/dist/admin/api/upload.js +13 -0
  20. package/dist/admin/client/collection/collection-entries.svelte +19 -6
  21. package/dist/admin/client/entry/entry.svelte +21 -23
  22. package/dist/admin/client/entry/header/a11y-validator.js +2 -2
  23. package/dist/admin/client/entry/header/publish-panel.svelte +33 -85
  24. package/dist/admin/client/entry/header/status-badge.svelte +2 -2
  25. package/dist/admin/client/entry/header/version-history-sheet.svelte +9 -9
  26. package/dist/admin/client/entry/header/visibility.svelte +16 -10
  27. package/dist/admin/client/entry/utils.d.ts +3 -0
  28. package/dist/admin/client/entry/utils.js +22 -4
  29. package/dist/admin/client/form/form-submission/form-submission-page.svelte +4 -1
  30. package/dist/admin/client/form/form-submission/submission-field.svelte +10 -0
  31. package/dist/admin/client/index.d.ts +1 -0
  32. package/dist/admin/client/index.js +1 -0
  33. package/dist/admin/client/maintenance/maintenance-page.svelte +146 -2
  34. package/dist/admin/components/fields/blocks-field.svelte +9 -10
  35. package/dist/admin/components/fields/field-renderer.svelte +4 -8
  36. package/dist/admin/components/fields/object-field.svelte +7 -12
  37. package/dist/admin/components/fields/select-field.svelte +8 -2
  38. package/dist/admin/components/fields/seo-field.svelte +40 -93
  39. package/dist/admin/components/fields/simple-array-field.svelte +5 -5
  40. package/dist/admin/components/fields/text-field-wrapper.svelte +52 -197
  41. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  42. package/dist/admin/components/fields/url-field-wrapper.svelte +15 -25
  43. package/dist/admin/components/fields/url-field.svelte +61 -72
  44. package/dist/admin/components/media/file-upload.svelte +5 -1
  45. package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
  46. package/dist/admin/components/media/media-library.svelte +109 -37
  47. package/dist/admin/components/media/media-selector.svelte +79 -11
  48. package/dist/admin/components/media/tag-sidebar.svelte +10 -6
  49. package/dist/admin/components/media/tag-sidebar.svelte.d.ts +7 -2
  50. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +21 -93
  51. package/dist/admin/components/tiptap/inline-block-node.js +6 -5
  52. package/dist/admin/components/tiptap/link-dialog.svelte +10 -11
  53. package/dist/admin/components/tiptap/slash-command.js +1 -1
  54. package/dist/admin/remote/entry.remote.d.ts +2 -5
  55. package/dist/admin/remote/entry.remote.js +22 -27
  56. package/dist/admin/remote/media.remote.d.ts +15 -0
  57. package/dist/admin/remote/media.remote.js +18 -2
  58. package/dist/admin/remote/preview.remote.js +3 -1
  59. package/dist/admin/utils/entryLabel.js +9 -6
  60. package/dist/admin/utils/translationStatus.js +1 -2
  61. package/dist/cli/scaffold/admin.js +34 -2
  62. package/dist/cms/runtime/api.d.ts +16 -12
  63. package/dist/cms/runtime/api.js +7 -6
  64. package/dist/cms/runtime/remote.js +2 -2
  65. package/dist/cms/runtime/schemas.d.ts +1 -1
  66. package/dist/cms/runtime/schemas.js +1 -1
  67. package/dist/cms/runtime/types.d.ts +118 -112
  68. package/dist/cms/runtime/types.js +0 -12
  69. package/dist/core/cms.d.ts +3 -1
  70. package/dist/core/cms.js +30 -0
  71. package/dist/core/fields/fieldSchemaToTs.js +9 -15
  72. package/dist/core/fields/formFieldSchemaToTs.js +7 -0
  73. package/dist/core/server/entries/operations/create.js +10 -4
  74. package/dist/core/server/entries/operations/get.d.ts +1 -0
  75. package/dist/core/server/entries/operations/get.js +186 -191
  76. package/dist/core/server/entries/operations/update.d.ts +6 -7
  77. package/dist/core/server/entries/operations/update.js +20 -38
  78. package/dist/core/server/fields/populateEntry.js +16 -52
  79. package/dist/core/server/fields/resolveImageFields.js +69 -120
  80. package/dist/core/server/fields/resolveRelationFields.js +30 -51
  81. package/dist/core/server/fields/resolveRichtextLinks.js +46 -100
  82. package/dist/core/server/fields/resolveTypographyOrphans.bench.d.ts +1 -0
  83. package/dist/core/server/fields/resolveTypographyOrphans.bench.js +87 -0
  84. package/dist/core/server/fields/resolveTypographyOrphans.d.ts +3 -0
  85. package/dist/core/server/fields/resolveTypographyOrphans.js +128 -0
  86. package/dist/core/server/fields/resolveUrlFields.js +47 -56
  87. package/dist/core/server/fields/utils/fixOrphans.d.ts +5 -0
  88. package/dist/core/server/fields/utils/fixOrphans.js +12 -0
  89. package/dist/core/server/fields/utils/imageStyles.d.ts +4 -2
  90. package/dist/core/server/fields/utils/imageStyles.js +41 -25
  91. package/dist/core/server/fields/utils/resolveMedia.js +1 -6
  92. package/dist/core/server/forms/submissions/operations/delete.js +26 -2
  93. package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +2 -0
  94. package/dist/core/server/forms/submissions/utils/parseMultipart.js +75 -0
  95. package/dist/core/server/generator/fields.d.ts +6 -0
  96. package/dist/core/server/generator/fields.js +43 -5
  97. package/dist/core/server/generator/formFieldSchemaToString.js +10 -0
  98. package/dist/core/server/generator/formFields.js +1 -0
  99. package/dist/core/server/generator/generator.js +98 -30
  100. package/dist/core/server/media/operations/getFiles.d.ts +5 -0
  101. package/dist/core/server/media/operations/getFiles.js +6 -0
  102. package/dist/core/server/media/operations/uploadPrivateFile.d.ts +4 -0
  103. package/dist/core/server/media/operations/uploadPrivateFile.js +8 -0
  104. package/dist/core/server/media/styles/operations/batchGenerateStyles.d.ts +16 -0
  105. package/dist/core/server/media/styles/operations/batchGenerateStyles.js +144 -0
  106. package/dist/db-postgres/index.js +303 -37
  107. package/dist/db-postgres/schema/entry.d.ts +0 -94
  108. package/dist/db-postgres/schema/entry.js +0 -6
  109. package/dist/db-postgres/schema/entryVersion.d.ts +17 -0
  110. package/dist/db-postgres/schema/entryVersion.js +1 -0
  111. package/dist/entity/index.d.ts +9 -4
  112. package/dist/entity/index.js +24 -24
  113. package/dist/files-local/index.js +43 -0
  114. package/dist/paraglide/messages/_index.d.ts +36 -3
  115. package/dist/paraglide/messages/_index.js +71 -3
  116. package/dist/paraglide/messages/en.d.ts +5 -0
  117. package/dist/paraglide/messages/en.js +14 -0
  118. package/dist/paraglide/messages/pl.d.ts +5 -0
  119. package/dist/paraglide/messages/pl.js +14 -0
  120. package/dist/sveltekit/components/preview.svelte +2 -326
  121. package/dist/sveltekit/components/preview.svelte.d.ts +5 -16
  122. package/dist/sveltekit/server/index.d.ts +2 -1
  123. package/dist/sveltekit/server/index.js +2 -1
  124. package/dist/sveltekit/server/preview.js +4 -7
  125. package/dist/types/adapters/db.d.ts +15 -1
  126. package/dist/types/adapters/files.d.ts +6 -0
  127. package/dist/types/cms.d.ts +5 -0
  128. package/dist/types/entries.d.ts +54 -18
  129. package/dist/types/fields.d.ts +14 -24
  130. package/dist/types/formFields.d.ts +7 -2
  131. package/dist/types/index.d.ts +2 -2
  132. package/dist/types/structured-content.d.ts +5 -0
  133. package/dist/updates/0.10.0/index.d.ts +2 -0
  134. package/dist/updates/0.10.0/index.js +15 -0
  135. package/dist/updates/0.11.0/index.d.ts +2 -0
  136. package/dist/updates/0.11.0/index.js +12 -0
  137. package/dist/updates/0.12.0/index.d.ts +2 -0
  138. package/dist/updates/0.12.0/index.js +12 -0
  139. package/dist/updates/0.13.0/index.d.ts +2 -0
  140. package/dist/updates/0.13.0/index.js +10 -0
  141. package/dist/updates/0.7.3/index.d.ts +2 -0
  142. package/dist/updates/0.7.3/index.js +10 -0
  143. package/dist/updates/0.8.0/index.d.ts +2 -0
  144. package/dist/updates/0.8.0/index.js +18 -0
  145. package/dist/updates/0.8.0/migrate.d.ts +2 -0
  146. package/dist/updates/0.8.0/migrate.js +101 -0
  147. package/dist/updates/0.9.0/index.d.ts +2 -0
  148. package/dist/updates/0.9.0/index.js +38 -0
  149. package/dist/updates/index.js +8 -1
  150. package/package.json +7 -6
  151. package/dist/admin/components/fields/image-field.svelte +0 -198
  152. package/dist/admin/components/fields/image-field.svelte.d.ts +0 -8
  153. package/dist/admin/components/fields/richtext-field.svelte +0 -13
  154. package/dist/admin/components/fields/richtext-field.svelte.d.ts +0 -8
  155. package/dist/admin/components/tiptap.svelte +0 -11
  156. package/dist/admin/components/tiptap.svelte.d.ts +0 -6
  157. package/dist/core/server/entries/utils/getEntryTranslation.d.ts +0 -1
  158. package/dist/core/server/entries/utils/getEntryTranslation.js +0 -18
  159. package/dist/paraglide/messages/hello_world.d.ts +0 -5
  160. package/dist/paraglide/messages/hello_world.js +0 -33
  161. package/dist/paraglide/messages/login_hello.d.ts +0 -16
  162. package/dist/paraglide/messages/login_hello.js +0 -34
  163. package/dist/paraglide/messages/login_please_login.d.ts +0 -16
  164. package/dist/paraglide/messages/login_please_login.js +0 -34
@@ -26,114 +26,44 @@
26
26
 
27
27
  const SKIP_TYPES: Set<FieldType> = new Set(['slug', 'seo']);
28
28
 
29
+ /** Ensure blockData has default values for all fields (no per-lang wrapping — data is flat) */
29
30
  function normalizeBlockData(
30
31
  data: Record<string, unknown>,
31
- fields: Field[],
32
- langs: string[]
32
+ fields: Field[]
33
33
  ): Record<string, unknown> {
34
34
  const result = { ...data };
35
35
  for (const f of fields) {
36
36
  const key = f.slug;
37
- if (['text', 'richtext', 'content'].includes(f.type)) {
38
- const val = result[key];
39
- const needsWrap =
40
- val == null ||
41
- typeof val !== 'object' ||
42
- (f.type === 'content' && 'type' in (val as Record<string, unknown>));
43
- if (needsWrap) {
44
- const fallback = val ?? (f.type === 'content' ? null : '');
45
- result[key] = Object.fromEntries(langs.map((l) => [l, fallback]));
46
- }
37
+ if (['text', 'richtext'].includes(f.type)) {
38
+ if (result[key] == null) result[key] = '';
39
+ } else if (f.type === 'content') {
40
+ // Leave as-is (null or doc)
47
41
  } else if (f.type === 'url') {
48
42
  const v = result[key] as Record<string, unknown> | undefined;
49
43
  if (!v || typeof v !== 'object') {
50
- result[key] = { id: '', url: {} };
51
- } else if (!('url' in v) || v.url == null) {
52
- result[key] = { ...v, url: {} };
53
- } else if (typeof v.url === 'string') {
54
- const urlStr = v.url as string;
55
- const textStr = typeof v.text === 'string' ? v.text : '';
56
- result[key] = {
57
- ...v,
58
- url: Object.fromEntries(langs.map((l) => [l, urlStr])),
59
- text: Object.fromEntries(langs.map((l) => [l, textStr]))
60
- };
44
+ result[key] = { id: '', url: '' };
45
+ } else {
46
+ if (typeof v.url !== 'string') v.url = '';
61
47
  }
62
48
  const urlField = f as import('../../../types/fields.js').UrlField;
63
49
  const d = result[key] as Record<string, unknown>;
64
- if (urlField.text && !d.text) d.text = {};
50
+ if (urlField.text && d.text == null) d.text = '';
65
51
  if (urlField.newTab && d.newTab === undefined) d.newTab = false;
66
52
  if (urlField.rel && d.rel === undefined) d.rel = '';
67
53
  } else if (f.type === 'blocks') {
68
54
  const bf = f as BlocksField;
69
55
  if (Array.isArray(result[key])) {
70
- result[key] = (result[key] as any[]).map((item) => ({
71
- ...item,
72
- data: normalizeBlockData(
73
- item.data ?? {},
74
- bf.of.find((d: ObjectField) => d.slug === item.slug)?.fields ?? [],
75
- langs
76
- )
77
- }));
78
- }
79
- } else if (f.type === 'object') {
80
- const of_ = f as ObjectField;
81
- if (result[key] && typeof result[key] === 'object' && 'data' in (result[key] as object)) {
82
- const obj = result[key] as Record<string, unknown>;
83
- result[key] = { ...obj, data: normalizeBlockData(obj.data as Record<string, unknown>, of_.fields, langs) };
84
- }
85
- }
86
- }
87
- return result;
88
- }
89
-
90
- function denormalizeBlockData(
91
- data: Record<string, unknown>,
92
- fields: Field[],
93
- currentLang: string
94
- ): Record<string, unknown> {
95
- const result = { ...data };
96
- for (const f of fields) {
97
- const key = f.slug;
98
- const val = result[key];
99
- if (['text', 'richtext', 'content'].includes(f.type)) {
100
- if (val && typeof val === 'object' && !Array.isArray(val)) {
101
- result[key] = (val as Record<string, unknown>)[currentLang] ?? (f.type === 'content' ? null : '');
102
- }
103
- } else if (f.type === 'url' && val && typeof val === 'object') {
104
- const v = val as Record<string, unknown>;
105
- const flat: Record<string, unknown> = {};
106
- if (v.id !== undefined) flat.id = v.id;
107
- flat.url =
108
- v.url && typeof v.url === 'object'
109
- ? (v.url as Record<string, string>)[currentLang] ?? ''
110
- : v.url ?? '';
111
- if ('text' in v) {
112
- flat.text =
113
- v.text && typeof v.text === 'object'
114
- ? (v.text as Record<string, string>)[currentLang] ?? ''
115
- : v.text ?? '';
116
- }
117
- if (v.newTab !== undefined) flat.newTab = v.newTab;
118
- if (v.rel !== undefined) flat.rel = v.rel;
119
- result[key] = flat;
120
- } else if (f.type === 'blocks') {
121
- const bf = f as BlocksField;
122
- if (Array.isArray(val)) {
123
- result[key] = (val as any[]).map((item) => ({
124
- ...item,
125
- data: denormalizeBlockData(
126
- item.data ?? {},
127
- bf.of.find((d: ObjectField) => d.slug === item.slug)?.fields ?? [],
128
- currentLang
56
+ result[key] = (result[key] as any[]).map((item) =>
57
+ normalizeBlockData(
58
+ item,
59
+ bf.of.find((d: ObjectField) => d.slug === item._slug)?.fields ?? []
129
60
  )
130
- }));
61
+ );
131
62
  }
132
63
  } else if (f.type === 'object') {
133
64
  const of_ = f as ObjectField;
134
- if (val && typeof val === 'object' && 'data' in (val as object)) {
135
- const obj = val as Record<string, unknown>;
136
- result[key] = { ...obj, data: denormalizeBlockData(obj.data as Record<string, unknown>, of_.fields, currentLang) };
65
+ if (result[key] && typeof result[key] === 'object') {
66
+ result[key] = normalizeBlockData(result[key] as Record<string, unknown>, of_.fields);
137
67
  }
138
68
  }
139
69
  }
@@ -145,7 +75,7 @@
145
75
  );
146
76
 
147
77
  const blockDef = $derived(inlineBlocks.find((b) => b.slug === node.attrs.blockType));
148
- const blockLabel = $derived(blockDef?.label ? (Object.values(blockDef.label)[0] ?? blockDef.slug) : node.attrs.blockType);
78
+ const blockLabel = $derived(blockDef?.label ? (typeof blockDef.label === 'string' ? blockDef.label : Object.values(blockDef.label)[0] ?? blockDef.slug) : node.attrs.blockType);
149
79
  const supportedFields = $derived(blockDef?.fields.filter((f) => !SKIP_TYPES.has(f.type)) ?? []);
150
80
 
151
81
  function parseBlockData(raw: unknown): Record<string, unknown> {
@@ -156,9 +86,8 @@
156
86
  return {};
157
87
  }
158
88
 
159
- const langs = contentLanguage.all;
160
89
  const allFields = blockDef?.fields ?? [];
161
- const standaloneForm = createStandaloneForm(normalizeBlockData(parseBlockData(node.attrs.blockData), allFields, langs));
90
+ const standaloneForm = createStandaloneForm(normalizeBlockData(parseBlockData(node.attrs.blockData), allFields));
162
91
  const formStore = standaloneForm.form;
163
92
 
164
93
  // Track last JSON we wrote to PM to avoid re-parsing our own writes
@@ -169,8 +98,7 @@
169
98
  const unsubscribe = formStore.subscribe((data) => {
170
99
  if (debounceTimer) clearTimeout(debounceTimer);
171
100
  debounceTimer = setTimeout(() => {
172
- const denormalized = denormalizeBlockData(data, allFields, contentLanguage.current);
173
- const json = JSON.stringify(denormalized);
101
+ const json = JSON.stringify(data);
174
102
  if (json !== lastWrittenJson) {
175
103
  lastWrittenJson = json;
176
104
  updateAttributes({ blockData: json });
@@ -188,7 +116,7 @@
188
116
  const raw = node.attrs.blockData;
189
117
  const rawJson = typeof raw === 'string' ? raw : JSON.stringify(raw);
190
118
  if (rawJson !== lastWrittenJson) {
191
- formStore.set(normalizeBlockData(parseBlockData(raw), allFields, langs));
119
+ formStore.set(normalizeBlockData(parseBlockData(raw), allFields));
192
120
  }
193
121
  });
194
122
 
@@ -25,7 +25,6 @@ function getFieldDefault(field) {
25
25
  return field.defaultValue;
26
26
  switch (field.type) {
27
27
  case 'text':
28
- case 'richtext':
29
28
  case 'date':
30
29
  case 'datetime':
31
30
  return '';
@@ -40,7 +39,6 @@ function getFieldDefault(field) {
40
39
  return [];
41
40
  case 'url':
42
41
  return { url: '' };
43
- case 'image':
44
42
  case 'file':
45
43
  case 'media':
46
44
  case 'relation':
@@ -119,8 +117,11 @@ export const InlineBlockNode = Node.create({
119
117
  };
120
118
  },
121
119
  addNodeView() {
122
- return SvelteNodeViewRenderer(InlineBlockNodeView, {
123
- context: this.options.context
124
- });
120
+ const Component = InlineBlockNodeView;
121
+ const baseContext = this.options.context;
122
+ return (props) => {
123
+ const context = baseContext ? new Map(baseContext) : undefined;
124
+ return SvelteNodeViewRenderer(Component, { context })(props);
125
+ };
125
126
  }
126
127
  });
@@ -22,11 +22,10 @@
22
22
  onOpenChange?: (open: boolean) => void;
23
23
  };
24
24
 
25
- type EntryWithSeo = Entry & {
26
- data: {
27
- seo?: SeoFieldData;
28
- [key: string]: unknown;
29
- };
25
+ type EntryWithSeo = {
26
+ _id: string;
27
+ _slug: string;
28
+ seo?: SeoFieldData;
30
29
  };
31
30
 
32
31
  let { open = $bindable(false), editor, onOpenChange }: Props = $props();
@@ -102,16 +101,16 @@
102
101
  ]) as [EntryWithSeo[], EntryWithSeo[]];
103
102
  const combined = [...slugResults, ...titleResults];
104
103
  const deduped = combined.filter(
105
- (entry, index, self) => index === self.findIndex((e) => e.id === entry.id)
104
+ (entry, index, self) => index === self.findIndex((e) => e._id === entry._id)
106
105
  );
107
106
  suggestions = deduped.slice(0, 5);
108
107
  }, 300);
109
108
  }
110
109
 
111
110
  function selectEntry(entry: EntryWithSeo) {
112
- entryId = entry.id;
111
+ entryId = entry._id;
113
112
  linkedEntry = entry;
114
- url = entry.data.seo?.slug || url;
113
+ url = entry.seo?.slug || url;
115
114
  suggestions = [];
116
115
  }
117
116
 
@@ -184,8 +183,8 @@
184
183
  onclick={unlinkEntry}
185
184
  >
186
185
  <LinkIcon class="text-muted-foreground h-4 w-4 shrink-0" />
187
- <span class="truncate font-medium">{linkedEntry.data.seo?.title || linkedEntry.id}</span>
188
- <span class="text-muted-foreground truncate text-xs">{linkedEntry.data.seo?.slug}</span>
186
+ <span class="truncate font-medium">{linkedEntry.seo?.title || linkedEntry._id}</span>
187
+ <span class="text-muted-foreground truncate text-xs">{linkedEntry.seo?.slug}</span>
189
188
  <X class="ml-auto h-3.5 w-3.5 shrink-0 opacity-50" />
190
189
  </button>
191
190
  {/if}
@@ -210,7 +209,7 @@
210
209
  >
211
210
  <Item.Content>
212
211
  <Item.Title>
213
- {suggestion.data.seo?.title || 'Bez tytułu'} ({suggestion.data.seo?.slug})
212
+ {suggestion.seo?.title || 'Bez tytułu'} ({suggestion.seo?.slug})
214
213
  </Item.Title>
215
214
  </Item.Content>
216
215
  </Item.Root>
@@ -66,7 +66,7 @@ function getStandardItems(editor) {
66
66
  function getInlineBlockItems(inlineBlocks, editor) {
67
67
  return inlineBlocks.map((block) => ({
68
68
  id: `block-${block.slug}`,
69
- label: block.label ? Object.values(block.label)[0] : block.slug,
69
+ label: block.label ? (typeof block.label === 'string' ? block.label : Object.values(block.label)[0]) : block.slug,
70
70
  group: 'Bloki',
71
71
  action: () => {
72
72
  editor.commands.insertInlineBlock({ blockType: block.slug });
@@ -56,18 +56,15 @@ export declare const getRawEntry: import("@sveltejs/kit").RemoteQueryFunction<{
56
56
  export declare const getEntryForEntryPage: import("@sveltejs/kit").RemoteQueryFunction<string, RawEntry>;
57
57
  export declare const updateEntryVersionCommand: import("@sveltejs/kit").RemoteCommand<{
58
58
  entryId: string;
59
+ lang: string;
59
60
  data: Record<string, unknown>;
60
61
  type: "draft" | "published-now" | "published-scheduled" | "cancel-published";
61
62
  scheduledAt?: Date | undefined;
62
- }, Promise<void | import("../../types/entries.js").DbEntryVersion>>;
63
+ }, Promise<import("../../types/entries.js").DbEntryVersion | undefined>>;
63
64
  export declare const updateEntryCommand: import("@sveltejs/kit").RemoteCommand<{
64
65
  id: string;
65
66
  data: {
66
- availableLocales?: string[] | undefined;
67
67
  archivedAt?: Date | null | undefined;
68
- publishedAt?: Date | null | undefined;
69
- publishedVersionId?: string | null | undefined;
70
- publishedBy?: string | null | undefined;
71
68
  sortOrder?: number | null | undefined;
72
69
  };
73
70
  }, Promise<import("../../types/entries.js").DbEntry>>;
@@ -2,7 +2,7 @@ import { command, query } from '$app/server';
2
2
  import { createEntry as createEntryOperation, createEntrySchema, createEntryVersion } from '../../core/server/entries/operations/create.js';
3
3
  import { getRawEntries as getRawEntriesOperation, countRawEntries as countRawEntriesOperation, getRawEntry as getRawEntryOperation, getRawEntryOrThrow, getDbEntry, getDbEntryOrThrow, getEntries as getEntriesOperation, getEntry as getEntryOperation, getEntryVersion as getEntryVersionOperation, getEntryLabels as getEntryLabelsOperation } from '../../core/server/entries/operations/get.js';
4
4
  import { getCMS } from '../../core/cms.js';
5
- import { pruneOldDraftVersions, unpublishEntry, upsertDraftVersion, updateEntry, updateEntrySchema, updateEntryVersionCommandTypes } from '../../core/server/entries/operations/update.js';
5
+ import { pruneOldDraftVersions, unpublishEntryLang, upsertDraftVersion, updateEntry, updateEntrySchema, updateEntryVersionCommandTypes } from '../../core/server/entries/operations/update.js';
6
6
  import z from 'zod';
7
7
  import { requireAuth } from './middleware/auth.js';
8
8
  import { entryStatuses } from '../../types/entries.js';
@@ -101,6 +101,7 @@ export const getEntryForEntryPage = query(z.string(), async (id) => {
101
101
  });
102
102
  const updateEntryVersionCommandSchema = z.object({
103
103
  entryId: z.string().uuid(),
104
+ lang: z.string(),
104
105
  data: z.record(z.string(), z.unknown()),
105
106
  type: z.enum(updateEntryVersionCommandTypes),
106
107
  scheduledAt: z.date().optional()
@@ -110,49 +111,39 @@ export const updateEntryVersionCommand = command(updateEntryVersionCommandSchema
110
111
  let result;
111
112
  switch (input.type) {
112
113
  case 'draft':
113
- result = await upsertDraftVersion(input.entryId, input.data, { skipValidation: true });
114
+ result = await upsertDraftVersion(input.entryId, input.data, input.lang, { skipValidation: true });
114
115
  break;
115
116
  case 'published-now': {
116
- const existingEntry = await getDbEntryOrThrow({ id: input.entryId });
117
- // Dual-write: still set version.publishedAt for backward compat
117
+ // Create a new published version for this language
118
118
  result = await createEntryVersion({
119
- ...input,
119
+ entryId: input.entryId,
120
+ lang: input.lang,
121
+ data: input.data,
120
122
  publishedAt: new Date(),
121
123
  publishedBy: user.id
122
124
  });
123
- // Update entry-level publish state
124
- await updateEntry(input.entryId, {
125
- publishedVersionId: result.id,
126
- publishedBy: user.id,
127
- publishedAt: existingEntry.publishedAt ?? new Date()
128
- });
129
125
  break;
130
126
  }
131
127
  case 'published-scheduled': {
132
128
  if (!input.scheduledAt) {
133
129
  throw new Error('scheduledAt is required for scheduled publishing');
134
130
  }
135
- // Dual-write: still set version.publishedAt for backward compat
136
131
  result = await createEntryVersion({
137
- ...input,
132
+ entryId: input.entryId,
133
+ lang: input.lang,
134
+ data: input.data,
138
135
  publishedAt: input.scheduledAt,
139
136
  publishedBy: user.id
140
137
  });
141
- // Update entry-level publish state
142
- await updateEntry(input.entryId, {
143
- publishedVersionId: result.id,
144
- publishedBy: user.id,
145
- publishedAt: input.scheduledAt
146
- });
147
138
  break;
148
139
  }
149
140
  case 'cancel-published':
150
- result = await unpublishEntry(input.entryId);
141
+ await unpublishEntryLang(input.entryId, input.lang);
151
142
  break;
152
143
  }
153
144
  // Prune old draft versions only after publish/schedule (drafts upsert in-place)
154
145
  if (input.type !== 'draft') {
155
- await pruneOldDraftVersions(input.entryId);
146
+ await pruneOldDraftVersions(input.entryId, input.lang);
156
147
  // Refresh cached entry so UI reactively updates (status badge, version history)
157
148
  await getEntryForEntryPage(input.entryId).refresh();
158
149
  }
@@ -216,8 +207,9 @@ export const getRecentEntries = query(z.number().default(6), async (limit) => {
216
207
  let label = null;
217
208
  if (config && config.type === 'collection' && config.entryAdminTitle && latestVersion) {
218
209
  const titleData = latestVersion.data[config.entryAdminTitle];
219
- if (titleData && typeof titleData === 'object') {
220
- label = String(Object.values(titleData)[0] || '');
210
+ // Data is flat titleData is the string directly
211
+ if (typeof titleData === 'string') {
212
+ label = titleData || '';
221
213
  }
222
214
  }
223
215
  if (config && config.type === 'single' && config.label) {
@@ -237,9 +229,11 @@ export const getRecentEntries = query(z.number().default(6), async (limit) => {
237
229
  collectionLabel = config.label;
238
230
  }
239
231
  }
240
- const status = entry.publishedVersionId
232
+ const hasPublished = Object.values(entry.publishedVersions).some((v) => v != null);
233
+ const hasScheduled = Object.values(entry.scheduledVersions).some((v) => v != null);
234
+ const status = hasPublished
241
235
  ? 'published'
242
- : entry.scheduledVersion
236
+ : hasScheduled
243
237
  ? 'scheduled'
244
238
  : 'draft';
245
239
  return {
@@ -262,8 +256,9 @@ export const getRecentActivity = query(z.number().default(10), async (limit) =>
262
256
  let label = null;
263
257
  if (config && config.type === 'collection' && config.entryAdminTitle) {
264
258
  const titleData = latestVersion.data[config.entryAdminTitle];
265
- if (titleData && typeof titleData === 'object') {
266
- label = String(Object.values(titleData)[0] || '');
259
+ // Data is flat titleData is the string directly
260
+ if (typeof titleData === 'string') {
261
+ label = titleData || null;
267
262
  }
268
263
  }
269
264
  if (config && config.type === 'single' && config.label) {
@@ -14,8 +14,23 @@ export declare const getMediaFiles: import("@sveltejs/kit").RemoteQueryFunction<
14
14
  tagIds?: string[] | undefined;
15
15
  mimeTypes?: string[] | undefined;
16
16
  search?: string | undefined;
17
+ untagged?: boolean | undefined;
18
+ limit?: number | undefined;
19
+ offset?: number | undefined;
17
20
  };
18
21
  }, import("../../types/media.js").MediaFile[]>;
22
+ export declare const countMediaFiles: import("@sveltejs/kit").RemoteQueryFunction<{
23
+ data: {
24
+ tagIds?: string[] | undefined;
25
+ mimeTypes?: string[] | undefined;
26
+ search?: string | undefined;
27
+ untagged?: boolean | undefined;
28
+ };
29
+ }, number>;
30
+ export declare const getMediaTagsWithCounts: import("@sveltejs/kit").RemoteQueryFunction<void, {
31
+ tag: import("../../types/media.js").MediaTag;
32
+ count: number;
33
+ }[]>;
19
34
  export declare const getFileById: import("@sveltejs/kit").RemoteQueryFunction<string, import("../../types/media.js").MediaFile | null>;
20
35
  export declare const deleteMediaFile: import("@sveltejs/kit").RemoteCommand<string, Promise<void>>;
21
36
  export declare const bulkDeleteMediaFiles: import("@sveltejs/kit").RemoteCommand<{
@@ -2,7 +2,7 @@ import { command, query } from '$app/server';
2
2
  import { setAlt, renameMediaFile as renameMediaFileOperation, updateMediaAccessibility as updateMediaAccessibilityOp } from '../../core/server/media/operations/updateFile.js';
3
3
  import z from 'zod';
4
4
  import { deleteMediaFile as deleteMediaFileFn, bulkDeleteMediaFiles as bulkDeleteMediaFilesFn } from '../../core/server/media/operations/deleteMediaFile.js';
5
- import { getFile, getFiles } from '../../core/server/media/operations/getFiles.js';
5
+ import { getFile, getFiles, countFiles, getMediaTagsWithCounts as getMediaTagsWithCountsFn } from '../../core/server/media/operations/getFiles.js';
6
6
  import { getMediaTags as getMediaTagsFn, createMediaTag as createMediaTagFn, updateMediaTag as updateMediaTagFn, deleteMediaTag as deleteMediaTagFn, setMediaFileTags as setMediaFileTagsFn, bulkSetMediaFileTags as bulkSetMediaFileTagsFn } from '../../core/server/media/operations/tags.js';
7
7
  import { requireAuth } from './middleware/auth.js';
8
8
  const setMediaFileAltSchema = z.object({
@@ -34,11 +34,27 @@ export const getMediaFiles = query(z.object({
34
34
  ids: z.array(z.string().uuid()).optional(),
35
35
  tagIds: z.array(z.string().uuid()).optional(),
36
36
  mimeTypes: z.array(z.string()).optional(),
37
- search: z.string().optional()
37
+ search: z.string().optional(),
38
+ untagged: z.boolean().optional(),
39
+ limit: z.number().int().positive().optional(),
40
+ offset: z.number().int().nonnegative().optional()
38
41
  })
39
42
  }), async (data) => {
40
43
  return getFiles(data);
41
44
  });
45
+ export const countMediaFiles = query(z.object({
46
+ data: z.object({
47
+ tagIds: z.array(z.string().uuid()).optional(),
48
+ mimeTypes: z.array(z.string()).optional(),
49
+ search: z.string().optional(),
50
+ untagged: z.boolean().optional()
51
+ })
52
+ }), async (data) => {
53
+ return countFiles(data);
54
+ });
55
+ export const getMediaTagsWithCounts = query(async () => {
56
+ return getMediaTagsWithCountsFn();
57
+ });
42
58
  export const getFileById = query(z.string().uuid(), async (id) => {
43
59
  return getFile(id);
44
60
  });
@@ -10,5 +10,7 @@ const schema = z.object({
10
10
  });
11
11
  export const populatePreviewData = command(schema, async ({ data, slug, language }) => {
12
12
  const config = getCMS().getBySlug(slug);
13
- return populateEntryData(data, getFieldsFromConfig(config), language);
13
+ const fields = getFieldsFromConfig(config);
14
+ const populated = await populateEntryData(data, fields, language);
15
+ return populated;
14
16
  });
@@ -1,19 +1,22 @@
1
1
  import { getAtPath } from './objectPath.js';
2
2
  export function getRawCollectionEntryLabel(entry, config, language) {
3
- if (entry.publishedVersion) {
3
+ const publishedVersion = entry.publishedVersions[language];
4
+ if (publishedVersion) {
5
+ // Data is flat — entryAdminTitle value is directly a string
4
6
  return config.entryAdminTitle
5
- ? getAtPath(entry.publishedVersion.data, config.entryAdminTitle)?.[language] || entry.id
7
+ ? String(publishedVersion.data[config.entryAdminTitle] || entry.id)
6
8
  : entry.id;
7
9
  }
8
- if (entry.draftVersion) {
10
+ const draftVersion = entry.draftVersions[language];
11
+ if (draftVersion) {
9
12
  return config.entryAdminTitle
10
- ? getAtPath(entry.draftVersion.data, config.entryAdminTitle)?.[language] || entry.id
13
+ ? String(draftVersion.data[config.entryAdminTitle] || entry.id)
11
14
  : entry.id;
12
15
  }
13
16
  return entry.id;
14
17
  }
15
18
  export function getCollectionEntryLabel(entry, config) {
16
19
  return config.entryAdminTitle
17
- ? getAtPath(entry.data, config.entryAdminTitle) || entry.id
18
- : entry.id;
20
+ ? getAtPath(entry, config.entryAdminTitle) || entry._id
21
+ : entry._id;
19
22
  }
@@ -13,7 +13,6 @@ function isFieldFilled(value, fieldType) {
13
13
  return false;
14
14
  switch (fieldType) {
15
15
  case 'text':
16
- case 'richtext':
17
16
  return typeof value === 'string' && value.length > 0;
18
17
  case 'content': {
19
18
  if (typeof value !== 'object')
@@ -46,7 +45,7 @@ function collectLocalizedFields(fields) {
46
45
  if (field.localized === false)
47
46
  continue;
48
47
  const label = extractLabel(field.label, field.slug);
49
- if (field.type === 'text' || field.type === 'richtext' || field.type === 'content') {
48
+ if (field.type === 'text' || field.type === 'content') {
50
49
  result.push({ slug: field.slug, label, type: field.type, required: !!field.required });
51
50
  }
52
51
  }
@@ -50,6 +50,16 @@ export * from 'includio-cms/admin/remote';
50
50
  </script>
51
51
 
52
52
  <AcceptInvitePage />
53
+ `
54
+ },
55
+ {
56
+ path: 'admin/reset-password/+page.svelte',
57
+ content: `${GENERATED_COMMENT}
58
+ <script lang="ts">
59
+ import { ResetPasswordPage } from 'includio-cms/admin/client';
60
+ </script>
61
+
62
+ <ResetPasswordPage />
53
63
  `
54
64
  },
55
65
  {
@@ -109,13 +119,25 @@ export * from 'includio-cms/admin/remote';
109
119
  `
110
120
  },
111
121
  {
112
- path: 'admin/(afterLogin)/form-submissions/[submissionId]/+page.svelte',
122
+ path: 'admin/(afterLogin)/form-submissions/[id]/+page.server.ts',
123
+ content: `${GENERATED_COMMENT_TS}
124
+ export async function load({ params }) {
125
+ return {
126
+ submissionId: params.id
127
+ };
128
+ }
129
+ `
130
+ },
131
+ {
132
+ path: 'admin/(afterLogin)/form-submissions/[id]/+page.svelte',
113
133
  content: `${GENERATED_COMMENT}
114
134
  <script lang="ts">
115
135
  import { FormSubmissionPage } from 'includio-cms/admin/client';
136
+
137
+ let { data } = $props();
116
138
  </script>
117
139
 
118
- <FormSubmissionPage />
140
+ <FormSubmissionPage {data} />
119
141
  `
120
142
  },
121
143
  {
@@ -138,6 +160,16 @@ export * from 'includio-cms/admin/remote';
138
160
  </script>
139
161
 
140
162
  <AccountPage />
163
+ `
164
+ },
165
+ {
166
+ path: 'admin/(afterLogin)/maintenance/+page.svelte',
167
+ content: `${GENERATED_COMMENT}
168
+ <script lang="ts">
169
+ import { MaintenancePage } from 'includio-cms/admin/client';
170
+ </script>
171
+
172
+ <MaintenancePage />
141
173
  `
142
174
  },
143
175
  {
@@ -1,20 +1,24 @@
1
1
  import type { SingleEntryMap, SingleSlug, CollectionEntryMap, CollectionSlug, FormEntryMap, SiteLanguage } from './types';
2
- interface GetEntryQueryOptions<T extends {
3
- data: Record<string, unknown>;
4
- }> {
2
+ interface GetEntryOptions {
5
3
  id?: string;
6
4
  status?: 'draft' | 'published' | 'scheduled' | 'archived';
7
- dataValues?: Partial<T['data']>;
8
- }
9
- interface GetEntryOptions {
10
- /**
11
- * Language code to fetch the entry in. Defaults to the first language in the CMS config.
12
- */
5
+ dataValues?: Record<string, unknown>;
13
6
  language?: SiteLanguage;
14
7
  }
15
- export declare function getSingleEntry<K extends SingleSlug>(slug: K, data: GetEntryQueryOptions<SingleEntryMap[K]>, options: GetEntryOptions): Promise<SingleEntryMap[K] | null>;
16
- export declare function getCollectionEntry<K extends CollectionSlug>(slug: K, data: GetEntryQueryOptions<CollectionEntryMap[K]>, options: GetEntryOptions): Promise<CollectionEntryMap[K] | null>;
17
- export declare function getCollectionEntries<K extends CollectionSlug>(slug: K, options: GetEntryOptions): Promise<CollectionEntryMap[K][]>;
8
+ interface GetEntriesOptions extends GetEntryOptions {
9
+ ids?: string[];
10
+ dataLike?: Record<string, unknown>;
11
+ orderBy?: {
12
+ column: 'createdAt' | 'updatedAt' | 'sortOrder';
13
+ direction: 'asc' | 'desc';
14
+ };
15
+ limit?: number;
16
+ offset?: number;
17
+ }
18
+ export declare function getSingleEntry<K extends SingleSlug>(slug: K, options?: GetEntryOptions): Promise<SingleEntryMap[K] | null>;
19
+ export declare function getCollectionEntry<K extends CollectionSlug>(slug: K, options?: GetEntryOptions): Promise<CollectionEntryMap[K] | null>;
20
+ export declare function getCollectionEntries<K extends CollectionSlug>(slug: K, options?: GetEntriesOptions): Promise<CollectionEntryMap[K][]>;
21
+ export declare function countCollectionEntries<K extends CollectionSlug>(slug: K, options?: Omit<GetEntriesOptions, 'limit' | 'offset' | 'orderBy'>): Promise<number>;
18
22
  export interface SubmitFormOptions {
19
23
  ip?: string;
20
24
  userAgent?: string;
@@ -1,22 +1,23 @@
1
1
  // This file is auto-generated. Do not edit directly.
2
- import { getEntry, getEntries, createFormSubmission } from 'includio-cms/sveltekit/server';
3
- export async function getSingleEntry(slug, data, options) {
2
+ import { getEntry, getEntries, countEntries, createFormSubmission } from 'includio-cms/sveltekit/server';
3
+ export async function getSingleEntry(slug, options = {}) {
4
4
  return (await getEntry({
5
- ...data,
6
5
  slug,
7
6
  ...options
8
7
  }));
9
8
  }
10
- export async function getCollectionEntry(slug, data, options) {
9
+ export async function getCollectionEntry(slug, options = {}) {
11
10
  return (await getEntry({
12
- ...data,
13
11
  slug,
14
12
  ...options
15
13
  }));
16
14
  }
17
- export async function getCollectionEntries(slug, options) {
15
+ export async function getCollectionEntries(slug, options = {}) {
18
16
  return (await getEntries({ slug, ...options }));
19
17
  }
18
+ export async function countCollectionEntries(slug, options = {}) {
19
+ return countEntries({ slug, ...options });
20
+ }
20
21
  export async function submitForm(slug, data, options) {
21
22
  return createFormSubmission({ slug, data, ...options });
22
23
  }