@stackbit/cms-sanity 0.2.45-develop.1 → 0.2.45-staging.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.
Files changed (34) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/sanity-content-source.d.ts +21 -10
  6. package/dist/sanity-content-source.d.ts.map +1 -1
  7. package/dist/sanity-content-source.js +74 -242
  8. package/dist/sanity-content-source.js.map +1 -1
  9. package/dist/sanity-document-converter.d.ts +11 -9
  10. package/dist/sanity-document-converter.d.ts.map +1 -1
  11. package/dist/sanity-document-converter.js +262 -205
  12. package/dist/sanity-document-converter.js.map +1 -1
  13. package/dist/sanity-operation-converter.d.ts +60 -0
  14. package/dist/sanity-operation-converter.d.ts.map +1 -0
  15. package/dist/sanity-operation-converter.js +664 -0
  16. package/dist/sanity-operation-converter.js.map +1 -0
  17. package/dist/sanity-schema-converter.d.ts +35 -3
  18. package/dist/sanity-schema-converter.d.ts.map +1 -1
  19. package/dist/sanity-schema-converter.js +290 -43
  20. package/dist/sanity-schema-converter.js.map +1 -1
  21. package/dist/sanity-schema-fetcher.d.ts +3 -3
  22. package/dist/sanity-schema-fetcher.d.ts.map +1 -1
  23. package/dist/utils.d.ts +53 -0
  24. package/dist/utils.d.ts.map +1 -1
  25. package/dist/utils.js +93 -1
  26. package/dist/utils.js.map +1 -1
  27. package/package.json +6 -5
  28. package/src/index.ts +1 -1
  29. package/src/sanity-content-source.ts +109 -317
  30. package/src/sanity-document-converter.ts +332 -231
  31. package/src/sanity-operation-converter.ts +785 -0
  32. package/src/sanity-schema-converter.ts +424 -70
  33. package/src/sanity-schema-fetcher.ts +3 -3
  34. package/src/utils.ts +98 -0
@@ -1,8 +1,9 @@
1
1
  import _ from 'lodash';
2
2
  import { SanityDocument } from '@sanity/client';
3
- import * as ContentSourceTypes from '@stackbit/types';
3
+ import * as StackbitTypes from '@stackbit/types';
4
4
  import { DocumentHistory, DocumentHistoryMap } from './sanity-api-client';
5
- import { ScheduledAction, ScheduledActionState } from '@stackbit/types';
5
+ import { SchemaContext, ModelWithContext, ModelContext } from './sanity-schema-converter';
6
+ import { getItemTypeForListItem, isLocalizedModelField, resolvedFieldType } from './utils';
6
7
 
