includio-cms 0.7.2 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/ROADMAP.md +54 -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 +35 -13
  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/client/users/users-page.svelte +5 -6
  35. package/dist/admin/client/users/users-page.svelte.d.ts +1 -4
  36. package/dist/admin/components/fields/block-picker-modal.svelte +13 -4
  37. package/dist/admin/components/fields/blocks-field.svelte +40 -19
  38. package/dist/admin/components/fields/field-renderer.svelte +4 -8
  39. package/dist/admin/components/fields/object-field.svelte +7 -12
  40. package/dist/admin/components/fields/select-field.svelte +8 -2
  41. package/dist/admin/components/fields/seo-field.svelte +40 -93
  42. package/dist/admin/components/fields/simple-array-field.svelte +27 -16
  43. package/dist/admin/components/fields/text-field-wrapper.svelte +52 -197
  44. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  45. package/dist/admin/components/fields/url-field-wrapper.svelte +15 -25
  46. package/dist/admin/components/fields/url-field.svelte +61 -72
  47. package/dist/admin/components/layout/layout-renderer.svelte +10 -4
  48. package/dist/admin/components/media/file-preview.svelte +10 -1
  49. package/dist/admin/components/media/file-upload.svelte +5 -1
  50. package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
  51. package/dist/admin/components/media/files-list.svelte +12 -3
  52. package/dist/admin/components/media/media-library.svelte +109 -37
  53. package/dist/admin/components/media/media-selector.svelte +90 -16
  54. package/dist/admin/components/media/tag-sidebar.svelte +10 -6
  55. package/dist/admin/components/media/tag-sidebar.svelte.d.ts +7 -2
  56. package/dist/admin/components/tiptap/FigureNodeView.svelte +15 -10
  57. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +53 -94
  58. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +8 -3
  59. package/dist/admin/components/tiptap/editor-toolbar.svelte +28 -23
  60. package/dist/admin/components/tiptap/image-dialog.svelte +12 -7
  61. package/dist/admin/components/tiptap/inline-block-node.js +6 -5
  62. package/dist/admin/components/tiptap/lang.d.ts +77 -0
  63. package/dist/admin/components/tiptap/lang.js +170 -0
  64. package/dist/admin/components/tiptap/link-dialog.svelte +31 -28
  65. package/dist/admin/components/tiptap/slash-command.js +27 -23
  66. package/dist/admin/components/tiptap/table-dialog.svelte +9 -4
  67. package/dist/admin/components/tiptap/video-dialog.svelte +6 -1
  68. package/dist/admin/remote/email.remote.d.ts +1 -0
  69. package/dist/admin/remote/email.remote.js +5 -0
  70. package/dist/admin/remote/entry.remote.d.ts +2 -5
  71. package/dist/admin/remote/entry.remote.js +23 -28
  72. package/dist/admin/remote/index.d.ts +1 -0
  73. package/dist/admin/remote/index.js +1 -0
  74. package/dist/admin/remote/media.remote.d.ts +15 -0
  75. package/dist/admin/remote/media.remote.js +18 -2
  76. package/dist/admin/remote/preview.remote.js +3 -1
  77. package/dist/admin/utils/entryLabel.js +9 -6
  78. package/dist/admin/utils/translationStatus.js +1 -2
  79. package/dist/cli/scaffold/admin.js +34 -2
  80. package/dist/cms/runtime/api.d.ts +16 -12
  81. package/dist/cms/runtime/api.js +7 -6
  82. package/dist/cms/runtime/remote.js +2 -2
  83. package/dist/cms/runtime/schemas.d.ts +1 -1
  84. package/dist/cms/runtime/schemas.js +1 -1
  85. package/dist/cms/runtime/types.d.ts +118 -112
  86. package/dist/cms/runtime/types.js +0 -12
  87. package/dist/core/cms.d.ts +3 -1
  88. package/dist/core/cms.js +30 -0
  89. package/dist/core/fields/fieldSchemaToTs.js +9 -15
  90. package/dist/core/fields/formFieldSchemaToTs.js +7 -0
  91. package/dist/core/server/entries/operations/create.js +10 -4
  92. package/dist/core/server/entries/operations/get.d.ts +1 -0
  93. package/dist/core/server/entries/operations/get.js +186 -191
  94. package/dist/core/server/entries/operations/update.d.ts +6 -7
  95. package/dist/core/server/entries/operations/update.js +20 -38
  96. package/dist/core/server/fields/populateEntry.js +16 -52
  97. package/dist/core/server/fields/resolveImageFields.js +69 -120
  98. package/dist/core/server/fields/resolveRelationFields.js +30 -51
  99. package/dist/core/server/fields/resolveRichtextLinks.js +46 -100
  100. package/dist/core/server/fields/resolveTypographyOrphans.bench.d.ts +1 -0
  101. package/dist/core/server/fields/resolveTypographyOrphans.bench.js +87 -0
  102. package/dist/core/server/fields/resolveTypographyOrphans.d.ts +3 -0
  103. package/dist/core/server/fields/resolveTypographyOrphans.js +128 -0
  104. package/dist/core/server/fields/resolveUrlFields.js +47 -56
  105. package/dist/core/server/fields/utils/fixOrphans.d.ts +5 -0
  106. package/dist/core/server/fields/utils/fixOrphans.js +12 -0
  107. package/dist/core/server/fields/utils/imageStyles.d.ts +4 -2
  108. package/dist/core/server/fields/utils/imageStyles.js +41 -25
  109. package/dist/core/server/fields/utils/resolveMedia.js +1 -6
  110. package/dist/core/server/forms/submissions/operations/delete.js +26 -2
  111. package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +2 -0
  112. package/dist/core/server/forms/submissions/utils/parseMultipart.js +75 -0
  113. package/dist/core/server/generator/fields.d.ts +6 -0
  114. package/dist/core/server/generator/fields.js +43 -5
  115. package/dist/core/server/generator/formFieldSchemaToString.js +10 -0
  116. package/dist/core/server/generator/formFields.js +1 -0
  117. package/dist/core/server/generator/generator.js +98 -30
  118. package/dist/core/server/media/operations/getFiles.d.ts +5 -0
  119. package/dist/core/server/media/operations/getFiles.js +6 -0
  120. package/dist/core/server/media/operations/uploadPrivateFile.d.ts +4 -0
  121. package/dist/core/server/media/operations/uploadPrivateFile.js +8 -0
  122. package/dist/core/server/media/styles/operations/batchGenerateStyles.d.ts +16 -0
  123. package/dist/core/server/media/styles/operations/batchGenerateStyles.js +144 -0
  124. package/dist/db-postgres/index.js +303 -37
  125. package/dist/db-postgres/schema/entry.d.ts +0 -94
  126. package/dist/db-postgres/schema/entry.js +0 -6
  127. package/dist/db-postgres/schema/entryVersion.d.ts +17 -0
  128. package/dist/db-postgres/schema/entryVersion.js +1 -0
  129. package/dist/entity/index.d.ts +9 -4
  130. package/dist/entity/index.js +24 -24
  131. package/dist/files-local/index.js +43 -0
  132. package/dist/paraglide/messages/_index.d.ts +36 -3
  133. package/dist/paraglide/messages/_index.js +71 -3
  134. package/dist/paraglide/messages/en.d.ts +5 -0
  135. package/dist/paraglide/messages/en.js +14 -0
  136. package/dist/paraglide/messages/pl.d.ts +5 -0
  137. package/dist/paraglide/messages/pl.js +14 -0
  138. package/dist/sveltekit/components/preview.svelte +2 -326
  139. package/dist/sveltekit/components/preview.svelte.d.ts +5 -16
  140. package/dist/sveltekit/server/index.d.ts +2 -1
  141. package/dist/sveltekit/server/index.js +2 -1
  142. package/dist/sveltekit/server/preview.js +4 -7
  143. package/dist/types/adapters/db.d.ts +15 -1
  144. package/dist/types/adapters/files.d.ts +6 -0
  145. package/dist/types/cms.d.ts +5 -0
  146. package/dist/types/entries.d.ts +54 -18
  147. package/dist/types/fields.d.ts +14 -24
  148. package/dist/types/formFields.d.ts +7 -2
  149. package/dist/types/index.d.ts +2 -2
  150. package/dist/types/layout.d.ts +0 -1
  151. package/dist/types/structured-content.d.ts +5 -0
  152. package/dist/updates/0.10.0/index.d.ts +2 -0
  153. package/dist/updates/0.10.0/index.js +15 -0
  154. package/dist/updates/0.11.0/index.d.ts +2 -0
  155. package/dist/updates/0.11.0/index.js +12 -0
  156. package/dist/updates/0.12.0/index.d.ts +2 -0
  157. package/dist/updates/0.12.0/index.js +12 -0
  158. package/dist/updates/0.13.0/index.d.ts +2 -0
  159. package/dist/updates/0.13.0/index.js +10 -0
  160. package/dist/updates/0.13.1/index.d.ts +2 -0
  161. package/dist/updates/0.13.1/index.js +20 -0
  162. package/dist/updates/0.7.3/index.d.ts +2 -0
  163. package/dist/updates/0.7.3/index.js +10 -0
  164. package/dist/updates/0.8.0/index.d.ts +2 -0
  165. package/dist/updates/0.8.0/index.js +18 -0
  166. package/dist/updates/0.8.0/migrate.d.ts +2 -0
  167. package/dist/updates/0.8.0/migrate.js +101 -0
  168. package/dist/updates/0.9.0/index.d.ts +2 -0
  169. package/dist/updates/0.9.0/index.js +38 -0
  170. package/dist/updates/index.js +9 -1
  171. package/package.json +7 -6
  172. package/dist/admin/components/fields/image-field.svelte +0 -198
  173. package/dist/admin/components/fields/image-field.svelte.d.ts +0 -8
  174. package/dist/admin/components/fields/richtext-field.svelte +0 -13
  175. package/dist/admin/components/fields/richtext-field.svelte.d.ts +0 -8
  176. package/dist/admin/components/tiptap.svelte +0 -11
  177. package/dist/admin/components/tiptap.svelte.d.ts +0 -6
  178. package/dist/core/server/entries/utils/getEntryTranslation.d.ts +0 -1
  179. package/dist/core/server/entries/utils/getEntryTranslation.js +0 -18
  180. package/dist/paraglide/messages/hello_world.d.ts +0 -5
  181. package/dist/paraglide/messages/hello_world.js +0 -33
  182. package/dist/paraglide/messages/login_hello.d.ts +0 -16
  183. package/dist/paraglide/messages/login_hello.js +0 -34
  184. package/dist/paraglide/messages/login_please_login.d.ts +0 -16
  185. package/dist/paraglide/messages/login_please_login.js +0 -34
