@stackbit/sdk 0.2.22 → 0.2.26

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 (36) hide show
  1. package/dist/config/config-consts.d.ts +1 -1
  2. package/dist/config/config-consts.js +2 -0
  3. package/dist/config/config-consts.js.map +1 -1
  4. package/dist/config/config-loader.d.ts +5 -4
  5. package/dist/config/config-loader.js +291 -99
  6. package/dist/config/config-loader.js.map +1 -1
  7. package/dist/config/config-schema.js +27 -3
  8. package/dist/config/config-schema.js.map +1 -1
  9. package/dist/config/config-types.d.ts +23 -4
  10. package/dist/config/config-writer.js +3 -0
  11. package/dist/config/config-writer.js.map +1 -1
  12. package/dist/config/presets-loader.js +17 -11
  13. package/dist/config/presets-loader.js.map +1 -1
  14. package/dist/content/content-loader.js +1 -1
  15. package/dist/content/content-schema.js +8 -0
  16. package/dist/content/content-schema.js.map +1 -1
  17. package/dist/utils/model-extender.js.map +1 -1
  18. package/dist/utils/model-iterators.d.ts +61 -1
  19. package/dist/utils/model-iterators.js +60 -11
  20. package/dist/utils/model-iterators.js.map +1 -1
  21. package/dist/utils/model-utils.d.ts +44 -3
  22. package/dist/utils/model-utils.js +93 -10
  23. package/dist/utils/model-utils.js.map +1 -1
  24. package/package.json +2 -2
  25. package/src/.DS_Store +0 -0
  26. package/src/config/config-consts.ts +2 -0
  27. package/src/config/config-loader.ts +327 -111
  28. package/src/config/config-schema.ts +34 -4
  29. package/src/config/config-types.ts +27 -4
  30. package/src/config/config-writer.ts +3 -0
  31. package/src/config/presets-loader.ts +18 -15
  32. package/src/content/content-loader.ts +2 -2
  33. package/src/content/content-schema.ts +9 -0
  34. package/src/utils/model-extender.ts +1 -1
  35. package/src/utils/model-iterators.ts +61 -13
  36. package/src/utils/model-utils.ts +91 -8
@@ -20,7 +20,10 @@ import {
20
20
  FieldGroupItem,
21
21
  YamlObjectModel,
22
22
  ContentModelMap,
23
- ContentModel
23
+ ContentModel,
24
+ ModelsSourceFiles,
25
+ ModelsSourceContentful,
26
+ ModelsSourceSanity
24
27
  } from './config-types';
25
28
 
