@stackbit/cms-core 0.1.11 → 0.1.12-locale.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.
@@ -3,7 +3,7 @@ import path from 'path';
3
3
  import sanitizeFilename from 'sanitize-filename';
4
4
 
5
5
  import * as CSITypes from '@stackbit/types';
6
- import { getLocalizedFieldForLocale, UserCommandSpawner } from '@stackbit/types';
6
+ import { getLocalizedFieldForLocale, ModelExtension, UserCommandSpawner } from '@stackbit/types';
7
7
  import {
8
8
  Config,
9
9
  Field,
@@ -105,6 +105,7 @@ export class ContentStore {
105
105
  private stackbitConfig: Config | null = null;
106
106
  private yamlModels: Model[] = [];
107
107
  private configModels: Model[] = [];
108
+ private modelExtensions: ModelExtension[] | null = null;
108
109
  private presets: PresetMap = {};
109
110
 
110
111
  constructor(options: ContentSourceOptions) {
@@ -152,22 +153,36 @@ export class ContentStore {
152
153
 
153
154
  async init({ stackbitConfig }: { stackbitConfig: Config | null }): Promise<void> {
154
155
  this.logger.debug('init');
156
+
157
+ this.stackbitConfig = stackbitConfig;
158
+
155
159
  if (stackbitConfig) {
156
- this.yamlModels = await this.loadYamlModels({ stackbitConfig });
160
+ if (stackbitConfig.modelExtensions) {
161
+ this.modelExtensions = stackbitConfig.modelExtensions;
162
+ } else {
163
+ this.yamlModels = await this.loadYamlModels({ stackbitConfig });
164
+ this.configModels = this.mergeConfigModels(stackbitConfig.models ?? [], this.yamlModels);
165
+ }
157
166
  this.presets = await this.loadPresets({ stackbitConfig });
158
167
  }
159
- await this.setStackbitConfig({ stackbitConfig });
168
+
169
+ await this.loadContentSourcesAndProcessData({ init: true });
170
+
160
171
  this.contentUpdatesWatchTimer.startTimer();
161
172
  }
162
173
 
163
174
  async onStackbitConfigChange({ stackbitConfig }: { stackbitConfig: Config | null }) {
164
175
  this.logger.debug('onStackbitConfigChange');
165
- await this.setStackbitConfig({ stackbitConfig });
166
- }
167
176
 
168
- private async setStackbitConfig({ stackbitConfig }: { stackbitConfig: Config | null }) {
169
177
  this.stackbitConfig = stackbitConfig;
170
- this.configModels = this.mergeConfigModels(this.stackbitConfig, this.yamlModels);
178
+
179
+ if (stackbitConfig) {
180
+ if (stackbitConfig.modelExtensions) {
181
+ this.modelExtensions = stackbitConfig.modelExtensions;
182
+ } else {
183
+ this.configModels = this.mergeConfigModels(stackbitConfig.models ?? [], this.yamlModels);
184
+ }
185
+ }
171
186
 
172
187
  await this.loadContentSourcesAndProcessData({ init: true });
173
188
  }
@@ -202,12 +217,16 @@ export class ContentStore {
202
217
  this.contentUpdatesWatchTimer.startTimer();
203
218
  }
204
219
 
220
+ stop() {
221
+ this.contentUpdatesWatchTimer.stopTimer();
222
+ }
223
+
205
224
  async onFilesChange(updatedFiles: string[]): Promise<{ schemaChanged?: boolean; contentChanges: ContentStoreTypes.ContentChangeResult }> {
206
225
  this.logger.debug('onFilesChange');
207
226
 
208
227
  let schemaChanged = false;
209
228
 
210
- if (this.stackbitConfig) {
229
+ if (this.stackbitConfig && !this.stackbitConfig.modelExtensions) {
211
230
  // Check if any of the yaml models files were changed. If yaml model files were changed,
212
231
  // reload them and merge them with models defined in stackbit config.
213
232
  const modelDirs = getYamlModelDirs(this.stackbitConfig);
@@ -216,7 +235,7 @@ export class ContentStore {
216
235
  this.logger.debug('identified change in stackbit model files');
217
236
  schemaChanged = true;
218
237
  this.yamlModels = await this.loadYamlModels({ stackbitConfig: this.stackbitConfig });
219
- this.configModels = this.mergeConfigModels(this.stackbitConfig, this.yamlModels);
238
+ this.configModels = this.mergeConfigModels(this.stackbitConfig.models ?? [], this.yamlModels);
220
239
  }
221
240
 
222
241
  // Check if any of the preset files were changed. If presets were changed, reload them.
@@ -287,8 +306,8 @@ export class ContentStore {
287
306
  return yamlModelsResult.models;
288
307
  }
289
308
 
290
- private mergeConfigModels(stackbitConfig: Config | null, modelsFromFiles: Model[]) {
291
- const configModelsResult = mergeConfigModelsWithModelsFromFiles(stackbitConfig?.models ?? [], modelsFromFiles);
309
+ private mergeConfigModels(configModels: Model[], modelsFromFiles: Model[]) {
310
+ const configModelsResult = mergeConfigModelsWithModelsFromFiles(configModels, modelsFromFiles);
292
311
  for (const error of configModelsResult.errors) {
293
312
  this.userLogger.warn(error.message);
294
313
  }
@@ -352,7 +371,7 @@ export class ContentStore {
352
371
  // update all content sources at once to prevent race conditions
353
372
  this.contentSourceDataById = await this.processData({
354
373
  stackbitConfig: this.stackbitConfig,
355
- configModels: this.configModels,
374
+ configModels: (this.modelExtensions as Model[]) ?? this.configModels ?? [],
356
375
  presets: this.presets,
357
376
  contentSourceRawDataArr: contentSourceRawDataArr
358
377
  });
@@ -613,9 +632,67 @@ export class ContentStore {
613
632
  presets: Record<string, Preset>;
614
633
  contentSourceRawDataArr: ContentSourceRawData[];
615
634
  }): Promise<Record<string, ContentSourceData>> {
635
+ // Group models from all content sources by their names
636
+
637
+ const csiModelGroups = contentSourceRawDataArr.reduce((modelGroups: Record<string, CSITypes.ModelWithSource[]>, csData) => {
638
+ return csData.csiModels.reduce((modelGroups, model) => {
639
+ if (!(model.name in modelGroups)) {
640
+ modelGroups[model.name] = [];
641
+ }
642
+ modelGroups[model.name]!.push({
643
+ srcType: csData.srcType,
644
+ srcProjectId: csData.srcProjectId,
645
+ ...model
646
+ });
647
+ return modelGroups;
648
+ }, modelGroups);
649
+ }, {});
650
+
651
+ // Match config models to the group of content source models with the same name.
652
+ // Then, match the config model to content source model by comparing srcType and
653
+ // srcProjectId. If after the comparison, there are more than one model left,
654
+ // log a warning and filter out that config model so it won't be merged with any
655
+ // of the content source models.
656
+ const modelMatchErrors: { configModel: ModelExtension; matchedCSIModels: CSITypes.ModelWithSource[] }[] = [];
657
+ const filteredConfigModels = (configModels as ModelExtension[]).filter((configModel) => {
658
+ const csiModels = csiModelGroups[configModel.name];
659
+ if (!csiModels) {
660
+ return false;
661
+ }
662
+ const matchedCSIModels = csiModels.filter((model) => {
663
+ const matchesType = !configModel.srcType || model.srcType === configModel.srcType;
664
+ const matchesId = !configModel.srcProjectId || model.srcProjectId === configModel.srcProjectId;
665
+ return matchesType && matchesId;
666
+ });
667
+ if (matchedCSIModels.length === 0) {
668
+ return false;
669
+ }
670
+ if (matchedCSIModels.length === 1) {
671
+ return true;
672
+ }
673
+ modelMatchErrors.push({
674
+ configModel,
675
+ matchedCSIModels
676
+ });
677
+ return false;
678
+ }) as Model[];
679
+
680
+ // Log model matching warnings using user logger
681
+ for (const { configModel, matchedCSIModels } of modelMatchErrors) {
682
+ let message = `name: '${configModel.name}'`;
683
+ if (configModel.srcType) {
684
+ message += `, srcType: '${configModel.srcType}'`;
685
+ }
686
+ if (configModel.srcProjectId) {
687
+ message += `, srcProjectId: '${configModel.srcProjectId}'`;
688
+ }
689
+ const matchesModelsMessage = matchedCSIModels.map((model) => `srcType: '${model.srcType}', srcProjectId: '${model.srcProjectId}'`).join('; ');
690
+ this.userLogger.warn(`model ${message} defined in stackbit config matches more that 1 model in the following content sources: ${matchesModelsMessage}`);
691
+ }
692
+
616
693
  const modelsWithSource = contentSourceRawDataArr.reduce((accum: CSITypes.ModelWithSource[], csData) => {
617
694
  const mergedModels = mergeConfigModelsWithExternalModels({
618
- configModels: configModels,
695
+ configModels: filteredConfigModels,
619
696
  externalModels: csData.csiModels
620
697
  });
621
698
  const modelsWithSource = mergedModels.map(
@@ -708,8 +785,11 @@ export class ContentStore {
708
785
  );
709
786
  }
710
787
 
711
- getPresets(): Record<string, any> {
712
- return this.presets ?? {};
788
+ getPresets({ locale }: { locale?: string } = {}): Record<string, any> {
789
+ if (!this.presets || !locale) {
790
+ return this.presets ?? {};
791
+ }
792
+ return _.pickBy(this.presets, (preset) => !preset.locale || preset.locale === locale);
713
793
  }
714
794
 
715
795
  getContentSourceEnvironment({ srcProjectId, srcType }: { srcProjectId: string; srcType: string }): string {
@@ -839,11 +919,14 @@ export class ContentStore {
839
919
  return contentSourceData.documentMap[srcDocumentId];
840
920
  }
841
921
 
842
- getDocuments(): ContentStoreTypes.Document[] {
922
+ getDocuments({ locale }: { locale?: string }): ContentStoreTypes.Document[] {
843
923
  return _.reduce(
844
924
  this.contentSourceDataById,
845
925
  (documents: ContentStoreTypes.Document[], contentSourceData) => {
846
- return documents.concat(contentSourceData.documents);
926
+ const currentDocuments = _.isEmpty(locale)
927
+ ? contentSourceData.documents
928
+ : contentSourceData.documents.filter(document => !document.locale || document.locale === locale)
929
+ return documents.concat(currentDocuments);
847
930
  },
848
931
  []
849
932
  );
@@ -855,23 +938,33 @@ export class ContentStore {
855
938
  return contentSourceData.assetMap[srcAssetId];
856
939
  }
857
940
 
858
- getAssets(): ContentStoreTypes.Asset[] {
941
+ getAssets({ locale }: { locale?: string }): ContentStoreTypes.Asset[] {
859
942
  return _.reduce(
860
943
  this.contentSourceDataById,
861
944
  (assets: ContentStoreTypes.Asset[], contentSourceData) => {
862
- return assets.concat(contentSourceData.assets);
945
+ const currentAssets = _.isEmpty(locale)
946
+ ? contentSourceData.assets
947
+ : contentSourceData.assets.filter(asset => !asset.locale || asset.locale === locale)
948
+ return assets.concat(currentAssets);
863
949
  },
864
950
  []
865
951
  );
866
952
  }
867
953
 
868
954
  getLocalizedApiObjects({ locale }: { locale?: string }): ContentStoreTypes.APIObject[] {
955
+ const hasExplicitLocale = !_.isEmpty(locale);
869
956
  return _.reduce(
870
957
  this.contentSourceDataById,
871
958
  (objects: ContentStoreTypes.APIObject[], contentSourceData) => {
872
- locale = locale ?? contentSourceData.defaultLocaleCode;
873
- const documentObjects = mapDocumentsToLocalizedApiObjects(contentSourceData.documents, locale);
874
- const imageObjects = mapAssetsToLocalizedApiImages(contentSourceData.assets, locale);
959
+ const documents = hasExplicitLocale
960
+ ? contentSourceData.documents.filter(document => !document.locale || document.locale === locale)
961
+ : contentSourceData.documents;
962
+ const assets = hasExplicitLocale
963
+ ? contentSourceData.assets.filter(asset => !asset.locale || asset.locale === locale)
964
+ : contentSourceData.assets;
965
+ const currentLocale = locale ?? contentSourceData.defaultLocaleCode;
966
+ const documentObjects = mapDocumentsToLocalizedApiObjects(documents, currentLocale);
967
+ const imageObjects = mapAssetsToLocalizedApiImages(assets, currentLocale);
875
968
  return objects.concat(documentObjects, imageObjects);
876
969
  },
877
970
  []
@@ -1418,7 +1511,7 @@ export class ContentStore {
1418
1511
  locale = locale ?? contentSourceData.defaultLocaleCode;
1419
1512
  const { documents, assets } = getCSIDocumentsAndAssetsFromContentSourceDataByIds(contentSourceData, contentSourceObjects);
1420
1513
  const userContext = getUserContextForSrcType(contentSourceData.srcType, user);
1421
- const internalValidationErrors = internalValidateContent(documents, assets, contentSourceData);
1514
+ const internalValidationErrors = internalValidateContent(documents, assets, contentSourceData, locale);
1422
1515
  const validationResult = await contentSourceData.instance.validateDocuments({ documents, assets, locale, userContext });
1423
1516
  errors = errors.concat(
1424
1517
  internalValidationErrors,
@@ -1466,22 +1559,34 @@ export class ContentStore {
1466
1559
  items: ContentStoreTypes.Document[];
1467
1560
  }> {
1468
1561
  this.logger.debug('searchDocuments');
1469
-
1562
+ const locale = data.locale;
1470
1563
  const objectsBySourceId = _.groupBy(data.models, (object) => getContentSourceId(object.srcType, object.srcProjectId));
1564
+ const contentSourceIds = Object.keys(objectsBySourceId);
1471
1565
  const documents: ContentStoreTypes.Document[] = [];
1472
1566
  const schema: Record<string, Record<string, Record<string, Model>>> = {};
1567
+ const defaultLocales: Record<string, string> = {};
1473
1568
 
1474
- Object.keys(objectsBySourceId).forEach((contentSourceId) => {
1569
+ contentSourceIds.forEach((contentSourceId) => {
1475
1570
  const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
1476
-
1477
- documents.push(...contentSourceData.documents);
1571
+
1478
1572
  _.set(schema, [contentSourceData.srcType, contentSourceData.srcProjectId], contentSourceData.modelMap);
1573
+
1574
+ const contentSourceDocuments = _.isEmpty(locale)
1575
+ ? contentSourceData.documents
1576
+ : contentSourceData.documents.filter(document => !document.locale || document.locale === locale)
1577
+ documents.push(...contentSourceDocuments);
1578
+
1579
+ if (contentSourceData.defaultLocaleCode) {
1580
+ defaultLocales[contentSourceId] = contentSourceData.defaultLocaleCode;
1581
+ }
1479
1582
  });
1480
1583
 
1481
1584
  return searchDocuments({
1482
1585
  ...data,
1483
1586
  documents,
1484
- schema
1587
+ schema,
1588
+ locale,
1589
+ defaultLocales
1485
1590
  });
1486
1591
  }
1487
1592
 
@@ -1544,13 +1649,16 @@ function getCSIDocumentsAndAssetsFromContentSourceDataByIds(
1544
1649
  function internalValidateContent(
1545
1650
  documents: CSITypes.Document[],
1546
1651
  assets: CSITypes.Asset[],
1547
- contentSourceData: ContentSourceData
1652
+ contentSourceData: ContentSourceData,
1653
+ locale?: string
1548
1654
  ): ContentStoreTypes.ValidationError[] {
1549
1655
  const errors: ContentStoreTypes.ValidationError[] = [];
1550
1656
  _.forEach(documents, (document) => {
1551
1657
  _.forEach(document.fields, (documentField, fieldName) => {
1552
- const localizedField = getLocalizedFieldForLocale(documentField)!;
1553
- errors.push(...validateDocumentFields(document, localizedField, [fieldName], contentSourceData));
1658
+ const localizedField = getLocalizedFieldForLocale(documentField, locale);
1659
+ if (localizedField) {
1660
+ errors.push(...validateDocumentFields(document, localizedField, [fieldName], contentSourceData, locale));
1661
+ }
1554
1662
  });
1555
1663
  });
1556
1664
  return errors;
@@ -1560,19 +1668,24 @@ function validateDocumentFields(
1560
1668
  document: CSITypes.Document,
1561
1669
  documentField: CSITypes.DocumentFieldNonLocalized,
1562
1670
  fieldPath: (string | number)[],
1563
- contentSourceData: ContentSourceData
1671
+ contentSourceData: ContentSourceData,
1672
+ locale?: string
1564
1673
  ): ContentStoreTypes.ValidationError[] {
1565
1674
  const errors: ContentStoreTypes.ValidationError[] = [];
1566
1675
 
1567
1676
  if (documentField.type === 'object') {
1568
1677
  _.forEach(documentField.fields, (documentField, fieldName) => {
1569
- const localizedField = getLocalizedFieldForLocale(documentField)!;
1570
- errors.push(...validateDocumentFields(document, localizedField, fieldPath.concat(fieldName), contentSourceData));
1678
+ const localizedField = getLocalizedFieldForLocale(documentField, locale);
1679
+ if (localizedField) {
1680
+ errors.push(...validateDocumentFields(document, localizedField, fieldPath.concat(fieldName), contentSourceData, locale));
1681
+ }
1571
1682
  });
1572
1683
  } else if (documentField.type === 'model') {
1573
1684
  _.forEach(documentField.fields, (documentField, fieldName) => {
1574
- const localizedField = getLocalizedFieldForLocale(documentField)!;
1575
- errors.push(...validateDocumentFields(document, localizedField, fieldPath.concat(fieldName), contentSourceData));
1685
+ const localizedField = getLocalizedFieldForLocale(documentField, locale);
1686
+ if (localizedField) {
1687
+ errors.push(...validateDocumentFields(document, localizedField, fieldPath.concat(fieldName), contentSourceData, locale));
1688
+ }
1576
1689
  });
1577
1690
  } else if (documentField.type === 'reference') {
1578
1691
  const objRef = documentField.refType === 'asset' ? contentSourceData.assetMap[documentField.refId] : contentSourceData.documentMap[documentField.refId];
@@ -1588,8 +1701,7 @@ function validateDocumentFields(
1588
1701
  }
1589
1702
  } else if (documentField.type === 'list') {
1590
1703
  _.forEach(documentField.items, (documentField, i) => {
1591
- const localizedField = getLocalizedFieldForLocale(documentField)!;
1592
- errors.push(...validateDocumentFields(document, documentField, fieldPath.concat(i), contentSourceData));
1704
+ errors.push(...validateDocumentFields(document, documentField, fieldPath.concat(i), contentSourceData, locale));
1593
1705
  });
1594
1706
  }
1595
1707
 
@@ -48,6 +48,7 @@ function sourceAssetToStoreAsset({
48
48
  createdBy: csiAsset.createdBy,
49
49
  updatedAt: csiAsset.updatedAt,
50
50
  updatedBy: csiAsset.updatedBy,
51
+ locale: csiAsset.locale,
51
52
  fields: {
52
53
  title: {
53
54
  label: 'Title',
@@ -131,6 +132,7 @@ function mapCSIDocumentToStoreDocument({
131
132
  createdBy: csiDocument.createdBy,
132
133
  updatedAt: csiDocument.updatedAt,
133
134
  updatedBy: csiDocument.updatedBy,
135
+ locale: csiDocument.locale,
134
136
  fields: mapCSIFieldsToStoreFields({
135
137
  csiDocumentFields: csiDocument.fields,
136
138
  modelFields: model.fields ?? [],
@@ -163,7 +165,16 @@ function mapCSIFieldsToStoreFields({
163
165
  modelField,
164
166
  context
165
167
  });
168
+ // Override document field types with specific model field types.
169
+ // For example when developer re-mapped content-source model "string"
170
+ // field to stackbit "color" field.
171
+ if (modelField.type === 'color' || modelField.type === 'style') {
172
+ docField.type = modelField.type;
173
+ }
166
174
  docField.label = modelField.label;
175
+ if ('localized' in modelField) {
176
+ docField.localized = modelField.localized;
177
+ }
167
178
  result[modelField.name] = docField;
168
179
  return result;
169
180
  }, {});
@@ -14,7 +14,7 @@ export function mergeObjectWithDocument({
14
14
  duplicatableModels,
15
15
  nonDuplicatableModels
16
16
  }: {
17
- object: Record<string, any> | undefined;
17
+ object: Record<string, unknown> | undefined;
18
18
  document: ContentStoreTypes.Document;
19
19
  locale?: string;
20
20
  documentMap: Record<string, ContentStoreTypes.Document>;
@@ -22,7 +22,7 @@ export function mergeObjectWithDocument({
22
22
  referenceBehavior?: 'copyReference' | 'duplicateContents';
23
23
  duplicatableModels?: string[];
24
24
  nonDuplicatableModels?: string[];
25
- }): Record<string, any> {
25
+ }): Record<string, unknown> {
26
26
  return mergeObjectWithDocumentFields({
27
27
  object,
28
28
  documentFields: document.fields,
@@ -49,9 +49,9 @@ function mergeObjectWithDocumentFields({
49
49
  documentFields,
50
50
  ...context
51
51
  }: {
52
- object: Record<string, any> | undefined;
52
+ object: Record<string, unknown> | undefined;
53
53
  documentFields: Record<string, ContentStoreTypes.DocumentField>;
54
- } & Context): Record<string, any> {
54
+ } & Context): Record<string, unknown> {
55
55
  return _.reduce(
56
56
  documentFields,
57
57
  (object, documentField, fieldName) => {
@@ -74,9 +74,9 @@ function mergeObjectWithDocumentField({
74
74
  documentField,
75
75
  ...context
76
76
  }: {
77
- value: any;
77
+ value: unknown;
78
78
  documentField: ContentStoreTypes.DocumentField;
79
- } & Context): any {
79
+ } & Context): unknown {
80
80
  const locale = context.locale;
81
81
  switch (documentField.type) {
82
82
  case 'string':
@@ -108,7 +108,7 @@ function mergeObjectWithDocumentField({
108
108
  }
109
109
  case 'image': {
110
110
  const localizedField = getDocumentFieldForLocale(documentField, locale);
111
- if (localizedField && !localizedField.isUnset) {
111
+ if (localizedField && !localizedField.isUnset && isPlainObjectOrUndefined(value)) {
112
112
  return mergeObjectWithDocumentFields({
113
113
  object: value,
114
114
  documentFields: localizedField.fields,
@@ -119,7 +119,7 @@ function mergeObjectWithDocumentField({
119
119
  }
120
120
  case 'object': {
121
121
  const localizedField = getDocumentFieldForLocale(documentField, locale);
122
- if (localizedField && !localizedField.isUnset) {
122
+ if (localizedField && !localizedField.isUnset && isPlainObjectOrUndefined(value)) {
123
123
  return mergeObjectWithDocumentFields({
124
124
  object: value,
125
125
  documentFields: localizedField.fields,
@@ -130,7 +130,7 @@ function mergeObjectWithDocumentField({
130
130
  }
131
131
  case 'model': {
132
132
  const localizedField = getDocumentFieldForLocale(documentField, locale);
133
- if (localizedField && !localizedField.isUnset) {
133
+ if (localizedField && !localizedField.isUnset && isPlainObjectOrUndefined(value)) {
134
134
  if (value && value.$$type !== localizedField.srcModelName) {
135
135
  // if the override object's $$type isn't equal to the type
136
136
  // of the current object in the field, then use whatever
@@ -150,7 +150,7 @@ function mergeObjectWithDocumentField({
150
150
  }
151
151
  case 'reference': {
152
152
  const localizedField = getDocumentFieldForLocale(documentField, locale);
153
- if (localizedField && !localizedField.isUnset) {
153
+ if (localizedField && !localizedField.isUnset && isPlainObjectOrUndefined(value)) {
154
154
  if (value && value.$$ref) {
155
155
  // if the override object has $$ref, use it
156
156
  break;
@@ -230,6 +230,10 @@ function mergeObjectWithDocumentField({
230
230
  return value;
231
231
  }
232
232
 
233
+ function isPlainObjectOrUndefined(value: unknown): value is Record<string, unknown> | undefined {
234
+ return typeof value === 'undefined' || _.isPlainObject(value);
235
+ }
236
+
233
237
  function shouldDuplicate({
234
238
  referenceField,
235
239
  modelName,
@@ -5,6 +5,7 @@ import { getLocalizedFieldForLocale } from '@stackbit/types';
5
5
 
6
6
  import { SearchFilter, SearchFilterItem } from '../types/search-filter';
7
7
  import { ContentStoreTypes } from '..';
8
+ import { getContentSourceId } from '../content-store-utils';
8
9
 
9
10
  const META_FIELD = {
10
11
  createdAt: {
@@ -28,6 +29,7 @@ export const searchDocuments = (data: {
28
29
  documents: ContentStoreTypes.Document[];
29
30
  schema: Schema;
30
31
  locale?: string;
32
+ defaultLocales?: Record<string, string>
31
33
  }): {
32
34
  total: number;
33
35
  items: ContentStoreTypes.Document[];
@@ -39,6 +41,7 @@ export const searchDocuments = (data: {
39
41
  let allDocuments = 0;
40
42
 
41
43
  const matchedDocuments = documents.filter((document) => {
44
+ const contentSourceId = getContentSourceId(document.srcType, document.srcProjectId);
42
45
  const isIncludedModel = _.find(data.models, {
43
46
  srcType: document.srcType,
44
47
  srcProjectId: document.srcProjectId,
@@ -51,7 +54,7 @@ export const searchDocuments = (data: {
51
54
  allDocuments += 1;
52
55
 
53
56
  if (query) {
54
- const matches = isDocumentMatchesPattern(document, query, data.locale);
57
+ const matches = isDocumentMatchesPattern(document, query, data.locale ?? data.defaultLocales?.[contentSourceId]);
55
58
  if (!matches) {
56
59
  return false;
57
60
  }
@@ -61,7 +64,7 @@ export const searchDocuments = (data: {
61
64
  // only 'and' supported for now; later we can add e.g. 'or'
62
65
  const matches = data.filter.and.every((filter) => {
63
66
  const field = getFieldForFilter(document, filter);
64
- return isFieldMatchesFilter({ field, filter, document, schema, locale: data.locale });
67
+ return isFieldMatchesFilter({ field, filter, document, schema, locale: data.locale ?? data.defaultLocales?.[contentSourceId] });
65
68
  });
66
69
 
67
70
  if (!matches) {