@stackbit/cms-core 0.8.7-develop.1 → 0.8.7-develop.2

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.
@@ -92,373 +92,6 @@ export function getDocumentFieldForLocale<Type extends FieldType>(
92
92
  }
93
93
  }
94
94
 
95
- export function getModelFieldForFieldAtPath(
96
- document: ContentStoreTypes.Document,
97
- model: Model,
98
- fieldPath: (string | number)[],
99
- modelMap: Record<string, Model>,
100
- locale?: string
101
- ): Field | FieldListItems {
102
- if (_.isEmpty(fieldPath)) {
103
- throw new Error('the fieldPath can not be empty');
104
- }
105
-
106
- function getField(docField: ContentStoreTypes.DocumentField, modelField: Field | FieldListItems, fieldPath: (string | number)[]): Field | FieldListItems {
107
- const fieldName = _.head(fieldPath);
108
- if (typeof fieldName === 'undefined') {
109
- throw new Error('the first fieldPath item must be string');
110
- }
111
- const childFieldPath = _.tail(fieldPath);
112
- let childDocField: ContentStoreTypes.DocumentField | undefined;
113
- let childModelField: Field | undefined;
114
- switch (docField.type) {
115
- case 'object': {
116
- const localizedObjectField = getDocumentFieldForLocale(docField, locale);
117
- if (!localizedObjectField) {
118
- throw new Error(`locale for field was not found`);
119
- }
120
- if (localizedObjectField.isUnset) {
121
- throw new Error(`field is not set`);
122
- }
123
- childDocField = localizedObjectField.fields[fieldName];
124
- childModelField = _.find((modelField as FieldObjectProps).fields, (field) => field.name === fieldName);
125
- if (!childDocField || !childModelField) {
126
- throw new Error(`field ${fieldName} doesn't exist`);
127
- }
128
- if (childFieldPath.length === 0) {
129
- return childModelField;
130
- }
131
- return getField(childDocField, childModelField, childFieldPath);
132
- }
133
- case 'model': {
134
- const localizedModelField = getDocumentFieldForLocale(docField, locale);
135
- if (!localizedModelField) {
136
- throw new Error(`locale for field was not found`);
137
- }
138
- if (localizedModelField.isUnset) {
139
- throw new Error(`field is not set`);
140
- }
141
- const modelName = localizedModelField.srcModelName;
142
- const childModel = modelMap[modelName];
143
- if (!childModel) {
144
- throw new Error(`model ${modelName} doesn't exist`);
145
- }
146
- childModelField = _.find(childModel.fields, (field) => field.name === fieldName);
147
- childDocField = localizedModelField.fields![fieldName];
148
- if (!childDocField || !childModelField) {
149
- throw new Error(`field ${fieldName} doesn't exist`);
150
- }
151
- if (childFieldPath.length === 0) {
152
- return childModelField;
153
- }
154
- return getField(childDocField, childModelField!, childFieldPath);
155
- }
156
- case 'list': {
157
- const localizedListField = getDocumentFieldForLocale(docField, locale);
158
- if (!localizedListField) {
159
- throw new Error(`locale for field was not found`);
160
- }
161
- const listItem = localizedListField.items && localizedListField.items[fieldName as number];
162
- const listItemsModel = (modelField as FieldListProps).items;
163
- if (!listItem || !listItemsModel) {
164
- throw new Error(`field ${fieldName} doesn't exist`);
165
- }
166
- if (childFieldPath.length === 0) {
167
- return listItemsModel;
168
- }
169
- if (!Array.isArray(listItemsModel)) {
170
- return getField(listItem, listItemsModel, childFieldPath);
171
- } else {
172
- const fieldListItems = (listItemsModel as FieldListItems[]).find((listItemsModel) => listItemsModel.type === listItem.type);
173
- if (!fieldListItems) {
174
- throw new Error('cannot find matching field model');
175
- }
176
- return getField(listItem, fieldListItems, childFieldPath);
177
- }
178
- }
179
- default:
180
- if (!_.isEmpty(childFieldPath)) {
181
- throw new Error('illegal fieldPath');
182
- }
183
- return modelField;
184
- }
185
- }
186
-
187
- const fieldName = _.head(fieldPath);
188
- const childFieldPath = _.tail(fieldPath);
189
-
190
- if (typeof fieldName !== 'string') {
191
- throw new Error('the first fieldPath item must be string');
192
- }
193
-
194
- const childDocField = document.fields[fieldName];
195
- const childModelField = _.find(model.fields, { name: fieldName });
196
-
197
- if (!childDocField || !childModelField) {
198
- throw new Error(`field ${fieldName} doesn't exist`);
199
- }
200
-
201
- if (childFieldPath.length === 0) {
202
- return childModelField;
203
- }
204
-
205
- return getField(childDocField, childModelField, childFieldPath);
206
- }
207
-
208
- /**
209
- * Returns a model field and a document field at the specified field path.
210
- *
211
- * If some fields along the fieldPath are localized, the fieldPath must
212
- * container the locale of the field under the "locales" property. The locales
213
- * along the field path don't have to be the same.
214
- * @example
215
- * fieldPath: ['fields', 'button', 'locales', 'en', 'fields', 'title', 'locales', 'es']
216
- *
217
- * If the provided fieldPath points to a list item, the returned model field
218
- * and document field will belong to a list item. In this case, the model field
219
- * will contain only field-specific properties and the document field will be
220
- * localized.
221
- * @example
222
- * fieldPath: ['fields', 'buttons', 'items', 2]
223
- *
224
- * The "fullFieldPath" flag specifies if the fieldPath includes container specifiers
225
- * such as "fields", "items" and "locales".
226
- * @example
227
- * fullFieldPath: false => fieldPath: ['sections', 1, 'title', 'es']
228
- * fullFieldPath: true => fieldPath: ['fields', 'sections', 'items', 1, 'fields', 'title', 'locales', 'es']
229
- *
230
- * @param document
231
- * @param model
232
- * @param fieldPath
233
- * @param modelMap
234
- * @param fullFieldPath
235
- */
236
- export function getModelAndDocumentFieldForLocalizedFieldPath({
237
- document,
238
- model,
239
- fieldPath,
240
- modelMap,
241
- fullFieldPath
242
- }: {
243
- document: ContentStoreTypes.Document;
244
- model: Model;
245
- fieldPath: (string | number)[];
246
- modelMap: Record<string, Model>;
247
- fullFieldPath?: boolean;
248
- }): { modelField: Field | FieldListItems; documentField: ContentStoreTypes.DocumentField | ContentStoreTypes.DocumentListFieldItems } {
249
- if (fullFieldPath) {
250
- if (_.head(fieldPath) !== 'fields') {
251
- throw new Error('fieldPath must start with "fields" specifier');
252
- }
253
- fieldPath = _.tail(fieldPath);
254
- }
255
- if (_.isEmpty(fieldPath)) {
256
- throw new Error('the fieldPath can not be empty');
257
- }
258
-
259
- const origModel = model;
260
- const origFieldPath = fieldPath;
261
- let modelFieldPath = fieldPath;
262
- function getPrefixOf(fieldPath: (string | number)[], include = 0) {
263
- return origFieldPath.slice(0, origFieldPath.length - fieldPath.length + include).join('.');
264
- }
265
- function getModelPrefixOf(fieldPath: (string | number)[], include = 0) {
266
- return modelFieldPath.slice(0, modelFieldPath.length - fieldPath.length + include).join('.');
267
- }
268
-
269
- function getField(
270
- docField: ContentStoreTypes.DocumentField | ContentStoreTypes.DocumentListFieldItems,
271
- modelField: Field | FieldListItems,
272
- fieldPath: (string | number)[]
273
- ): { modelField: Field | FieldListItems; documentField: ContentStoreTypes.DocumentField | ContentStoreTypes.DocumentListFieldItems } {
274
- // if no more items in fieldPath return the found document and model fields
275
- if (fieldPath.length === 0) {
276
- return {
277
- modelField: modelField,
278
- documentField: docField
279
- };
280
- }
281
-
282
- if (docField.localized) {
283
- if (fullFieldPath) {
284
- if (_.head(fieldPath) !== 'locales') {
285
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must contain "locales" specifier for localized field`);
286
- }
287
- fieldPath = _.tail(fieldPath);
288
- }
289
- const locale = _.head(fieldPath);
290
- if (typeof locale !== 'string') {
291
- throw new Error(`the locale specifier must be string in fieldPath '${origFieldPath.join('.')}'`);
292
- }
293
- fieldPath = _.tail(fieldPath);
294
- const localizedDocField = getDocumentFieldForLocale(docField, locale);
295
- if (!localizedDocField) {
296
- throw new Error(`fieldPath '${origFieldPath.join('.')}' doesn't specify the locale`);
297
- }
298
- docField = localizedDocField;
299
- }
300
-
301
- switch (docField.type) {
302
- case 'object': {
303
- if (docField.isUnset) {
304
- throw new Error(
305
- `fieldPath '${origFieldPath.join('.')}' points to the fields of unset "object" field at path '${getPrefixOf(
306
- fieldPath
307
- )}' of a document '${document.srcObjectId}'`
308
- );
309
- }
310
- if (fullFieldPath) {
311
- if (_.head(fieldPath) !== 'fields') {
312
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must contain "fields" specifier for an "object" field`);
313
- }
314
- fieldPath = _.tail(fieldPath);
315
- }
316
- const fieldName = _.head(fieldPath);
317
- if (typeof fieldName === 'undefined') {
318
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must specify a field name for an "object" field`);
319
- }
320
- fieldPath = _.tail(fieldPath);
321
-
322
- const childDocField = docField.fields[fieldName];
323
- if (!childDocField) {
324
- throw new Error(
325
- `document '${document.srcObjectId}' of type '${origModel.name}' doesn't have a field at path: '${getPrefixOf(fieldPath, 1)}'`
326
- );
327
- }
328
- if (modelField.type !== 'object') {
329
- throw new Error(
330
- `field type '${modelField.type}' of a model '${model.name}' at field path '${getModelPrefixOf(
331
- fieldPath,
332
- 1
333
- )}' doesn't match document field type 'object'`
334
- );
335
- }
336
- const childModelField = _.find(modelField.fields, (field) => field.name === fieldName);
337
- if (!childModelField) {
338
- throw new Error(`model '${model.name}' doesn't have a field at path: '${getModelPrefixOf(fieldPath, 1)}'`);
339
- }
340
- return getField(childDocField, childModelField, fieldPath);
341
- }
342
- case 'model': {
343
- if (docField.isUnset) {
344
- throw new Error(
345
- `fieldPath '${origFieldPath.join('.')}' points to the fields of unset "model" field at path '${getPrefixOf(
346
- fieldPath
347
- )}' of a document '${document.srcObjectId}'`
348
- );
349
- }
350
- modelFieldPath = fieldPath;
351
- if (fullFieldPath) {
352
- if (_.head(fieldPath) !== 'fields') {
353
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must contain "fields" specifier for a "model" field`);
354
- }
355
- fieldPath = _.tail(fieldPath);
356
- }
357
- const fieldName = _.head(fieldPath);
358
- if (typeof fieldName === 'undefined') {
359
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must specify a field name for an "model" field`);
360
- }
361
- fieldPath = _.tail(fieldPath);
362
-
363
- const modelName = docField.srcModelName;
364
- const childModel = modelMap[modelName];
365
- if (!childModel) {
366
- throw new Error(
367
- `a "model" field of a document '${document.srcObjectId}' at path '${getPrefixOf(
368
- fieldPath,
369
- 1
370
- )}' contains an object with non existing modelName '${modelName}'`
371
- );
372
- }
373
- model = childModel;
374
- const childDocField = docField.fields[fieldName];
375
- if (!childDocField) {
376
- throw new Error(
377
- `document '${document.srcObjectId}' of type '${origModel.name}' doesn't have a field at path: '${getPrefixOf(fieldPath, 1)}'`
378
- );
379
- }
380
- const childModelField = _.find(childModel.fields, (field) => field.name === fieldName);
381
- if (!childModelField) {
382
- throw new Error(`model '${childModel.name}' doesn't have a field at path: '${getModelPrefixOf(fieldPath, 1)}'`);
383
- }
384
- return getField(childDocField, childModelField, fieldPath);
385
- }
386
- case 'list': {
387
- if (fullFieldPath) {
388
- if (_.head(fieldPath) !== 'items') {
389
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must contain "items" specifier for a "list" field`);
390
- }
391
- fieldPath = _.tail(fieldPath);
392
- }
393
- const itemIndex = _.head(fieldPath) as number;
394
- if (typeof itemIndex === 'undefined') {
395
- throw new Error(`fieldPath '${origFieldPath.join('.')}' must specify a list item index for a "list" field`);
396
- }
397
- fieldPath = _.tail(fieldPath);
398
-
399
- const listItem = docField.items && docField.items[itemIndex as number];
400
- if (!listItem) {
401
- throw new Error(
402
- `document '${document.srcObjectId}' of type '${origModel.name}' doesn't have a list item at path: '${getPrefixOf(fieldPath, 1)}'`
403
- );
404
- }
405
- if (modelField.type !== 'list') {
406
- throw new Error(
407
- `field type '${modelField.type}' of a model '${model.name}' at field path '${getModelPrefixOf(
408
- fieldPath,
409
- 1
410
- )}' doesn't match document field type 'list'`
411
- );
412
- }
413
- const listItemsModel = modelField.items;
414
- if (!Array.isArray(listItemsModel)) {
415
- return getField(listItem, listItemsModel, fieldPath);
416
- } else {
417
- const fieldListItems = (listItemsModel as FieldListItems[]).find((listItemsModel) => listItemsModel.type === listItem.type);
418
- if (!fieldListItems) {
419
- throw new Error(
420
- `cannot find list item model for document field of type '${listItem.type}' at field path: '${getModelPrefixOf(fieldPath, 1)}'`
421
- );
422
- }
423
- return getField(listItem, fieldListItems, fieldPath);
424
- }
425
- }
426
- default:
427
- if (!_.isEmpty(fieldPath)) {
428
- throw new Error(
429
- `field path '${getModelPrefixOf(fieldPath, 1)}' of a model '${model.name}' points to a primitive field '${
430
- docField.type
431
- }' while having more path parts`
432
- );
433
- }
434
- return {
435
- modelField,
436
- documentField: docField
437
- };
438
- }
439
- }
440
-
441
- const fieldName = _.head(fieldPath);
442
- const childFieldPath = _.tail(fieldPath);
443
-
444
- if (typeof fieldName !== 'string') {
445
- throw new Error('the first fieldPath item must be string');
446
- }
447
-
448
- const childDocField: ContentStoreTypes.DocumentField | undefined = document.fields[fieldName];
449
- const childModelField: Field | undefined = _.find(model.fields, { name: fieldName });
450
-
451
- if (!childDocField) {
452
- throw new Error(`document '${document.srcObjectId}' of type '${model.name}' doesn't have a field at path: '${fieldName}'`);
453
- }
454
-
455
- if (!childModelField) {
456
- throw new Error(`model '${model.name}' doesn't have a field at path: '${fieldName}'`);
457
- }
458
-
459
- return getField(childDocField, childModelField, childFieldPath);
460
- }
461
-
462
95
  export function groupModelsByContentSource({ models }: { models: CSITypes.ModelWithSource[] }): Record<string, Record<string, Record<string, Model>>> {
463
96
  const modelMapByContentSource: Record<string, Record<string, Record<string, Model>>> = {};
464
97
  for (const model of models) {
@@ -30,7 +30,6 @@ import {
30
30
  getContentSourceDataByIdOrThrow,
31
31
  getContentSourceIdForContentSource,
32
32
  getCSIDocumentsAndAssetsFromContentSourceDataByIds,
33
- getModelFieldForFieldAtPath,
34
33
  getUserContextForSrcType,
35
34
  groupDocumentsByContentSource,
36
35
  groupModelsByContentSource,
@@ -60,6 +59,8 @@ import { GitService } from './services';
60
59
  import { getAssetSourcesForClient } from './utils/asset-sources-utils';
61
60
  import { deleteDocumentHooked, publishDocumentHooked, updateDocumentHooked } from './utils/document-hooks';
62
61
  import { resolveCustomActionsById, getGlobalAndBulkAPIActions, runCustomAction, stripModelActions } from './utils/custom-actions';
62
+ import { getSanitizedTreeViews } from './utils/tree-views';
63
+ import { getModelFieldAtFieldPath } from './utils/field-path-utils';
63
64
  import {
64
65
  logCreateDocument,
65
66
  logDeleteDocument,
@@ -69,7 +70,6 @@ import {
69
70
  logUploadAssets,
70
71
  pluralize
71
72
  } from './utils/user-log-utils';
72
- import { getSanitizedTreeViews } from './utils/tree-views';
73
73
  import { ContentEnginePublicAPI, PluginRef, contentEngine } from 'content-engine';
74
74
 
75
75
  export type HandleConfigAssets = <T extends Model>({ models, presets }: { models?: T[]; presets?: PresetMap }) => Promise<{ models: T[]; presets: PresetMap }>;
@@ -1818,20 +1818,13 @@ export class ContentStore {
1818
1818
  throw new Error(`Document not found: '${srcDocumentId}'. Source: '${contentSourceData.id}'.`);
1819
1819
  }
1820
1820
 
1821
- // get the document model
1822
- const documentModelName = document.srcModelName;
1823
1821
  const modelMap = contentSourceData.modelMap;
1824
- const model = modelMap[documentModelName];
1825
1822
  const csiModelMap = contentSourceData.csiModelMap;
1826
- const csiModel = csiModelMap[documentModelName];
1827
- if (!model || !csiModel) {
1828
- throw new Error(`Error updating document: could not find document model '${documentModelName}'.`);
1829
- }
1830
1823
 
1831
1824
  // get the 'reference' model field in the updated document that will be used to link the new document
1832
1825
  locale = locale ?? contentSourceData.defaultLocaleCode;
1833
- const modelField = getModelFieldForFieldAtPath(document, model, fieldPath, modelMap, locale);
1834
- const csiModelField = getModelFieldForFieldAtPath(document, csiModel, fieldPath, csiModelMap, locale);
1826
+ const modelField = getModelFieldAtFieldPath(document, fieldPath, modelMap, locale);
1827
+ const csiModelField = getModelFieldAtFieldPath(document, fieldPath, csiModelMap, locale);
1835
1828
  if (!modelField || !csiModelField) {
1836
1829
  throw Error(`Field path not found:'${fieldPath.join('.')}'.`);
1837
1830
  }
@@ -2020,17 +2013,11 @@ export class ContentStore {
2020
2013
  throw new Error(`Document not found: '${srcDocumentId}'. Source: '${contentSourceData.id}'.`);
2021
2014
  }
2022
2015
 
2023
- // get the document model
2024
- const documentModelName = document.srcModelName;
2025
2016
  const csiModelMap = contentSourceData.csiModelMap;
2026
- const csiModel = csiModelMap[documentModelName];
2027
- if (!csiModel) {
2028
- throw new Error(`Error updating document: could not find document model '${documentModelName}'.`);
2029
- }
2030
2017
 
2031
2018
  // get the 'reference' model field in the updated document that will be used to link the new asset
2032
2019
  locale = locale ?? contentSourceData.defaultLocaleCode;
2033
- const csiModelField = getModelFieldForFieldAtPath(document, csiModel, fieldPath, csiModelMap, locale);
2020
+ const csiModelField = getModelFieldAtFieldPath(document, fieldPath, csiModelMap, locale);
2034
2021
  if (!csiModelField) {
2035
2022
  throw Error(`Field path not found: '${fieldPath.join('.')}'.`);
2036
2023
  }
@@ -2181,16 +2168,15 @@ export class ContentStore {
2181
2168
  const modelMap = contentSourceData.modelMap;
2182
2169
  const csiModelMap = contentSourceData.csiModelMap;
2183
2170
  const documentModelName = document.srcModelName;
2184
- const model = modelMap[documentModelName];
2185
2171
  const csiModel = csiModelMap[documentModelName];
2186
- if (!model || !csiModel) {
2172
+ if (!csiModel) {
2187
2173
  throw new Error(`Error updating document: could not find document model '${documentModelName}'.`);
2188
2174
  }
2189
2175
 
2190
2176
  const operations = await mapPromise(updateOperations, async (updateOperation): Promise<CSITypes.UpdateOperation> => {
2191
2177
  const locale = updateOperation.locale ?? contentSourceData.defaultLocaleCode;
2192
- const modelField = getModelFieldForFieldAtPath(document, model, updateOperation.fieldPath, modelMap, locale);
2193
- const csiModelField = getModelFieldForFieldAtPath(document, csiModel, updateOperation.fieldPath, csiModelMap, locale);
2178
+ const modelField = getModelFieldAtFieldPath(document, updateOperation.fieldPath, modelMap, locale);
2179
+ const csiModelField = getModelFieldAtFieldPath(document, updateOperation.fieldPath, csiModelMap, locale);
2194
2180
  switch (updateOperation.opType) {
2195
2181
  case 'set': {
2196
2182
  const field = await convertOperationField({
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@ export * as stackbit from './stackbit';
2
2
  export * as annotator from './annotator';
3
3
  export * as utils from './utils';
4
4
  export * as searchUtils from './utils/search-utils';
5
+ export * from './utils/field-path-utils';
6
+ export { getDocumentFieldForLocale } from './content-store-utils';
5
7
  export * as consts from './consts';
6
8
  export * from './common/common-schema';
7
9
  export * from './common/common-types';
@@ -25,11 +25,8 @@ import {
25
25
  import { createConfigDelegate } from './config-delegate';
26
26
  import { getContentSourceActionsForSourceThunk } from './document-hooks';
27
27
  import { mapStoreDocumentToCSIDocumentWithSource, mapStoreFieldToCSIField } from './store-to-csi-docs-converter';
28
- import {
29
- getContentSourceDataByTypeAndProjectIdOrThrow,
30
- getModelAndDocumentFieldForLocalizedFieldPath,
31
- getUserContextForSrcTypeThunk
32
- } from '../content-store-utils';
28
+ import { getModelAndDocumentFieldForLocalizedFieldPath } from './field-path-utils';
29
+ import { getContentSourceDataByTypeAndProjectIdOrThrow, getUserContextForSrcTypeThunk } from '../content-store-utils';
33
30
 
34
31
  /**
35
32
  * Removes "run" and "state" functions from actions of models, nested objects and fields.
@@ -731,7 +728,6 @@ function getHandlerParamsForFieldAction({
731
728
  // the documentField should be localized because fieldPath includes locales
732
729
  const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
733
730
  document,
734
- model,
735
731
  fieldPath: extendedAction.fieldPath,
736
732
  modelMap: contentSourceData.modelMap
737
733
  }) as {
@@ -864,7 +860,6 @@ function findCustomActionById({
864
860
  } else {
865
861
  const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
866
862
  document,
867
- model,
868
863
  fieldPath,
869
864
  modelMap: contentSourceData.modelMap
870
865
  });