7
8
  export interface SanitySchedule {
8
9
  author: string;
@@ -34,12 +35,12 @@ export type DocumentContext = {
34
35
 
35
36
  export type AssetContext = DocumentContext;
36
37
 
37
- export type ContextualDocument = ContentSourceTypes.Document<DocumentContext>;
38
- export type ContextualAsset = ContentSourceTypes.Asset<AssetContext>;
38
+ export type ContextualDocument = StackbitTypes.Document<DocumentContext>;
39
+ export type ContextualAsset = StackbitTypes.Asset<AssetContext>;
39
40
 
40
41
  export const DRAFT_ID_PREFIX = 'drafts.';
41
42
 
42
- export type GetModelByName = ContentSourceTypes.Cache['getModelByName'];
43
+ export type GetModelByName = StackbitTypes.Cache<SchemaContext, DocumentContext, AssetContext, ModelContext>['getModelByName'];
43
44
 
44
45
  export type ConvertDocumentsOptions = {
45
46
  documents: SanityDocument[];
@@ -60,11 +61,11 @@ export function isDraftId(objectId: string) {
60
61
  return objectId && objectId.startsWith(DRAFT_ID_PREFIX);
61
62
  }
62
63
 
63
- export function convertAndFilterScheduledActions(sanitySchedules: SanitySchedule[]): ContentSourceTypes.ScheduledAction[] {
64
+ export function convertAndFilterScheduledActions(sanitySchedules: SanitySchedule[]): StackbitTypes.ScheduledAction[] {
64
65
  const allScheduledActions = sanitySchedules.map((schedule: SanitySchedule) => convertScheduledAction(schedule));
65
66
  return filterScheduledActions(allScheduledActions);
66
67
  }
67
- export function convertScheduledAction(sanitySchedule: SanitySchedule): ContentSourceTypes.ScheduledAction {
68
+ export function convertScheduledAction(sanitySchedule: SanitySchedule): StackbitTypes.ScheduledAction {
68
69
  return {
69
70
  id: sanitySchedule.id,
70
71
  name: sanitySchedule.name,
@@ -77,12 +78,12 @@ export function convertScheduledAction(sanitySchedule: SanitySchedule): ContentS
77
78
  };
78
79
  }
79
80
 
80
- export function filterScheduledActions(scheduledActions: ScheduledAction[]) {
81
+ export function filterScheduledActions(scheduledActions: StackbitTypes.ScheduledAction[]) {
81
82
  const cutoffDate = new Date();
82
83
  cutoffDate.setMonth(cutoffDate.getMonth() - 1);
83
84
  const cutoffDateStr = cutoffDate.toISOString();
84
85
  return scheduledActions.filter(
85
- (scheduledAction: ScheduledAction) => scheduledAction.state !== 'cancelled' && scheduledAction.executeAt.localeCompare(cutoffDateStr) > 0
86
+ (scheduledAction: StackbitTypes.ScheduledAction) => scheduledAction.state !== 'cancelled' && scheduledAction.executeAt.localeCompare(cutoffDateStr) > 0
86
87
  );
87
88
  }
88
89
 
@@ -144,6 +145,8 @@ function convertDocument({
144
145
  fields: convertFields({
145
146
  object: document,
146
147
  modelFields: model.fields,
148
+ rootModel: model,
149
+ modelFieldPath: [],
147
150
  getModelByName
148
151
  }),
149
152
  context: {
@@ -202,13 +205,15 @@ export function convertAssets({ assets, documentsHistory }: ConvertAssetsOptions
202
205
 
203
206
  export type ConvertFieldsOptions = {
204
207
  object: Record<string, any>;
205
- modelFields?: ContentSourceTypes.Field[];
208
+ modelFields?: StackbitTypes.Field[];
209
+ rootModel: ModelWithContext;
210
+ modelFieldPath: string[];
206
211
  getModelByName: GetModelByName;
207
212
  };
208
213
 
209
- function convertFields({ object, modelFields, getModelByName }: ConvertFieldsOptions): Record<string, ContentSourceTypes.DocumentField> {
214
+ function convertFields({ object, modelFields, rootModel, modelFieldPath, getModelByName }: ConvertFieldsOptions): Record<string, StackbitTypes.DocumentField> {
210
215
  const fieldsByName = _.keyBy(modelFields, 'name');
211
- const result: Record<string, ContentSourceTypes.DocumentField> = {};
216
+ const result: Record<string, StackbitTypes.DocumentField> = {};
212
217
  for (const fieldName in fieldsByName) {
213
218
  const value = object[fieldName];
214
219
  const modelField = fieldsByName[fieldName];
@@ -218,6 +223,8 @@ function convertFields({ object, modelFields, getModelByName }: ConvertFieldsOpt
218
223
  const field = convertFieldType({
219
224
  value,
220
225
  modelField,
226
+ rootModel,
227
+ modelFieldPath: modelFieldPath.concat(fieldName),
221
228
  getModelByName
222
229
  });
223
230
  if (field) {
@@ -227,13 +234,41 @@ function convertFields({ object, modelFields, getModelByName }: ConvertFieldsOpt
227
234
  return result;
228
235
  }
229
236
 
230
- interface ConvertFieldTypeOptions {
237
+ /**
238
+ * Converts Sanity's document or object field value, or list item value, to
239
+ * Stackbit's DocumentField or DocumentListFieldItems.
240
+ *
241
+ * The convertFieldType has two overloads:
242
+ * When modelField is Field, it returns DocumentField or undefined.
243
+ * When modelField is FieldListItems it returns DocumentListFieldItems or undefined.
244
+ */
245
+ function convertFieldType(options: {
231
246
  value: any;
232
- modelField: ContentSourceTypes.Field | ContentSourceTypes.FieldSpecificProps;
247
+ modelField: StackbitTypes.Field;
248
+ rootModel: ModelWithContext;
249
+ modelFieldPath: string[];
233
250
  getModelByName: GetModelByName;
234
- }
235
-
236
- function convertFieldType({ value, modelField, getModelByName }: ConvertFieldTypeOptions): ContentSourceTypes.DocumentField | null {
251
+ }): StackbitTypes.DocumentField | undefined;
252
+ function convertFieldType(options: {
253
+ value: any;
254
+ modelField: StackbitTypes.FieldListItems;
255
+ rootModel: ModelWithContext;
256
+ modelFieldPath: string[];
257
+ getModelByName: GetModelByName;
258
+ }): StackbitTypes.DocumentListFieldItems | undefined;
259
+ function convertFieldType({
260
+ value,
261
+ modelField,
262
+ rootModel,
263
+ modelFieldPath,
264
+ getModelByName
265
+ }: {
266
+ value: any;
267
+ modelField: StackbitTypes.Field | StackbitTypes.FieldListItems;
268
+ rootModel: ModelWithContext;
269
+ modelFieldPath: string[];
270
+ getModelByName: GetModelByName;
271
+ }): StackbitTypes.DocumentField | undefined {
237
272
  switch (modelField.type) {
238
273
  case 'string':
239
274
  case 'text':
@@ -246,154 +281,246 @@ function convertFieldType({ value, modelField, getModelByName }: ConvertFieldTyp
246
281
  case 'enum':
247
282
  case 'json':
248
283
  case 'style':
249
- case 'cross-reference':
250
- case 'markdown':
251
- if (_.isUndefined(value)) {
252
- return null;
253
- }
254
- return {
255
- value,
256
- type: modelField.type
257
- } as ContentSourceTypes.DocumentField;
284
+ case 'markdown': {
285
+ return createDocumentField({
286
+ fieldValue: value,
287
+ modelField: modelField,
288
+ documentFieldBaseProps: { type: modelField.type },
289
+ documentFieldSpecificProps: (value: any) => {
290
+ if (_.isUndefined(value)) {
291
+ return undefined;
292
+ }
293
+ return { value };
294
+ }
295
+ });
296
+ }
297
+ case 'cross-reference': {
298
+ return undefined;
299
+ }
258
300
  case 'list': {
259
- const itemsModel = modelField.items ?? { type: 'string' };
260
- let getListItemModel: (listItem: any, fieldModel: any) => ContentSourceTypes.FieldListItems | null;
261
- if (_.isArray(itemsModel)) {
262
- // in Sanity, list items may have multiple types, in this case, 'items' will be an array
263
- getListItemModel = (listItem: any, fieldModel: FieldListMultiItem) => getItemTypeForListItem(listItem, fieldModel);
264
- } else {
265
- getListItemModel = () => itemsModel;
266
- }
267
- return {
268
- type: 'list',
269
- items: _.reduce(
270
- value,
271
- (accum: ContentSourceTypes.DocumentListFieldItems[], item) => {
272
- const itemModel = getListItemModel(item, modelField);
273
- if (!itemModel) {
274
- return accum;
275
- }
276
- const documentField = convertFieldType({
277
- value: item,
278
- modelField: itemModel,
301
+ return createDocumentField({
302
+ fieldValue: value,
303
+ modelField: modelField,
304
+ documentFieldBaseProps: { type: 'list' },
305
+ documentFieldSpecificProps: (value: any) => {
306
+ return {
307
+ items: _.reduce(
308
+ value,
309
+ (accum: StackbitTypes.DocumentListFieldItems[], item) => {
310
+ const itemModel = getItemTypeForListItem(item, modelField);
311
+ if (!itemModel) {
312
+ return accum;
313
+ }
314
+ const documentField = convertFieldType({
315
+ value: item,
316
+ modelField: itemModel,
317
+ rootModel,
318
+ modelFieldPath: modelFieldPath.concat('items'),
319
+ getModelByName
320
+ });
321
+ if (!documentField) {
322
+ return accum;
323
+ }
324
+ return accum.concat(documentField);
325
+ },
326
+ []
327
+ )
328
+ };
329
+ }
330
+ });
331
+ }
332
+ case 'object': {
333
+ return createDocumentField({
334
+ fieldValue: value,
335
+ modelField: modelField,
336
+ documentFieldBaseProps: { type: 'object' },
337
+ documentFieldSpecificProps: (value: any) => {
338
+ if (!value) {
339
+ return undefined;
340
+ }
341
+ return {
342
+ fields: convertFields({
343
+ object: value,
344
+ modelFields: modelField.fields,
345
+ rootModel,
346
+ modelFieldPath,
279
347
  getModelByName
280
- }) as ContentSourceTypes.DocumentListFieldItems;
281
- if (!documentField) {
282
- return accum;
283
- }
284
- return accum.concat(documentField);
285
- },
286
- []
287
- )
288
- };
348
+ })
349
+ };
350
+ }
351
+ });
289
352
  }
290
- case 'object':
291
- if (!value) {
292
- return null;
293
- }
294
- return {
295
- type: 'object',
296
- fields: convertFields({ object: value, modelFields: modelField.fields, getModelByName })
297
- };
298
353
  case 'model': {
299
- if (!value) {
300
- return null;
301
- }
302
- const model = getModelByName(value._type);
303
- if (!model) {
304
- return null;
305
- }
306
- return {
307
- type: 'model',
308
- modelName: model.name,
309
- fields: convertFields({
310
- object: value,
311
- modelFields: model.fields,
312
- getModelByName
313
- })
314
- };
354
+ return createDocumentField({
355
+ fieldValue: value,
356
+ modelField: modelField,
357
+ documentFieldBaseProps: { type: 'model' },
358
+ documentFieldSpecificProps: (value: any) => {
359
+ if (!value) {
360
+ return undefined;
361
+ }
362
+ const modelName = resolvedFieldType({ sanityFieldType: value._type, model: rootModel, modelFieldPath });
363
+ const model = getModelByName(modelName);
364
+ if (!model) {
365
+ return undefined;
366
+ }
367
+ return {
368
+ modelName: model.name,
369
+ fields: convertFields({
370
+ object: value,
371
+ modelFields: model.fields,
372
+ rootModel: model,
373
+ modelFieldPath: [],
374
+ getModelByName
375
+ })
376
+ };
377
+ }
378
+ });
315
379
  }
316
380
  case 'reference': {
317
- if (!value) {
318
- return null;
319
- }
320
- let refId = _.get(value, '_ref', null);
321
- if (refId) {
322
- refId = refId.replace(/^drafts\./, '');
323
- }
324
- return {
325
- type: 'reference',
326
- refType: 'document',
327
- refId
328
- };
381
+ return createDocumentField({
382
+ fieldValue: value,
383
+ modelField: modelField,
384
+ documentFieldBaseProps: {
385
+ type: 'reference',
386
+ refType: 'document'
387
+ },
388
+ documentFieldSpecificProps: (value: any) => {
389
+ if (!value) {
390
+ return undefined;
391
+ }
392
+ let refId = _.get(value, '_ref', null);
393
+ if (refId) {
394
+ refId = refId.replace(/^drafts\./, '');
395
+ }
396
+ return {
397
+ refId
398
+ };
399
+ }
400
+ });
329
401
  }
330
- case 'color':
331
- if (!value) {
332
- return null;
333
- }
334
- return {
335
- type: 'color',
336
- value: _.get(value, 'hex')
337
- };
338
- case 'richText':
339
- if (!value) {
340
- return null;
341
- }
342
- return {
343
- value: null,
344
- type: 'richText',
345
- hint: flattenRichText(value).substring(0, 200)
346
- };
347
- case 'image':
348
- if (!value) {
349
- return null;
350
- }
351
- if (modelField.source === 'cloudinary' || modelField.source === 'aprimo') {
352
- return {
353
- type: 'image',
354
- source: modelField.source,
355
- sourceData: value
356
- };
357
- }
358
- if (modelField.source === 'bynder') {
359
- return {
360
- type: 'image',
361
- source: modelField.source,
362
- sourceData: value,
363
- fields: {
364
- title: {
365
- type: 'string',
366
- value: value?.name
367
- },
368
- url: {
369
- type: 'string',
370
- value: value?.previewImg
402
+ case 'color': {
403
+ return createDocumentField({
404
+ fieldValue: value,
405
+ modelField: modelField,
406
+ documentFieldBaseProps: { type: 'color' },
407
+ documentFieldSpecificProps: (value: any) => {
408
+ if (!value) {
409
+ return undefined;
410
+ }
411
+ return {
412
+ value: _.get(value, 'hex')
413
+ };
414
+ }
415
+ });
416
+ }
417
+ case 'richText': {
418
+ return createDocumentField({
419
+ fieldValue: value,
420
+ modelField: modelField,
421
+ documentFieldBaseProps: { type: 'richText' },
422
+ documentFieldSpecificProps: (value: any) => {
423
+ if (!value) {
424
+ return undefined;
425
+ }
426
+ return {
427
+ value: value,
428
+ hint: flattenRichText(value).substring(0, 200)
429
+ };
430
+ }
431
+ });
432
+ }
433
+ case 'image': {
434
+ if (modelField.source && ['cloudinary', 'aprimo', 'bynder'].includes(modelField.source)) {
435
+ return createDocumentField({
436
+ fieldValue: value,
437
+ modelField: modelField,
438
+ documentFieldBaseProps: {
439
+ type: 'image',
440
+ source: modelField.source
441
+ },
442
+ documentFieldSpecificProps: (value: any) => {
443
+ if (!value) {
444
+ return undefined;
371
445
  }
446
+ return {
447
+ sourceData: value,
448
+ ...(modelField.source === 'bynder'
449
+ ? {
450
+ fields: {
451
+ title: {
452
+ type: 'string',
453
+ value: value?.name
454
+ },
455
+ url: {
456
+ type: 'string',
457
+ value: value?.previewImg
458
+ }
459
+ }
460
+ }
461
+ : null)
462
+ };
372
463
  }
373
- };
374
- }
375
- return {
376
- type: 'reference',
377
- refId: _.get(value, 'asset._ref'),
378
- refType: 'asset'
379
- };
380
- case 'file':
381
- if (!value) {
382
- return null;
383
- }
384
- return {
385
- type: 'reference',
386
- refId: _.get(value, 'asset._ref'),
387
- refType: 'asset'
388
- };
389
- case 'slug':
390
- if (!value) {
391
- return null;
464
+ });
392
465
  }
393
- return {
394
- type: 'slug',
395
- value: _.get(value, 'current')
396
- };
466
+ return createDocumentField({
467
+ fieldValue: value,
468
+ // The modelField.type and documentField.type should match.
469
+ // However, in the case of the modelField.type === 'image',
470
+ // it is OK to have the documentField.type === 'reference'
471
+ // and documentField.type === 'asset.
472
+ modelField: modelField as unknown as StackbitTypes.FieldReference,
473
+ documentFieldBaseProps: {
474
+ type: 'reference',
475
+ refType: 'asset'
476
+ },
477
+ documentFieldSpecificProps: (value: any) => {
478
+ if (!value) {
479
+ return undefined;
480
+ }
481
+ return {
482
+ refId: _.get(value, 'asset._ref')
483
+ };
484
+ }
485
+ });
486
+ }
487
+ case 'file': {
488
+ return createDocumentField({
489
+ fieldValue: value,
490
+ // The modelField.type and documentField.type should match.
491
+ // However, in the case of the modelField.type === 'file',
492
+ // it is OK to have the documentField.type === 'reference'
493
+ // and documentField.type === 'asset.
494
+ modelField: modelField as unknown as StackbitTypes.FieldReference,
495
+ documentFieldBaseProps: {
496
+ type: 'reference',
497
+ refType: 'asset'
498
+ },
499
+ documentFieldSpecificProps: (value: any) => {
500
+ if (!value) {
501
+ return undefined;
502
+ }
503
+ return {
504
+ refId: _.get(value, 'asset._ref')
505
+ };
506
+ }
507
+ });
508
+ }
509
+ case 'slug': {
510
+ return createDocumentField({
511
+ fieldValue: value,
512
+ modelField: modelField,
513
+ documentFieldBaseProps: { type: 'slug' },
514
+ documentFieldSpecificProps: (value: any) => {
515
+ if (!value) {
516
+ return undefined;
517
+ }
518
+ return {
519
+ value: _.get(value, 'current')
520
+ };
521
+ }
522
+ });
523
+ }
397
524
  default: {
398
525
  const _exhaustiveCheck: never = modelField;
399
526
  return _exhaustiveCheck;
@@ -408,7 +535,7 @@ type CommonFieldsType = {
408
535
  };
409
536
 
410
537
  function commonFields({ draftDocument, publishedDocument, documentHistory }: CommonFieldsType): {
411
- status: ContentSourceTypes.Document['status'];
538
+ status: StackbitTypes.Document['status'];
412
539
  createdAt: string;
413
540
  updatedAt: string;
414
541
  updatedBy: string[];
@@ -420,7 +547,7 @@ function commonFields({ draftDocument, publishedDocument, documentHistory }: Com
420
547
  }
421
548
 
422
549
  const isDraft = isDraftId(document._id);
423
- let status: ContentSourceTypes.Document['status'];
550
+ let status: StackbitTypes.Document['status'];
424
551
  if (isDraft && !publishedDocument) {
425
552
  status = 'added';
426
553
  } else {
@@ -437,82 +564,56 @@ function commonFields({ draftDocument, publishedDocument, documentHistory }: Com
437
564
  };
438
565
  }
439
566
 
440
- interface FieldListMultiItem {
441
- type: 'list';
442
- items: ContentSourceTypes.FieldListItems[];
443
- }
444
-
445
- /**
446
- * Sanity 'Array' field type can hold multiple field types.
447
- *
448
- * For example, Sanity Arrays can simultaneously include items of `model`
449
- * and `reference` types. https://www.sanity.io/docs/array-type#wT47gyCx
450
- *
451
- * With that, Sanity Arrays cannot include both primitive and complex types:
452
- * https://www.sanity.io/docs/array-type#fNBIr84P
453
- *
454
- * TODO:
455
- * This is not yet supported by Stackbit's TypeScript types, so the `any`
456
- * must be used. Additionally, if a Sanity array has multiple types of items one
457
- * of which is the 'object' type, then it will also have the 'name' property to
458
- * allow matching 'object' items to their types. The 'name' property is not
459
- * supported in Stackbit list items, so '@ts-ignore' is used.
460
- *
461
- * However, Stackbit client app should be able to render this types of lists correctly.
462
- *
463
- * @example A list that can include items of type 'model', 'reference' and 'object'.
464
- * {
465
- * type: 'list',
466
- * items: [{
467
- * type: 'model',
468
- * models: [...]
469
- * }, {
470
- * type: 'reference',
471
- * models: [...]
472
- * }, {
473
- * type: 'object',
474
- * name: 'nested_object_name',
475
- * fields: {...}
476
- * }]
477
- * }
478
- */
479
- function getItemTypeForListItem(listItem: any, fieldModel: FieldListMultiItem): ContentSourceTypes.FieldListItems | null {
480
- // Handle primitive list item types
481
- // For primitive list items, the list will hold the primitive values as is,
482
- // therefore, use JavaScript's `typeof` to infer the type of the values
483
- const type = _.get(listItem, '_type');
484
- if (!type) {
485
- const type = typeof listItem;
486
- if (typeIsPrimitive(type)) {
487
- return { type: type } as ContentSourceTypes.FieldListItems;
567
+ function createDocumentField<
568
+ Type extends StackbitTypes.FieldType,
569
+ BaseProps extends StackbitTypes.DocumentFieldBasePropsForType<Type>,
570
+ SpecificProps extends StackbitTypes.DocumentFieldSpecificPropsForType<Type>
571
+ >({
572
+ fieldValue,
573
+ modelField,
574
+ documentFieldBaseProps,
575
+ documentFieldSpecificProps
576
+ }: {
577
+ fieldValue: any;
578
+ modelField: StackbitTypes.FieldForType<Type> | StackbitTypes.FieldListItemsForType<Type>;
579
+ documentFieldBaseProps: BaseProps;
580
+ documentFieldSpecificProps: (value: any) => SpecificProps | undefined;
581
+ }): (BaseProps & (SpecificProps | { localized: true; locales: Record<string, { locale: string } & SpecificProps> })) | undefined {
582
+ if (isLocalizedModelField(modelField)) {
583
+ if (!Array.isArray(fieldValue)) {
584
+ return undefined;
488
585
  }
489
- return null;
586
+ return {
587
+ ...documentFieldBaseProps,
588
+ localized: true,
589
+ locales: _.reduce(
590
+ fieldValue,
591
+ (accum: Record<string, { locale: string } & SpecificProps>, localizedItem: { _key: string; value: any }) => {
592
+ if (localizedItem === null || typeof localizedItem === 'undefined') {
593
+ return accum;
594
+ }
595
+ const fieldSpecificProps = documentFieldSpecificProps(localizedItem.value);
596
+ if (!fieldSpecificProps) {
597
+ return accum;
598
+ }
599
+ accum[localizedItem._key] = {
600
+ locale: localizedItem._key,
601
+ ...fieldSpecificProps
602
+ };
603
+ return accum;
604
+ },
605
+ {}
606
+ )
607
+ };
490
608
  }
491
- const itemModels = fieldModel.items;
492
- if (type === 'reference') {
493
- return _.find(itemModels, { type: 'reference' }) ?? null;
494
- } else if (type === 'block') {
495
- return _.find(itemModels, { type: 'richText' }) ?? null;
496
- } else {
497
- return (
498
- _.find(itemModels, (itemModel) => {
499
- if (itemModel.type === 'model') {
500
- return _.includes(itemModel.models, type);
501
- } else {
502
- // if field was one of base types (object, image, slug, etc.)
503
- // and it had a "name" property, then the "_type" will be equal to that name,
504
- // otherwise the "_type" will be equal to the base type
505
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
506
- // @ts-ignore
507
- return itemModel.name === type || itemModel.type === type;
508
- }
509
- }) ?? null
510
- );
609
+ const fieldSpecificProps = documentFieldSpecificProps(fieldValue);
610
+ if (!fieldSpecificProps) {
611
+ return undefined;
511
612
  }
512
- }
513
-
514
- function typeIsPrimitive(type: string): type is 'string' | 'number' | 'boolean' {
515
- return ['string', 'number', 'boolean'].includes(type);
613
+ return {
614
+ ...documentFieldBaseProps,
615
+ ...fieldSpecificProps
616
+ };
516
617
  }
517
618
 
518
619
  function flattenRichText(richTextArray: any) {
@@ -541,7 +642,7 @@ function flattenRichText(richTextArray: any) {
541
642
  );
542
643
  }
543
644
 
544
- function getScheduledActionState(sanitySchedule: SanitySchedule): ScheduledActionState {
645
+ function getScheduledActionState(sanitySchedule: SanitySchedule): StackbitTypes.ScheduledActionState {
545
646
  if (sanitySchedule.state === 'cancelled' && sanitySchedule.stateReason !== 'cancelled by user') {
546
647
  return 'failed';
547
648
  }