@stackbit/cms-core 0.1.21-cross-references.0 → 0.1.21

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 (42) hide show
  1. package/dist/content-store-types.d.ts +8 -34
  2. package/dist/content-store-types.d.ts.map +1 -1
  3. package/dist/content-store-utils.d.ts +1 -3
  4. package/dist/content-store-utils.d.ts.map +1 -1
  5. package/dist/content-store-utils.js +1 -8
  6. package/dist/content-store-utils.js.map +1 -1
  7. package/dist/content-store.d.ts +4 -5
  8. package/dist/content-store.d.ts.map +1 -1
  9. package/dist/content-store.js +49 -62
  10. package/dist/content-store.js.map +1 -1
  11. package/dist/utils/create-update-csi-docs.d.ts +10 -11
  12. package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
  13. package/dist/utils/create-update-csi-docs.js +13 -92
  14. package/dist/utils/create-update-csi-docs.js.map +1 -1
  15. package/dist/utils/csi-to-store-docs-converter.js +1 -69
  16. package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
  17. package/dist/utils/duplicate-document.js +0 -3
  18. package/dist/utils/duplicate-document.js.map +1 -1
  19. package/dist/utils/model-utils.d.ts +4 -4
  20. package/dist/utils/model-utils.d.ts.map +1 -1
  21. package/dist/utils/model-utils.js +2 -67
  22. package/dist/utils/model-utils.js.map +1 -1
  23. package/dist/utils/schema-utils.d.ts +87 -0
  24. package/dist/utils/schema-utils.d.ts.map +1 -0
  25. package/dist/utils/schema-utils.js +195 -0
  26. package/dist/utils/schema-utils.js.map +1 -0
  27. package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
  28. package/dist/utils/store-to-api-docs-converter.js +1 -38
  29. package/dist/utils/store-to-api-docs-converter.js.map +1 -1
  30. package/dist/utils/store-to-csi-docs-converter.js +0 -30
  31. package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
  32. package/package.json +4 -4
  33. package/src/content-store-types.ts +3 -41
  34. package/src/content-store-utils.ts +1 -9
  35. package/src/content-store.ts +50 -66
  36. package/src/utils/create-update-csi-docs.ts +18 -109
  37. package/src/utils/csi-to-store-docs-converter.ts +17 -91
  38. package/src/utils/duplicate-document.ts +0 -3
  39. package/src/utils/model-utils.ts +7 -95
  40. package/src/utils/schema-utils.js +212 -0
  41. package/src/utils/store-to-api-docs-converter.ts +1 -38
  42. package/src/utils/store-to-csi-docs-converter.ts +0 -30
@@ -7,41 +7,41 @@ import { fieldPathToString, mapPromise } from '@stackbit/utils';
7
7
  import * as CSITypes from '@stackbit/types';
8
8
 
9
9
  import * as ContentStoreTypes from '../content-store-types';
10
- import { getContentSourceId, getUserContextForSrcType, updateOperationValueFieldWithCrossReference } from '../content-store-utils';
11
10
 
12
11
  export type CreateDocumentCallback = ({
13
12
  updateOperationFields,
14
- contentSourceData,
15
13
  modelName
16
14
  }: {
17
15
  updateOperationFields: Record<string, CSITypes.UpdateOperationField>;
18
- contentSourceData: ContentStoreTypes.ContentSourceData;
19
16
  modelName: string;
20
17
  }) => Promise<CSITypes.Document>;
21
18
 