@@ -1,51 +1,9 @@
1
- import { walkInlineBlockNodes } from '../../../admin/components/tiptap/structured-content-utils.js';
2
- import { translateObject } from '../entries/utils/getEntryTranslation.js';
3
1
  import { resolveMediaFields } from './resolveImageFields.js';
4
2
  import { resolveRelationFields } from './resolveRelationFields.js';
5
3
  import { resolveRichtextLinks } from './resolveRichtextLinks.js';
6
4
  import { resolveUrlFields } from './resolveUrlFields.js';
5
+ import { resolveTypographyOrphans } from './resolveTypographyOrphans.js';
7
6
  import { getCMS } from '../../cms.js';
8
- function translateInlineBlockData(data, fields, language) {
9
- for (const field of fields) {
10
- const val = data[field.slug];
11
- if (val == null)
12
- continue;
13
- switch (field.type) {
14
- case 'content': {
15
- const cf = field;
16
- if (cf.inlineBlocks?.length &&
17
- val &&
18
- typeof val === 'object' &&
19
- 'type' in val &&
20
- val.type === 'doc') {
21
- walkInlineBlockNodes(val, (node) => {
22
- if (node.attrs?.blockData && typeof node.attrs.blockData === 'object') {
23
- node.attrs.blockData = translateObject(node.attrs.blockData, language);
24
- const def = cf.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
25
- if (def) {
26
- translateInlineBlockData(node.attrs.blockData, def.fields, language);
27
- }
28
- }
29
- });
30
- }
31
- break;
32
- }
33
- case 'object':
34
- if (val && typeof val === 'object' && 'data' in val)
35
- translateInlineBlockData(val.data, field.fields, language);
36
- break;
37
- case 'blocks':
38
- if (Array.isArray(val)) {
39
- for (const item of val) {
40
- const def = field.of.find((d) => d.slug === item.slug);
41
- if (def)
42
- translateInlineBlockData(item.data, def.fields, language);
43
- }
44
- }
45
- break;
46
- }
47
- }
48
- }
49
7
  async function resolveCustomFields(data, fields) {
50
8
  // Check if any custom fields exist before accessing CMS
51
9
  const hasCustom = fields.some((f) => f.type === 'custom' ||
@@ -72,19 +30,16 @@ async function resolveCustomFields(data, fields) {
72
30
  break;
73
31
  }
74
32
  case 'object':
75
- if (val && typeof val === 'object' && 'data' in val) {
76
- result[field.slug] = {
77
- ...val,
78
- data: await resolveCustomFields(val.data, field.fields)
79
- };
33
+ if (val && typeof val === 'object') {
34
+ result[field.slug] = await resolveCustomFields(val, field.fields);
80
35
  }
81
36
  break;
82
37
  case 'blocks':
83
38
  if (Array.isArray(val)) {
84
39
  result[field.slug] = await Promise.all(val.map(async (item) => {
85
- const blockDef = field.of.find((d) => d.slug === item.slug);
40
+ const blockDef = field.of.find((d) => d.slug === item._slug);
86
41
  if (blockDef) {
87
- return { ...item, data: await resolveCustomFields(item.data, blockDef.fields) };
42
+ return await resolveCustomFields(item, blockDef.fields);
88
43
  }
89
44
  return item;
90
45
  }));
@@ -100,7 +55,16 @@ export async function populateEntryData(data, fields, language) {
100
55
  populatedData = await resolveMediaFields(populatedData, fields);
101
56
  populatedData = await resolveRichtextLinks(populatedData, fields, language);
102
57
  populatedData = (await resolveCustomFields(populatedData, fields));
103
- populatedData = translateObject(populatedData, language);
104
- translateInlineBlockData(populatedData, fields, language);
58
+ // Typography orphan fix — enabled by default, opt-out via config
59
+ let fixOrphans = true;
60
+ try {
61
+ fixOrphans = getCMS().typographyConfig.fixOrphans !== false;
62
+ }
63
+ catch {
64
+ // CMS not initialized — keep default
65
+ }
66
+ if (fixOrphans) {
67
+ populatedData = resolveTypographyOrphans(populatedData, fields);
68
+ }
105
69
  return populatedData;
106
70
  }
@@ -14,7 +14,6 @@ export async function resolveMediaFields(data, fields) {
14
14
  continue;
15
15
  switch (field.type) {
16
16
  case 'media':
17
- case 'image':
18
17
  case 'file': {
19
18
  if (field.multiple && Array.isArray(val)) {
20
19
  val.forEach((id) => {
@@ -38,39 +37,32 @@ export async function resolveMediaFields(data, fields) {
38
37
  break;
39
38
  }
40
39
  case 'object':
41
- collectIds(val.data, field.fields);
40
+ collectIds(val, field.fields);
42
41
  break;
43
42
  case 'blocks':
44
43
  if (Array.isArray(val)) {
45
44
  val.forEach((item) => {
46
- // Find the object definition for this item based on its slug
47
- const objectDef = field.of.find((objDef) => objDef.slug === item.slug);
45
+ const objectDef = field.of.find((objDef) => objDef.slug === item._slug);
48
46
  if (objectDef) {
49
- collectIds(item.data, objectDef.fields);
47
+ collectIds(item, objectDef.fields);
50
48
  }
51
49
  });
52
50
  }
53
51
  break;
54
52
  case 'content': {
55
53
  const contentField = field;
56
- // Content field is localized: { lang: StructuredContentDoc }
57
- if (typeof val === 'object' && val !== null) {
58
- for (const doc of Object.values(val)) {
59
- if (doc && typeof doc === 'object' && doc.type === 'doc') {
60
- // Regular media nodes (figure/video/image)
61
- mediaIds.push(...extractMediaIdsFromDoc(doc));
62
- // Inline block fields (image/file/media inside blocks/objects)
63
- if (contentField.inlineBlocks?.length) {
64
- walkInlineBlockNodes(doc, (node) => {
65
- const bd = node.attrs?.blockData;
66
- if (!bd || typeof bd !== 'object')
67
- return;
68
- const def = contentField.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
69
- if (def)
70
- collectIds(bd, def.fields);
71
- });
72
- }
73
- }
54
+ // Content is now a single doc, not Record<lang, doc>
55
+ if (val && typeof val === 'object' && val.type === 'doc') {
56
+ mediaIds.push(...extractMediaIdsFromDoc(val));
57
+ if (contentField.inlineBlocks?.length) {
58
+ walkInlineBlockNodes(val, (node) => {
59
+ const bd = node.attrs?.blockData;
60
+ if (!bd || typeof bd !== 'object')
61
+ return;
62
+ const def = contentField.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
63
+ if (def)
64
+ collectIds(bd, def.fields);
65
+ });
74
66
  }
75
67
  }
76
68
  break;
@@ -105,12 +97,18 @@ export async function resolveMediaFields(data, fields) {
105
97
  if (!mediaFile)
106
98
  return { data: null, styles: {} };
107
99
  if (mediaFile.type === 'image') {
108
- const imgResult = await getImageStyles({ ...field, type: 'image' }, mediaFile);
109
- return {
110
- data: mediaFile,
111
- styles: imgResult.styles,
112
- blurDataUrl: imgResult.blurDataUrl
113
- };
100
+ try {
101
+ const imgResult = await getImageStyles(field, mediaFile);
102
+ return {
103
+ data: mediaFile,
104
+ styles: imgResult.styles,
105
+ blurDataUrl: imgResult.blurDataUrl
106
+ };
107
+ }
108
+ catch (e) {
109
+ console.warn(`[CMS] Image style resolution failed for media field "${field.slug}", media ${mediaFile.id}:`, e);
110
+ return { data: mediaFile, styles: {}, blurDataUrl: null };
111
+ }
114
112
  }
115
113
  // Video/audio: resolve transcript + audioDescription
116
114
  let transcript = null;
@@ -143,37 +141,6 @@ export async function resolveMediaFields(data, fields) {
143
141
  }
144
142
  break;
145
143
  }
146
- case 'image': {
147
- if (field.multiple && Array.isArray(val)) {
148
- result[field.slug] = await Promise.all(val.map(async (id) => {
149
- const mediaFile = mediaMap[id];
150
- if (!mediaFile) {
151
- return { data: null, styles: {} };
152
- }
153
- const imgResult = await getImageStyles(field, mediaFile);
154
- return {
155
- data: mediaFile,
156
- styles: imgResult.styles,
157
- blurDataUrl: imgResult.blurDataUrl
158
- };
159
- }));
160
- }
161
- else {
162
- const mediaFile = mediaMap[val];
163
- if (!mediaFile) {
164
- result[field.slug] = { data: null, styles: {} };
165
- break;
166
- }
167
- const imgResult = await getImageStyles(field, mediaFile);
168
- result[field.slug] = {
169
- data: mediaFile,
170
- styles: imgResult.styles,
171
- blurDataUrl: imgResult.blurDataUrl
172
- };
173
- break;
174
- }
175
- break;
176
- }
177
144
  case 'file': {
178
145
  if (field.multiple && Array.isArray(val)) {
179
146
  result[field.slug] = val.map((id) => mediaMap[id] ?? null);
@@ -198,80 +165,62 @@ export async function resolveMediaFields(data, fields) {
198
165
  break;
199
166
  }
200
167
  case 'object':
201
- result[field.slug] = {
202
- ...val,
203
- data: await resolveValues(val.data, field.fields)
204
- };
168
+ result[field.slug] = await resolveValues(val, field.fields);
205
169
  break;
206
170
  case 'blocks':
207
171
  result[field.slug] = await Promise.all(
208
172
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
173
  val.map(async (item) => {
210
- // Find the object definition for this item based on its slug
211
- const objectDef = field.of.find((objDef) => objDef.slug === item.slug);
174
+ const objectDef = field.of.find((objDef) => objDef.slug === item._slug);
212
175
  if (objectDef) {
213
- return {
214
- ...item,
215
- data: await resolveValues(item.data, objectDef.fields)
216
- };
176
+ return await resolveValues(item, objectDef.fields);
217
177
  }
218
- // If no matching object definition found, return item unchanged
219
178
  return item;
220
179
  }));
221
180
  break;
222
181
  case 'content': {
223
- // Content field is localized: { lang: StructuredContentDoc }
182
+ // Content is now a single doc, not Record<lang, doc>
224
183
  const contentField = field;
225
- if (typeof val === 'object' && val !== null) {
226
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
- const resolved = {};
228
- for (const [lang, doc] of Object.entries(val)) {
229
- if (doc && typeof doc === 'object' && doc.type === 'doc') {
230
- const cloned = cloneDoc(doc);
231
- walkMediaNodes(cloned, (node) => {
232
- const mediaId = node.attrs?.['data-media-id'];
233
- if (!mediaId)
234
- return;
235
- const mediaFile = mediaMap[mediaId];
236
- if (!mediaFile)
237
- return;
238
- node.attrs.src = mediaFile.url;
239
- if (mediaFile.width)
240
- node.attrs.width = mediaFile.width;
241
- if (mediaFile.height)
242
- node.attrs.height = mediaFile.height;
243
- if (mediaFile.alt && !node.attrs.alt)
244
- node.attrs.alt = mediaFile.alt;
245
- node.attrs._media = {
246
- data: mediaFile,
247
- ...(mediaFile.type === 'image' ? { blurDataUrl: mediaFile.blurDataUrl } : {})
248
- };
249
- });
250
- // Resolve inline block fields
251
- if (contentField.inlineBlocks?.length) {
252
- const promises = [];
253
- walkInlineBlockNodes(cloned, (node) => {
254
- const bd = node.attrs?.blockData;
255
- if (!bd || typeof bd !== 'object')
256
- return;
257
- const def = contentField.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
258
- if (!def)
259
- return;
260
- promises.push(resolveValues(bd, def.fields).then((resolvedBd) => {
261
- node.attrs.blockData = resolvedBd;
262
- }));
263
- });
264
- if (promises.length > 0) {
265
- await Promise.all(promises);
266
- }
267
- }
268
- resolved[lang] = cloned;
269
- }
270
- else {
271
- resolved[lang] = doc;
184
+ if (val && typeof val === 'object' && val.type === 'doc') {
185
+ const cloned = cloneDoc(val);
186
+ walkMediaNodes(cloned, (node) => {
187
+ const mediaId = node.attrs?.['data-media-id'];
188
+ if (!mediaId)
189
+ return;
190
+ const mediaFile = mediaMap[mediaId];
191
+ if (!mediaFile)
192
+ return;
193
+ node.attrs.src = mediaFile.url;
194
+ if (mediaFile.width)
195
+ node.attrs.width = mediaFile.width;
196
+ if (mediaFile.height)
197
+ node.attrs.height = mediaFile.height;
198
+ if (mediaFile.alt && !node.attrs.alt)
199
+ node.attrs.alt = mediaFile.alt;
200
+ node.attrs._media = {
201
+ data: mediaFile,
202
+ ...(mediaFile.type === 'image' ? { blurDataUrl: mediaFile.blurDataUrl } : {})
203
+ };
204
+ });
205
+ // Resolve inline block fields
206
+ if (contentField.inlineBlocks?.length) {
207
+ const promises = [];
208
+ walkInlineBlockNodes(cloned, (node) => {
209
+ const bd = node.attrs?.blockData;
210
+ if (!bd || typeof bd !== 'object')
211
+ return;
212
+ const def = contentField.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
213
+ if (!def)
214
+ return;
215
+ promises.push(resolveValues(bd, def.fields).then((resolvedBd) => {
216
+ node.attrs.blockData = resolvedBd;
217
+ }));
218
+ });
219
+ if (promises.length > 0) {
220
+ await Promise.all(promises);
272
221
  }
273
222
  }
274
- result[field.slug] = resolved;
223
+ result[field.slug] = cloned;
275
224
  }
276
225
  break;
277
226
  }
@@ -25,34 +25,30 @@ export async function resolveRelationFields(data, fields, language) {
25
25
  break;
26
26
  }
27
27
  case 'object':
28
- collectIds(val.data, field.fields);
28
+ collectIds(val, field.fields);
29
29
  break;
30
30
  case 'blocks':
31
31
  if (Array.isArray(val)) {
32
32
  val.forEach((item) => {
33
- // Find the object definition for this item based on its slug
34
- const objectDef = field.of.find((objDef) => objDef.slug === item.slug);
33
+ const objectDef = field.of.find((objDef) => objDef.slug === item._slug);
35
34
  if (objectDef) {
36
- collectIds(item.data, objectDef.fields);
35
+ collectIds(item, objectDef.fields);
37
36
  }
38
37
  });
39
38
  }
40
39
  break;
41
40
  case 'content': {
42
41
  const cf = field;
43
- if (typeof val === 'object' && val !== null && cf.inlineBlocks?.length) {
44
- for (const doc of Object.values(val)) {
45
- if (doc && typeof doc === 'object' && doc.type === 'doc') {
46
- walkInlineBlockNodes(doc, (node) => {
47
- const bd = node.attrs?.blockData;
48
- if (!bd || typeof bd !== 'object')
49
- return;
50
- const def = cf.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
51
- if (def)
52
- collectIds(bd, def.fields);
53
- });
54
- }
55
- }
42
+ // Content is now a single doc, not Record<lang, doc>
43
+ if (val && typeof val === 'object' && val.type === 'doc' && cf.inlineBlocks?.length) {
44
+ walkInlineBlockNodes(val, (node) => {
45
+ const bd = node.attrs?.blockData;
46
+ if (!bd || typeof bd !== 'object')
47
+ return;
48
+ const def = cf.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
49
+ if (def)
50
+ collectIds(bd, def.fields);
51
+ });
56
52
  }
57
53
  break;
58
54
  }
@@ -69,7 +65,7 @@ export async function resolveRelationFields(data, fields, language) {
69
65
  ids: entriesIds,
70
66
  language
71
67
  });
72
- const entriesMap = Object.fromEntries(entries.map((e) => [e.id, e]));
68
+ const entriesMap = Object.fromEntries(entries.map((e) => [e._id, e]));
73
69
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
70
  const resolveValues = (value, fields) => {
75
71
  // Start with a copy of all original data to preserve non-field properties
@@ -92,50 +88,33 @@ export async function resolveRelationFields(data, fields, language) {
92
88
  break;
93
89
  }
94
90
  case 'object':
95
- result[field.slug] = {
96
- ...val,
97
- data: resolveValues(val.data, field.fields)
98
- };
91
+ result[field.slug] = resolveValues(val, field.fields);
99
92
  break;
100
93
  case 'blocks':
101
94
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
95
  result[field.slug] = val.map((item) => {
103
- // Find the object definition for this item based on its slug
104
- const objectDef = field.of.find((objDef) => objDef.slug === item.slug);
96
+ const objectDef = field.of.find((objDef) => objDef.slug === item._slug);
105
97
  if (objectDef) {
106
- return {
107
- ...item,
108
- data: resolveValues(item.data, objectDef.fields)
109
- };
98
+ return resolveValues(item, objectDef.fields);
110
99
  }
111
- // If no matching object definition found, return item unchanged
112
100
  return item;
113
101
  });
114
102
  break;
115
103
  case 'content': {
116
104
  const cf = field;
117
- if (typeof val === 'object' && val !== null && cf.inlineBlocks?.length) {
118
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
- const resolved = {};
120
- for (const [lang, doc] of Object.entries(val)) {
121
- if (doc && typeof doc === 'object' && doc.type === 'doc') {
122
- const cloned = cloneDoc(doc);
123
- walkInlineBlockNodes(cloned, (node) => {
124
- const bd = node.attrs?.blockData;
125
- if (!bd || typeof bd !== 'object')
126
- return;
127
- const def = cf.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
128
- if (!def)
129
- return;
130
- node.attrs.blockData = resolveValues(bd, def.fields);
131
- });
132
- resolved[lang] = cloned;
133
- }
134
- else {
135
- resolved[lang] = doc;
136
- }
137
- }
138
- result[field.slug] = resolved;
105
+ // Content is now a single doc, not Record<lang, doc>
106
+ if (val && typeof val === 'object' && val.type === 'doc' && cf.inlineBlocks?.length) {
107
+ const cloned = cloneDoc(val);
108
+ walkInlineBlockNodes(cloned, (node) => {
109
+ const bd = node.attrs?.blockData;
110
+ if (!bd || typeof bd !== 'object')
111
+ return;
112
+ const def = cf.inlineBlocks.find((b) => b.slug === node.attrs?.blockType);
113
+ if (!def)
114
+ return;
115
+ node.attrs.blockData = resolveValues(bd, def.fields);
116
+ });
117
+ result[field.slug] = cloned;
139
118
  }
140
119
  break;
141
120
  }