26
29
  function getConfigFromValidationState(state: Joi.State): YamlConfig {
@@ -200,6 +203,11 @@ const styleObjectModelNotObject = 'styleObjectModelName.model.type';
200
203
  const styleObjectModelNameSchema = Joi.string()
201
204
  .allow('', null)
202
205
  .custom((value, { error, state }) => {
206
+ const config = getConfigFromValidationState(state);
207
+ const externalModels = config.cmsName && ['contentful', 'sanity'].includes(config.cmsName);
208
+ if (externalModels) {
209
+ return value;
210
+ }
203
211
  const models = getModelsFromValidationState(state);
204
212
  const modelNames = Object.keys(models);
205
213
  if (!modelNames.includes(value)) {
@@ -245,11 +253,32 @@ const importSchema = Joi.alternatives().conditional('.type', {
245
253
  ]
246
254
  });
247
255
 
248
- const modelsSourceSchema = Joi.object<ModelsSource>({
249
- type: 'files',
256
+ const modelsSourceFilesSchema = Joi.object<ModelsSourceFiles>({
257
+ type: Joi.string().valid(Joi.override, 'files').required(),
250
258
  modelDirs: Joi.array().items(Joi.string()).required()
251
259
  });
252
260
 
261
+ const modelsSourceContentfulSchema = Joi.object<ModelsSourceContentful>({
262
+ type: Joi.string().valid(Joi.override, 'contentful').required(),
263
+ module: Joi.string()
264
+ });
265
+
266
+ const modelsSourceSanitySchema = Joi.object<ModelsSourceSanity>({
267
+ type: Joi.string().valid(Joi.override, 'sanity').required(),
268
+ sanityStudioPath: Joi.string().required(),
269
+ module: Joi.string()
270
+ });
271
+
272
+ const modelsSourceSchema = Joi.object<ModelsSource>({
273
+ type: Joi.string().valid('files', 'contentful', 'sanity').required()
274
+ }).when('.type', {
275
+ switch: [
276
+ { is: 'files', then: modelsSourceFilesSchema },
277
+ { is: 'contentful', then: modelsSourceContentfulSchema },
278
+ { is: 'sanity', then: modelsSourceSanitySchema }
279
+ ]
280
+ });
281
+
253
282
  const assetsSchema = Joi.object<Assets>({
254
283
  referenceType: Joi.string().valid('static', 'relative').required(),
255
284
  assetsDir: Joi.string().allow('').when('referenceType', {
@@ -309,7 +338,8 @@ const fieldCommonPropsSchema = Joi.object({
309
338
  group: inGroups,
310
339
  const: Joi.any(),
311
340
  hidden: Joi.boolean(),
312
- readOnly: Joi.boolean()
341
+ readOnly: Joi.boolean(),
342
+ localized: Joi.boolean()
313
343
  }).oxor('const', 'default');
314
344
 
315
345
  const numberFieldPartialSchema = Joi.object({
@@ -3,7 +3,7 @@ import { CMS_NAMES, FIELD_TYPES, SSG_NAMES, STYLE_PROPS } from './config-consts'
3
3
 
4
4
  export interface Config extends BaseConfig {
5
5
  models: Model[];
6
- presets?: any;
6
+ presets?: Record<string, any>;
7
7
  }
8
8
 
9
9
  export interface YamlConfig extends BaseConfig {
@@ -85,16 +85,31 @@ export interface ContentModel extends BaseMatch {
85
85
  newFilePath?: string;
86
86
  }
87
87
 
88
- export interface ModelsSource {
88
+ export type ModelsSource = ModelsSourceFiles | ModelsSourceContentful | ModelsSourceSanity;
89
+
90
+ export interface ModelsSourceFiles {
89
91
  type: 'files';
90
92
  modelDirs: string[];
91
93
  }
92
94
 
95
+ export interface ModelsSourceContentful {
96
+ type: 'contentful';
97
+ module?: string;
98
+ [key: string]: any;
99
+ }
100
+
101
+ export interface ModelsSourceSanity {
102
+ type: 'sanity';
103
+ sanityStudioPath: string;
104
+ module?: string;
105
+ [key: string]: any;
106
+ }
107
+
93
108
  /*******************
94
109
  *** Model Types ***
95
110
  *******************/
96
111
 
97
- export type Model = StricterUnion<ObjectModel | DataModel | PageModel | ConfigModel>;
112
+ export type Model = StricterUnion<ObjectModel | DataModel | PageModel | ConfigModel | ImageModel>;
98
113
 
99
114
  export type ObjectModel = YamlObjectModel & BaseModel;
100
115
  export type DataModel = YamlDataModel & BaseModel;
@@ -174,6 +189,14 @@ export interface FieldGroupItem {
174
189
  label: string;
175
190
  }
176
191
 
192
+ export interface ImageModel {
193
+ type: 'image';
194
+ name: '__image_model';
195
+ label?: string;
196
+ labelField?: string;
197
+ fields?: Field[];
198
+ }
199
+
177
200
  /*******************
178
201
  *** Field Types ***
179
202
  *******************/
@@ -209,7 +232,7 @@ export interface FieldCommonProps {
209
232
  export type FieldType = typeof FIELD_TYPES[number];
210
233
 
211
234
  export interface FieldSimpleProps {
212
- type: 'string' | 'url' | 'slug' | 'text' | 'markdown' | 'html' | 'boolean' | 'date' | 'datetime' | 'color' | 'image' | 'file';
235
+ type: 'string' | 'url' | 'slug' | 'text' | 'markdown' | 'html' | 'boolean' | 'date' | 'datetime' | 'color' | 'image' | 'file' | 'json' | 'richText';
213
236
  }
214
237
 
215
238
  export type FieldEnumProps = FieldEnumDropdownProps | FieldEnumThumbnailsProps | FieldEnumPaletteProps;
@@ -29,6 +29,9 @@ export function convertToYamlConfig({ config }: { config: Config }): YamlConfig
29
29
  yamlConfig.models = _.reduce(
30
30
  config.models,
31
31
  (yamlModels: ModelMap, model: Model) => {
32
+ if (model.type === 'image') {
33
+ return yamlModels;
34
+ }
32
35
  const yamlModel = _.omit(model, ['name', '__metadata']) as YamlModel;
33
36
  if (yamlModel.type === 'page' && !yamlModel.hideContent && yamlModel.fields) {
34
37
  _.remove(yamlModel.fields, (field) => field.name === 'markdown_content');
@@ -20,13 +20,14 @@ export async function loadPresets(dirPath: string, config: Config): Promise<Pres
20
20
  if (!(await fse.pathExists(presetsDir))) {
21
21
  continue;
22
22
  }
23
- presetFiles.push(
24
- ...(await fse.readdir(presetsDir)).filter((fileName) => path.parse(fileName).ext === '.json').map((fileName) => path.join(presetsRelDir, fileName))
25
- );
23
+ const files = (await fse.readdir(presetsDir))
24
+ .filter((fileName) => ['.json', '.yaml', '.yml'].includes(path.parse(fileName).ext))
25
+ .map((fileName) => path.join(presetsRelDir, fileName));
26
+ presetFiles.push(...files);
26
27
  }
27
28
 
28
- const presets: any = {};
29
- const presetsByModel: any = {};
29
+ const presets: Record<string, any> = {};
30
+ const presetsIdsByModel: Record<string, any> = {};
30
31
  const errors: ConfigPresetsError[] = [];
31
32
 
32
33
  for (const presetFile of presetFiles) {
@@ -46,22 +47,24 @@ export async function loadPresets(dirPath: string, config: Config): Promise<Pres
46
47
  preset.thumbnail = resolveThumbnailPath(preset.thumbnail, presetsRelDir);
47
48
  }
48
49
  _.set(preset, 'modelName', presetData.model);
49
- append(presetsByModel, presetData.model, presetId);
50
+ append(presetsIdsByModel, presetData.model, presetId);
50
51
  });
51
52
  }
52
53
 
53
- // update config with presets
54
- for (const model of config.models) {
55
- const presetsForModel = presetsByModel[model.name];
56
- if (presetsForModel) {
57
- model.presets = presetsForModel;
54
+ // update models with presets IDs
55
+ const models = _.map(config.models, (model) => {
56
+ const presetIdsForModel = presetsIdsByModel[model.name];
57
+ if (!presetIdsForModel) {
58
+ return model;
58
59
  }
59
- }
60
-
61
- config.presets = presets;
60
+ return { ...model, presets: presetIdsForModel };
61
+ });
62
62
 
63
63
  return {
64
- config,
64
+ config: Object.assign({}, config, {
65
+ models,
66
+ presets
67
+ }),
65
68
  errors
66
69
  };
67
70
  }
@@ -10,7 +10,7 @@ import {
10
10
  isDataModel,
11
11
  isPageModel,
12
12
  getModelsByQuery,
13
- getListItemsField,
13
+ getListFieldItems,
14
14
  isListField,
15
15
  isModelField,
16
16
  isListDataModel,
@@ -480,7 +480,7 @@ function addMetadataRecursively({
480
480
  } else if (_.isArray(value)) {
481
481
  let fieldListItems: FieldListItems;
482
482
  if (field && isListField(field)) {
483
- fieldListItems = getListItemsField(field);
483
+ fieldListItems = getListFieldItems(field);
484
484
  } else if (model && isListDataModel(model)) {
485
485
  fieldListItems = model.items;
486
486
  } else {
@@ -106,6 +106,10 @@ function joiSchemaForModelFields(fields: Field[] | undefined, config: Config, fi
106
106
  );
107
107
  }
108
108
 
109
+ function assertUnreachable(field: never): never {
110
+ throw new Error('Unhandled field.type case');
111
+ }
112
+
109
113
  function joiSchemaForField(field: Field | FieldListItems, config: Config, fieldPath: FieldPath) {
110
114
  let fieldSchema;
111
115
  switch (field.type) {
@@ -148,6 +152,11 @@ function joiSchemaForField(field: Field | FieldListItems, config: Config, fieldP
148
152
  case 'list':
149
153
  fieldSchema = listFieldValueSchema(field, config, fieldPath);
150
154
  break;
155
+ case 'json':
156
+ case 'richText':
157
+ return Joi.any().forbidden();
158
+ default:
159
+ assertUnreachable(field);
151
160
  }
152
161
  if ('const' in field) {
153
162
  fieldSchema = fieldSchema.valid(field.const).invalid(null, '').required();
@@ -11,7 +11,7 @@ export function extendModelArray(models: Model[]): { models: Model[]; errors: Co
11
11
  models,
12
12
  (result: { models: Model[]; errors: ConfigValidationError[] }, model) => {
13
13
  // YamlModel is the same as Model just without 'name' and '__metadata' properties
14
- const { model: extendedModel, errors } = memorized(model, model.name, modelsByName);
14
+ const { model: extendedModel, errors } = memorized(model, model.name, modelsByName as ModelMap);
15
15
  return {
16
16
  models: result.models.concat(extendedModel),
17
17
  errors: result.errors.concat(errors)
@@ -1,7 +1,7 @@
1
1
  import _ from 'lodash';
2
2
 
3
- import { getListItemsField, isListDataModel, isListField, isObjectListItems, isModelField, isObjectField, isModelListItems } from './model-utils';
4
- import { Field, FieldListItems, FieldModelProps, Model } from '../config/config-types';
3
+ import { getListFieldItems, isListDataModel, isListField, isObjectListItems, isModelField, isObjectField, isModelListItems } from './model-utils';
4
+ import { DataModel, Field, FieldList, FieldListItems, FieldModelProps, FieldObjectProps, Model } from '../config/config-types';
5
5
 
6
6
  /**
7
7
  * This function invokes the `iteratee` function for every field of the `model`.
@@ -39,24 +39,24 @@ import { Field, FieldListItems, FieldModelProps, Model } from '../config/config-
39
39
  * @param model The model to iterate fields
40
40
  * @param iteratee The callback function
41
41
  */
42
- export function iterateModelFieldsRecursively(model: Model, iteratee: (field: Field, fieldPath: string[]) => void) {
43
- function _iterateDeep({ fields, fieldPath }: { fields: Field[]; fieldPath: string[] }) {
44
- fieldPath = fieldPath.concat('fields');
42
+ export function iterateModelFieldsRecursively(model: Model, iteratee: (field: Field, modelKeyPath: string[]) => void) {
43
+ function _iterateDeep({ fields, modelKeyPath }: { fields: Field[]; modelKeyPath: string[] }) {
44
+ modelKeyPath = modelKeyPath.concat('fields');
45
45
  _.forEach(fields, (field) => {
46
46
  if (!field) {
47
47
  return;
48
48
  }
49
- const childFieldPath = fieldPath.concat(field.name);
50
- iteratee(field, childFieldPath);
49
+ const childModelKeyPath = modelKeyPath.concat(field.name);
50
+ iteratee(field, childModelKeyPath);
51
51
  if (isObjectField(field)) {
52
52
  _iterateDeep({
53
53
  fields: field.fields,
54
- fieldPath: childFieldPath
54
+ modelKeyPath: childModelKeyPath
55
55
  });
56
56
  } else if (isListField(field) && field.items && isObjectListItems(field.items)) {
57
57
  _iterateDeep({
58
58
  fields: field.items?.fields,
59
- fieldPath: childFieldPath.concat('items')
59
+ modelKeyPath: childModelKeyPath.concat('items')
60
60
  });
61
61
  }
62
62
  });
@@ -65,16 +65,64 @@ export function iterateModelFieldsRecursively(model: Model, iteratee: (field: Fi
65
65
  if (model && isListDataModel(model) && model.items && isObjectListItems(model.items)) {
66
66
  _iterateDeep({
67
67
  fields: model.items?.fields,
68
- fieldPath: ['items']
68
+ modelKeyPath: ['items']
69
69
  });
70
70
  } else {
71
71
  _iterateDeep({
72
72
  fields: model?.fields || [],
73
- fieldPath: []
73
+ modelKeyPath: []
74
74
  });
75
75
  }
76
76
  }
77
77
 
78
+ export function mapModelFieldsRecursively(model: Model, iteratee: (field: Field, modelKeyPath: string[]) => Field) {
79
+ function _mapField(field: Field, modelKeyPath: string[]): Field {
80
+ if (!field) {
81
+ return field;
82
+ }
83
+ modelKeyPath = modelKeyPath.concat(field.name);
84
+ field = iteratee(field, modelKeyPath);
85
+ if (isObjectField(field)) {
86
+ return _mapObjectField(field, modelKeyPath);
87
+ } else if (isListField(field)) {
88
+ return _mapListField(field, modelKeyPath);
89
+ } else {
90
+ return field;
91
+ }
92
+ }
93
+
94
+ function _mapObjectField<T extends FieldObjectProps | Model>(field: T, modelKeyPath: string[]): T {
95
+ const fields = field.fields;
96
+ if (!fields) {
97
+ return field;
98
+ }
99
+ modelKeyPath = modelKeyPath.concat('fields');
100
+ return {
101
+ ...field,
102
+ fields: _.map(fields, (field) => _mapField(field, modelKeyPath))
103
+ };
104
+ }
105
+
106
+ function _mapListField<T extends FieldList | (DataModel & { isList: true })>(field: T, modelKeyPath: string[]): T {
107
+ const items = field.items;
108
+ if (!items || !isObjectListItems(items)) {
109
+ return field;
110
+ }
111
+ return {
112
+ ...field,
113
+ items: _mapObjectField(items, modelKeyPath.concat('items'))
114
+ };
115
+ }
116
+
117
+ if (!model) {
118
+ return model;
119
+ } else if (isListDataModel(model)) {
120
+ return _mapListField(model, []);
121
+ } else {
122
+ return _mapObjectField(model, []);
123
+ }
124
+ }
125
+
78
126
  export function iterateObjectFieldsWithModelRecursively(
79
127
  value: any,
80
128
  model: Model,
@@ -165,7 +213,7 @@ export function iterateObjectFieldsWithModelRecursively(
165
213
  } else if (_.isArray(value)) {
166
214
  let fieldListItems: FieldListItems | null = null;
167
215
  if (field && isListField(field)) {
168
- fieldListItems = getListItemsField(field);
216
+ fieldListItems = getListFieldItems(field);
169
217
  } else if (model && isListDataModel(model)) {
170
218
  fieldListItems = model.items;
171
219
  }
@@ -287,7 +335,7 @@ export function mapObjectFieldsWithModelRecursively(
287
335
  } else if (_.isArray(value)) {
288
336
  let fieldListItems: FieldListItems | null = null;
289
337
  if (field && isListField(field)) {
290
- fieldListItems = getListItemsField(field);
338
+ fieldListItems = getListFieldItems(field);
291
339
  } else if (model && isListDataModel(model)) {
292
340
  fieldListItems = model.items;
293
341
  }
@@ -79,20 +79,20 @@ export function isEnumField(field: Field): field is FieldEnum {
79
79
  }
80
80
 
81
81
  export function isListOfObjectsField(field: Field): field is FieldListObject {
82
- return isListField(field) && isObjectListItems(getListItemsField(field));
82
+ return isListField(field) && isObjectListItems(getListFieldItems(field));
83
83
  }
84
84
 
85
85
  export function isListOfModelsField(field: Field): field is FieldListModel {
86
- return isListField(field) && isModelListItems(getListItemsField(field));
86
+ return isListField(field) && isModelListItems(getListFieldItems(field));
87
87
  }
88
88
 
89
89
  export function isListOfReferencesField(field: Field): field is FieldListReference {
90
- return isListField(field) && isReferenceListItems(getListItemsField(field));
90
+ return isListField(field) && isReferenceListItems(getListFieldItems(field));
91
91
  }
92
92
 
93
93
  export function isListOfCustomModelsField(field: Field, modelsByName?: Record<string, Model>): field is FieldList {
94
94
  // custom model field types are deprecated
95
- return isListField(field) && isCustomModelListItems(getListItemsField(field), modelsByName);
95
+ return isListField(field) && isCustomModelListItems(getListFieldItems(field), modelsByName);
96
96
  }
97
97
 
98
98
  export function isListField(field: Field): field is FieldList {
@@ -122,7 +122,7 @@ export function isCustomModelListItems(items: FieldListItems, modelsByName?: Rec
122
122
  * items field, the default field is string:
123
123
  *
124
124
  * @example
125
- * listItemField = getListItemsField({
125
+ * listItemField = getListFieldItems({
126
126
  * type: 'list',
127
127
  * name: '...',
128
128
  * items: { type: 'object', fields: [] }
@@ -134,15 +134,36 @@ export function isCustomModelListItems(items: FieldListItems, modelsByName?: Rec
134
134
  * }
135
135
  *
136
136
  * // list field without `items`
137
- * listItemField = getListItemsField({ type: 'list', name: '...' }
137
+ * listItemField = getListFieldItems({ type: 'list', name: '...' }
138
138
  * listItemField => { type: 'string' }
139
139
  *
140
140
  * @param {Object} field
141
141
  * @return {Object}
142
142
  */
143
- export function getListItemsField(field: FieldList): FieldListItems {
143
+ export function getListFieldItems(field: FieldList): FieldListItems {
144
144
  // items.type defaults to string
145
- return _.defaults(field.items, { type: 'string' });
145
+ return Object.assign({ type: 'string' }, field.items);
146
+ }
147
+
148
+ export function normalizeListField(field: FieldList): FieldList {
149
+ if (field.items?.type) {
150
+ return field;
151
+ }
152
+ return {
153
+ ...field,
154
+ items: {
155
+ type: 'string',
156
+ ...(field.items ?? {})
157
+ }
158
+ };
159
+ }
160
+
161
+ export function normalizeListFieldInPlace(field: FieldList): FieldList {
162
+ // 'items.type' of list field default to 'string', set it explicitly
163
+ if (!_.has(field, 'items.type')) {
164
+ _.set(field, 'items.type', 'string');
165
+ }
166
+ return field;
146
167
  }
147
168
 
148
169
  export function assignLabelFieldIfNeeded(modelOrField: Model | FieldObjectProps) {
@@ -176,3 +197,65 @@ export function resolveLabelFieldForModel(modelOrField: Model | FieldObjectProps
176
197
  }
177
198
  return labelField || null;
178
199
  }
200
+
201
+ export function getModelFieldForModelKeyPath(model: Model, modelKeyPath: string[]) {
202
+ function _getField(field: Field, modelKeyPath: string[]): Model | Field | FieldListItems | FieldObjectProps | null {
203
+ if (modelKeyPath.length === 0) {
204
+ return field;
205
+ } else if (isObjectField(field)) {
206
+ return _getObjectFields(field, modelKeyPath);
207
+ } else if (isListField(field)) {
208
+ return _getListItems(field, modelKeyPath);
209
+ } else {
210
+ return null;
211
+ }
212
+ }
213
+
214
+ function _getObjectFields<T extends FieldObjectProps | Model>(field: T, modelKeyPath: string[]): Model | Field | FieldListItems | FieldObjectProps | null {
215
+ if (modelKeyPath.length === 0) {
216
+ return field;
217
+ }
218
+ const key = modelKeyPath.shift();
219
+ if (key !== 'fields' || !field.fields) {
220
+ return null;
221
+ }
222
+ if (modelKeyPath.length === 0) {
223
+ return null;
224
+ }
225
+ const fieldName = modelKeyPath.shift();
226
+ const childField = _.find(field.fields, (field) => field.name === fieldName);
227
+ if (!childField) {
228
+ return null;
229
+ }
230
+ return _getField(childField, modelKeyPath);
231
+ }
232
+
233
+ function _getListItems<T extends FieldList | (DataModel & { isList: true })>(
234
+ field: T,
235
+ modelKeyPath: string[]
236
+ ): Model | Field | FieldListItems | FieldObjectProps | null {
237
+ if (modelKeyPath.length === 0) {
238
+ return field;
239
+ }
240
+ const key = modelKeyPath.shift();
241
+ if (key !== 'items' || !field.items) {
242
+ return null;
243
+ }
244
+ if (modelKeyPath.length === 0) {
245
+ return field.items;
246
+ }
247
+ if (!isObjectListItems(field.items)) {
248
+ return null;
249
+ }
250
+ return _getObjectFields(field.items, modelKeyPath);
251
+ }
252
+
253
+ modelKeyPath = modelKeyPath.slice();
254
+ if (!model) {
255
+ return null;
256
+ } else if (isListDataModel(model)) {
257
+ return _getListItems(model, modelKeyPath);
258
+ } else {
259
+ return _getObjectFields(model, modelKeyPath);
260
+ }
261
+ }