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

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,8 +70,7 @@ import {
69
70
  logUploadAssets,
70
71
  pluralize
71
72
  } from './utils/user-log-utils';
72
- import { getSanitizedTreeViews } from './utils/tree-views';
73
- import { ContentEnginePublicAPI, PluginRef, contentEngine } from 'content-engine';
73
+ import { ContentEngine, 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 }>;
76
76
 
@@ -148,7 +148,7 @@ export class ContentStore {
148
148
  private contentStoreEventQueue: ContentStoreEventQueue = [];
149
149
  private treeViews: CSITypes.TreeViewNode[] = [];
150
150
  private customActionRunStateMap: ContentStoreTypes.CustomActionRunStateMap = {};
151
- private contentEngine?: ContentEnginePublicAPI | null;
151
+ private contentEngine?: ContentEngine | null;
152
152
 
153
153
  constructor(options: ContentSourceOptions) {
154
154
  this.logger = options.logger.createLogger({ label: 'content-store' });
@@ -409,6 +409,8 @@ export class ContentStore {
409
409
  logger.info('creating content engine');
410
410
  this.contentEngine = contentEngine({
411
411
  directory: this.stackbitConfig?.dirPath,
412
+ port: this.stackbitConfig?.contentEngine?.port,
413
+ host: this.stackbitConfig?.contentEngine?.host,
412
414
  engineConfig: {
413
415
  plugins
414
416
  }
@@ -1818,20 +1820,13 @@ export class ContentStore {
1818
1820
  throw new Error(`Document not found: '${srcDocumentId}'. Source: '${contentSourceData.id}'.`);
1819
1821
  }
1820
1822
 
1821
- // get the document model
1822
- const documentModelName = document.srcModelName;
1823
1823
  const modelMap = contentSourceData.modelMap;
1824
- const model = modelMap[documentModelName];
1825
1824
  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
1825
 
1831
1826
  // get the 'reference' model field in the updated document that will be used to link the new document
1832
1827
  locale = locale ?? contentSourceData.defaultLocaleCode;
1833
- const modelField = getModelFieldForFieldAtPath(document, model, fieldPath, modelMap, locale);
1834
- const csiModelField = getModelFieldForFieldAtPath(document, csiModel, fieldPath, csiModelMap, locale);
1828
+ const modelField = getModelFieldAtFieldPath(document, fieldPath, modelMap, locale);
1829
+ const csiModelField = getModelFieldAtFieldPath(document, fieldPath, csiModelMap, locale);
1835
1830
  if (!modelField || !csiModelField) {
1836
1831
  throw Error(`Field path not found:'${fieldPath.join('.')}'.`);
1837
1832
  }
@@ -2020,17 +2015,11 @@ export class ContentStore {
2020
2015
  throw new Error(`Document not found: '${srcDocumentId}'. Source: '${contentSourceData.id}'.`);
2021
2016
  }
2022
2017
 
2023
- // get the document model
2024
- const documentModelName = document.srcModelName;
2025
2018
  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
2019
 
2031
2020
  // get the 'reference' model field in the updated document that will be used to link the new asset
2032
2021
  locale = locale ?? contentSourceData.defaultLocaleCode;
2033
- const csiModelField = getModelFieldForFieldAtPath(document, csiModel, fieldPath, csiModelMap, locale);
2022
+ const csiModelField = getModelFieldAtFieldPath(document, fieldPath, csiModelMap, locale);
2034
2023
  if (!csiModelField) {
2035
2024
  throw Error(`Field path not found: '${fieldPath.join('.')}'.`);
2036
2025
  }
@@ -2181,16 +2170,15 @@ export class ContentStore {
2181
2170
  const modelMap = contentSourceData.modelMap;
2182
2171
  const csiModelMap = contentSourceData.csiModelMap;
2183
2172
  const documentModelName = document.srcModelName;
2184
- const model = modelMap[documentModelName];
2185
2173
  const csiModel = csiModelMap[documentModelName];
2186
- if (!model || !csiModel) {
2174
+ if (!csiModel) {
2187
2175
  throw new Error(`Error updating document: could not find document model '${documentModelName}'.`);
2188
2176
  }
2189
2177
 
2190
2178
  const operations = await mapPromise(updateOperations, async (updateOperation): Promise<CSITypes.UpdateOperation> => {
2191
2179
  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);
2180
+ const modelField = getModelFieldAtFieldPath(document, updateOperation.fieldPath, modelMap, locale);
2181
+ const csiModelField = getModelFieldAtFieldPath(document, updateOperation.fieldPath, csiModelMap, locale);
2194
2182
  switch (updateOperation.opType) {
2195
2183
  case 'set': {
2196
2184
  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
  });