22
19
  export function getCreateDocumentThunk({
20
+ csiModelMap,
23
21
  locale,
24
22
  defaultLocaleDocumentId,
25
- user
23
+ userContext,
24
+ contentSourceInstance
26
25
  }: {
26
+ csiModelMap: Record<string, CSIModel>;
27
27
  locale?: string;
28
28
  defaultLocaleDocumentId?: string;
29
- user?: ContentStoreTypes.User;
29
+ userContext: unknown;
30
+ contentSourceInstance: CSITypes.ContentSourceInterface;
30
31
  }): CreateDocumentCallback {
31
- return async ({ updateOperationFields, modelName, contentSourceData }) => {
32
+ return async ({ updateOperationFields, modelName }) => {
32
33
  // When passing model and modelMap to contentSourceInstance, we have to pass
33
34
  // the original models (i.e., csiModel and csiModelMap) that we've received
34
35
  // from that contentSourceInstance. We can't pass internal models as they
35
36
  // might
36
- const csiModel = contentSourceData.csiModelMap[modelName];
37
+ const csiModel = csiModelMap[modelName];
37
38
  if (!csiModel) {
38
39
  throw new Error(`no model with name '${modelName}' was found`);
39
40
  }
40
- const userContext = getUserContextForSrcType(contentSourceData.srcType, user);
41
- return await contentSourceData.instance.createDocument({
41
+ return await contentSourceInstance.createDocument({
42
42
  updateOperationFields: updateOperationFields,
43
43
  model: csiModel,
44
- modelMap: contentSourceData.csiModelMap,
44
+ modelMap: csiModelMap,
45
45
  locale,
46
46
  defaultLocaleDocumentId,
47
47
  userContext
@@ -90,24 +90,18 @@ export function getCreateDocumentThunk({
90
90
  export async function createDocumentRecursively({
91
91
  object,
92
92
  modelName,
93
- contentSourceId,
94
- contentSourceDataById,
93
+ modelMap,
94
+ csiModelMap,
95
95
  createDocument
96
96
  }: {
97
97
  object?: Record<string, any>;
98
98
  modelName: string;
99
- contentSourceId: string;
100
- contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
99
+ modelMap: Record<string, SDKModel>;
100
+ csiModelMap: Record<string, CSIModel>;
101
101
  createDocument: CreateDocumentCallback;
102
102
  }): Promise<{ document: CSITypes.Document; newRefDocuments: CSITypes.Document[] }> {
103
- const contentSourceData = contentSourceDataById[contentSourceId];
104
- if (!contentSourceData) {
105
- throw new Error(`no content source data for id '${contentSourceId}'`);
106
- }
107
- const modelMap = contentSourceData.modelMap;
108
- const csiModelMap = contentSourceData.csiModelMap;
109
103
  const model = modelMap[modelName];
110
- const csiModel = contentSourceData.csiModelMap[modelName];
104
+ const csiModel = csiModelMap[modelName];
111
105
  if (!model || !csiModel) {
112
106
  throw new Error(`no model with name '${modelName}' was found`);
113
107
  }
@@ -127,14 +121,11 @@ export async function createDocumentRecursively({
127
121
  fieldPath: [modelName],
128
122
  modelMap,
129
123
  csiModelMap,
130
- contentSourceId,
131
- contentSourceDataById,
132
124
  createDocument
133
125
  });
134
126
 
135
127
  const document = await createDocument({
136
128
  updateOperationFields: nestedResult.fields,
137
- contentSourceData: contentSourceData,
138
129
  modelName: modelName
139
130
  });
140
131
  return {
@@ -161,8 +152,6 @@ async function createObjectRecursively({
161
152
  fieldPath,
162
153
  modelMap,
163
154
  csiModelMap,
164
- contentSourceId,
165
- contentSourceDataById,
166
155
  createDocument
167
156
  }: {
168
157
  object?: Record<string, any>;
@@ -171,8 +160,6 @@ async function createObjectRecursively({
171
160
  fieldPath: (string | number)[];
172
161
  modelMap: Record<string, SDKModel>;
173
162
  csiModelMap: Record<string, CSIModel>;
174
- contentSourceId: string;
175
- contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
176
163
  createDocument: CreateDocumentCallback;
177
164
  }): Promise<{
178
165
  fields: Record<string, CSITypes.UpdateOperationField>;
@@ -219,8 +206,6 @@ async function createObjectRecursively({
219
206
  fieldPath: fieldPath.concat(fieldName),
220
207
  modelMap,
221
208
  csiModelMap,
222
- contentSourceId,
223
- contentSourceDataById,
224
209
  createDocument
225
210
  });
226
211
  result.fields[fieldName] = fieldResult.field;
@@ -241,8 +226,6 @@ async function createUpdateOperationFieldRecursively({
241
226
  fieldPath,
242
227
  modelMap,
243
228
  csiModelMap,
244
- contentSourceId,
245
- contentSourceDataById,
246
229
  createDocument
247
230
  }: {
248
231
  value: any;
@@ -251,8 +234,6 @@ async function createUpdateOperationFieldRecursively({
251
234
  fieldPath: (string | number)[];
252
235
  modelMap: Record<string, SDKModel>;
253
236
  csiModelMap: Record<string, CSIModel>;
254
- contentSourceId: string;
255
- contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
256
237
  createDocument: CreateDocumentCallback;
257
238
  }): Promise<{ field: CSITypes.UpdateOperationField; newRefDocuments: CSITypes.Document[] }> {
258
239
  if (csiModelField.type === 'object') {
@@ -266,8 +247,6 @@ async function createUpdateOperationFieldRecursively({
266
247
  fieldPath,
267
248
  modelMap,
268
249
  csiModelMap,
269
- contentSourceId,
270
- contentSourceDataById,
271
250
  createDocument
272
251
  });
273
252
  return {
@@ -305,8 +284,6 @@ async function createUpdateOperationFieldRecursively({
305
284
  fieldPath,
306
285
  modelMap,
307
286
  csiModelMap,
308
- contentSourceId,
309
- contentSourceDataById,
310
287
  createDocument
311
288
  });
312
289
  return {
@@ -363,14 +340,11 @@ async function createUpdateOperationFieldRecursively({
363
340
  modelName = modelNames[0];
364
341
  }
365
342
  }
366
- if (!modelName) {
367
- throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
368
- }
369
343
  const { document, newRefDocuments } = await createDocumentRecursively({
370
344
  object: rest,
371
345
  modelName,
372
- contentSourceId,
373
- contentSourceDataById,
346
+ modelMap,
347
+ csiModelMap,
374
348
  createDocument
375
349
  });
376
350
  return {
@@ -382,39 +356,6 @@ async function createUpdateOperationFieldRecursively({
382
356
  newRefDocuments: [document, ...newRefDocuments]
383
357
  };
384
358
  }
385
- } else if (['string', 'text', 'json'].includes(csiModelField.type) && modelField.type === 'cross-reference') {
386
- const fieldType = csiModelField.type as 'string' | 'text' | 'json';
387
- let { $$ref: refId = null, $$type: modelName = null, $$refSrcType: refSrcType = null, $$refProjectId: refProjectId = null, ...rest } = value;
388
- let refObject;
389
- const newRefDocuments: CSITypes.Document[] = [];
390
- if (refId && refSrcType && refProjectId) {
391
- refObject = { refId, refSrcType, refProjectId };
392
- } else {
393
- if (!modelName || !refSrcType || !refProjectId) {
394
- const models = modelField.models;
395
- if (models && models.length === 1) {
396
- modelName = models[0]!.modelName;
397
- refSrcType = models[0]!.srcType;
398
- refProjectId = models[0]!.srcProjectId;
399
- }
400
- }
401
- if (!modelName || !refSrcType || !refProjectId) {
402
- throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
403
- }
404
- const { document, newRefDocuments } = await createDocumentRecursively({
405
- object: rest,
406
- modelName,
407
- contentSourceId: getContentSourceId(refSrcType, refProjectId),
408
- contentSourceDataById,
409
- createDocument
410
- });
411
- newRefDocuments.push(document, ...newRefDocuments);
412
- refObject = { refId: document.id, refSrcType, refProjectId };
413
- }
414
- return {
415
- field: updateOperationValueFieldWithCrossReference(fieldType, refObject),
416
- newRefDocuments
417
- };
418
359
  } else if (csiModelField.type === 'list') {
419
360
  if (modelField.type !== 'list') {
420
361
  throw new Error(`field type mismatch between external and internal models at field path ${fieldPathToString(fieldPath)}`);
@@ -437,8 +378,6 @@ async function createUpdateOperationFieldRecursively({
437
378
  fieldPath: fieldPath.concat(index),
438
379
  modelMap,
439
380
  csiModelMap,
440
- contentSourceId,
441
- contentSourceDataById,
442
381
  createDocument
443
382
  });
444
383
  if (result.field.type === 'list') {
@@ -486,8 +425,6 @@ export async function convertOperationField({
486
425
  csiModelField,
487
426
  modelMap,
488
427
  csiModelMap,
489
- contentSourceId,
490
- contentSourceDataById,
491
428
  createDocument
492
429
  }: {
493
430
  operationField: ContentStoreTypes.UpdateOperationField;
@@ -496,8 +433,6 @@ export async function convertOperationField({
496
433
  csiModelField: FieldSpecificProps;
497
434
  modelMap: Record<string, SDKModel>;
498
435
  csiModelMap: Record<string, SDKModel>;
499
- contentSourceId: string;
500
- contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
501
436
  createDocument: CreateDocumentCallback;
502
437
  }): Promise<CSITypes.UpdateOperationField> {
503
438
  switch (operationField.type) {
@@ -512,8 +447,6 @@ export async function convertOperationField({
512
447
  fieldPath,
513
448
  modelMap,
514
449
  csiModelMap,
515
- contentSourceId,
516
- contentSourceDataById,
517
450
  createDocument
518
451
  });
519
452
  return {
@@ -534,8 +467,6 @@ export async function convertOperationField({
534
467
  fieldPath,
535
468
  modelMap,
536
469
  csiModelMap,
537
- contentSourceId,
538
- contentSourceDataById,
539
470
  createDocument
540
471
  });
541
472
  return {
@@ -545,23 +476,7 @@ export async function convertOperationField({
545
476
  };
546
477
  }
547
478
  case 'reference':
548
- // ContentStore and CSI 'reference' operation field have the same format
549
479
  return operationField;
550
- case 'cross-reference':
551
- if (csiModelField.type !== 'string' && csiModelField.type !== 'text' && csiModelField.type !== 'json') {
552
- throw new Error(
553
- `update operation with with 'cross-reference' field can be performed on 'string', 'text' and 'json' content-source field types only, got '${csiModelField.type}'`
554
- );
555
- }
556
- const refObject = {
557
- refId: operationField.refId,
558
- refSrcType: operationField.refSrcType,
559
- refProjectId: operationField.refProjectId
560
- };
561
- return {
562
- type: csiModelField.type,
563
- value: csiModelField.type === 'json' ? refObject : JSON.stringify(refObject)
564
- };
565
480
  case 'list': {
566
481
  if (modelField.type !== 'list' || csiModelField.type !== 'list') {
567
482
  throw new Error(`the operation field type '${operationField.type}' does not match the model field type '${modelField.type}'`);
@@ -576,8 +491,6 @@ export async function convertOperationField({
576
491
  fieldPath: fieldPath.concat(index),
577
492
  modelMap,
578
493
  csiModelMap,
579
- contentSourceId,
580
- contentSourceDataById,
581
494
  createDocument
582
495
  });
583
496
  if (result.field.type === 'list') {
@@ -593,9 +506,7 @@ export async function convertOperationField({
593
506
  }
594
507
  case 'enum':
595
508
  if (csiModelField.type !== 'enum' && csiModelField.type !== 'string') {
596
- throw new Error(
597
- `update operation with 'enum' field can be performed on 'string' and 'enum' content-source field types only, got '${csiModelField.type}'`
598
- );
509
+ throw new Error(`the operation field type 'enum' can be performed on 'string' and 'enum' content-source field types '${csiModelField.type}'`);
599
510
  }
600
511
  // When inserting new enum value into a list, the client does not
601
512
  // send value. Set first option as the value.
@@ -632,8 +543,6 @@ export async function convertOperationField({
632
543
  fieldPath,
633
544
  modelMap,
634
545
  csiModelMap,
635
- contentSourceId,
636
- contentSourceDataById,
637
546
  createDocument
638
547
  });
639
548
  return result.field;
@@ -184,7 +184,7 @@ function mapCSIFieldToStoreField({
184
184
  context: MapContext;
185
185
  }): ContentStoreTypes.DocumentField {
186
186
  if (!csiDocumentField) {
187
- const isUnset = ['object', 'model', 'reference', 'cross-reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
187
+ const isUnset = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
188
188
  return {
189
189
  type: modelField.type,
190
190
  ...(localized
@@ -223,8 +223,6 @@ function mapCSIFieldToStoreField({
223
223
  case 'file':
224
224
  case 'reference':
225
225
  return csiDocumentField as ContentStoreTypes.DocumentField;
226
- case 'cross-reference':
227
- return mapReferenceField(csiDocumentField)
228
226
  case 'object':
229
227
  return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField, context);
230
228
  case 'model':
@@ -243,76 +241,6 @@ function mapCSIFieldToStoreField({
243
241
  }
244
242
  }
245
243
 
246
- function mapReferenceField(csiDocumentField: CSITypes.DocumentField): ContentStoreTypes.DocumentCrossReferenceField {
247
- const unlocalizedUnset = {
248
- type: 'cross-reference',
249
- refType: 'document',
250
- isUnset: true
251
- } as const;
252
- if (csiDocumentField.type !== 'string' && csiDocumentField.type !== 'text' && csiDocumentField.type !== 'json') {
253
- if (isLocalizedField(csiDocumentField)) {
254
- return {
255
- type: 'cross-reference',
256
- refType: 'document',
257
- localized: true,
258
- locales: {}
259
- };
260
- }
261
- return unlocalizedUnset;
262
- }
263
- const parseRefObject = (value: any): { refId: string; refSrcType: string; refProjectId: string } | null => {
264
- if (typeof value === 'string') {
265
- try {
266
- value = JSON.parse(value);
267
- } catch (error) {
268
- return null;
269
- }
270
- }
271
- if (_.isPlainObject(value) && 'refId' in value && 'refSrcType' in value && 'refProjectId' in value) {
272
- return {
273
- refId: value.refId,
274
- refSrcType: value.refSrcType,
275
- refProjectId: value.refProjectId
276
- };
277
- }
278
- return null;
279
- };
280
- if (isLocalizedField(csiDocumentField)) {
281
- csiDocumentField.locales;
282
- return {
283
- type: 'cross-reference',
284
- refType: 'document',
285
- localized: true,
286
- locales: _.reduce(
287
- csiDocumentField.locales,
288
- (accum: Record<string, { locale: string; refId: string; refSrcType: string; refProjectId: string }>, locale, localeKey) => {
289
- const refObject = parseRefObject(locale.value);
290
- if (refObject) {
291
- accum[localeKey] = {
292
- locale: locale.locale,
293
- ...refObject
294
- };
295
- }
296
- return accum;
297
- },
298
- {}
299
- )
300
- };
301
- }
302
- if (!('value' in csiDocumentField)) {
303
- return unlocalizedUnset;
304
- }
305
- const refObject = parseRefObject(csiDocumentField.value);
306
- if (!refObject) {
307
- return unlocalizedUnset;
308
- }
309
- return {
310
- type: 'cross-reference',
311
- refType: 'document',
312
- ...refObject
313
- };
314
- }
315
-
316
244
  function mapObjectField(
317
245
  csiDocumentField: CSITypes.DocumentObjectField,
318
246
  modelField: FieldObjectProps,
@@ -385,15 +313,14 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
385
313
  if (!isLocalizedField(csiDocumentField)) {
386
314
  return {
387
315
  type: csiDocumentField.type,
388
- items: csiDocumentField.items.map(
389
- (item) =>
390
- mapCSIFieldToStoreField({
391
- csiDocumentField: item,
392
- modelField: modelField.items ?? { type: 'string' },
393
- // list items can not be localized, only the list itself can be localized
394
- localized: false,
395
- context
396
- }) as ContentStoreTypes.DocumentListFieldItems
316
+ items: csiDocumentField.items.map((item) =>
317
+ mapCSIFieldToStoreField({
318
+ csiDocumentField: item,
319
+ modelField: modelField.items ?? { type: 'string' },
320
+ // list items can not be localized, only the list itself can be localized
321
+ localized: false,
322
+ context
323
+ }) as ContentStoreTypes.DocumentListFieldItems
397
324
  )
398
325
  };
399
326
  }
@@ -403,15 +330,14 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
403
330
  locales: _.mapValues(csiDocumentField.locales, (locale) => {
404
331
  return {
405
332
  locale: locale.locale,
406
- items: (locale.items ?? []).map(
407
- (item) =>
408
- mapCSIFieldToStoreField({
409
- csiDocumentField: item,
410
- modelField: modelField.items ?? { type: 'string' },
411
- // list items can not be localized, only the list itself can be localized
412
- localized: false,
413
- context
414
- }) as ContentStoreTypes.DocumentListFieldItems
333
+ items: (locale.items ?? []).map((item) =>
334
+ mapCSIFieldToStoreField({
335
+ csiDocumentField: item,
336
+ modelField: modelField.items ?? { type: 'string' },
337
+ // list items can not be localized, only the list itself can be localized
338
+ localized: false,
339
+ context
340
+ }) as ContentStoreTypes.DocumentListFieldItems
415
341
  )
416
342
  };
417
343
  })
@@ -202,9 +202,6 @@ function mergeObjectWithDocumentField({
202
202
  }
203
203
  break;
204
204
  }
205
- case 'cross-reference':
206
- // TODO: implement duplicating documents with cross-references
207
- break;
208
205
  case 'list': {
209
206
  const localizedField = getDocumentFieldForLocale(documentField, locale);
210
207
  if (value) {
@@ -1,18 +1,10 @@
1
1
  import _ from 'lodash';
2
2
 
3
- import { FieldCrossReferenceModel, Logger, ModelWithSource } from '@stackbit/types';
4
- import {
5
- Model,
6
- assignLabelFieldIfNeeded,
7
- isObjectField,
8
- isCrossReferenceField,
9
- isPageModel,
10
- mapModelFieldsRecursively,
11
- mapListItemsPropsOrSelfSpecificProps,
12
- validateConfig
13
- } from '@stackbit/sdk';
14
-
15
- export function normalizeModels({ models, logger }: { models: ModelWithSource[]; logger: Logger }): ModelWithSource[] {
3
+ import { Logger } from '@stackbit/types';
4
+ import { Model, assignLabelFieldIfNeeded, isObjectField, isPageModel, mapModelFieldsRecursively, mapListItemsPropsOrSelfSpecificProps } from '@stackbit/sdk';
5
+ import { validateConfig } from '@stackbit/sdk';
6
+
7
+ export function normalizeModels<T extends Model>({ models, logger }: { models: T[]; logger: Logger }): T[] {
16
8
  return models.map((model) => {
17
9
  model = { ...model };
18
10
 
@@ -53,98 +45,18 @@ export function normalizeModels({ models, logger }: { models: ModelWithSource[];
53
45
  mapListItemsPropsOrSelfSpecificProps(field, (listItemsPropsOrField) => {
54
46
  if (isObjectField(listItemsPropsOrField)) {
55
47
  assignLabelFieldIfNeeded(listItemsPropsOrField);
56
- } else if (isCrossReferenceField(listItemsPropsOrField)) {
57
- listItemsPropsOrField.models = validateAndNormalizeCrossReferenceModels({
58
- crossReferenceModels: listItemsPropsOrField.models,
59
- models,
60
- logger
61
- });
62
48
  }
63
49
  return listItemsPropsOrField;
64
50
  });
65
51
 
66
52
  return field;
67
- });
53
+ }) as T;
68
54
 
69
55
  return model;
70
56
  });
71
57
  }
72
58
 
73
- function validateAndNormalizeCrossReferenceModels({
74
- crossReferenceModels,
75
- models,
76
- logger
77
- }: {
78
- crossReferenceModels: FieldCrossReferenceModel[];
79
- models: ModelWithSource[];
80
- logger: Logger;
81
- }): FieldCrossReferenceModel[] {
82
- const modelGroupsByModelName = models.reduce((modelGroups: Record<string, ModelWithSource[]>, model) => {
83
- if (!(model.name in modelGroups)) {
84
- modelGroups[model.name] = [];
85
- }
86
- modelGroups[model.name]!.push(model);
87
- return modelGroups;
88
- }, {});
89
-
90
- // Match cross-reference models to the group of content source models with
91
- // the same name. Then, match the cross-reference model to content source
92
- // model by comparing srcType and srcProjectId. If after the comparison,
93
- // there are more than one model left, log a warning and filter out that
94
- // cross-reference model so it won't cause any model ambiguity.
95
- const nonMatchedCrossReferenceModels: {
96
- crossReferenceModel: FieldCrossReferenceModel;
97
- matchedModels: ModelWithSource[];
98
- }[] = [];
99
-
100
- const normalizedCrossReferenceModels = crossReferenceModels.reduce((matchedCrossReferenceModels: FieldCrossReferenceModel[], crossReferenceModel) => {
101
- const models = modelGroupsByModelName[crossReferenceModel.modelName];
102
- if (!models) {
103
- nonMatchedCrossReferenceModels.push({ crossReferenceModel, matchedModels: [] });
104
- return matchedCrossReferenceModels;
105
- }
106
- const matchedModels = models.filter((model) => {
107
- const matchesType = !crossReferenceModel.srcType || model.srcType === crossReferenceModel.srcType;
108
- const matchesId = !crossReferenceModel.srcProjectId || model.srcProjectId === crossReferenceModel.srcProjectId;
109
- return matchesType && matchesId;
110
- });
111
- if (matchedModels.length !== 1) {
112
- nonMatchedCrossReferenceModels.push({ crossReferenceModel, matchedModels });
113
- return matchedCrossReferenceModels;
114
- }
115
- const matchedModel = matchedModels[0]!;
116
- matchedCrossReferenceModels.push({
117
- modelName: crossReferenceModel.modelName,
118
- srcType: matchedModel.srcType,
119
- srcProjectId: matchedModel.srcProjectId
120
- });
121
- return matchedCrossReferenceModels;
122
- }, []);
123
-
124
- // Log model matching warnings using user logger
125
- for (const { crossReferenceModel, matchedModels } of nonMatchedCrossReferenceModels) {
126
- let message = `a model of cross-reference field: '${crossReferenceModel.modelName}'`;
127
- if (crossReferenceModel.srcType) {
128
- message += `, srcType: '${crossReferenceModel.srcType}'`;
129
- }
130
- if (crossReferenceModel.srcProjectId) {
131
- message += `, srcProjectId: '${crossReferenceModel.srcProjectId}'`;
132
- }
133
- message = message + ` defined in stackbit config`;
134
- let contentSourceModelsMessage;
135
- if (matchedModels.length) {
136
- const matchesModelsMessage = matchedModels.map((model) => `srcType: '${model.srcType}', srcProjectId: '${model.srcProjectId}'`).join('; ');
137
- contentSourceModelsMessage = ` matches more that 1 model in the following content sources: ${matchesModelsMessage}`;
138
- } else {
139
- contentSourceModelsMessage = ' does not match any content source model';
140
- }
141
- logger.warn(message + contentSourceModelsMessage);
142
- }
143
-
144
- return normalizedCrossReferenceModels;
145
- }
146
-
147
- export function validateModels<T extends Model>({ models, logger }: { models: T[]; logger: Logger }): T[] {
59
+ export function validateModels<T extends Model>({ models, logger }: { models: T[], logger: Logger }): T[] {
148
60
  const { config, errors } = validateConfig({
149
61
  stackbitVersion: '0.5.0',
150
62
  models: models,