@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
@@ -0,0 +1,664 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.localizedValue = exports.mapUpdateOperationFieldToSanityValue = exports.Operations = exports.convertUpdateOperation = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const uuid_1 = require("uuid");
9
+ const tinycolor2_1 = __importDefault(require("tinycolor2"));
10
+ const utils_1 = require("./utils");
11
+ function convertUpdateOperation({ operation, ...rest }) {
12
+ switch (operation.opType) {
13
+ case 'set':
14
+ return exports.Operations.set({ operation, ...rest });
15
+ case 'unset':
16
+ return exports.Operations.unset({ operation, ...rest });
17
+ case 'insert':
18
+ return exports.Operations.insert({ operation, ...rest });
19
+ case 'remove':
20
+ return exports.Operations.remove({ operation, ...rest });
21
+ case 'reorder':
22
+ return exports.Operations.reorder({ operation, ...rest });
23
+ }
24
+ }
25
+ exports.convertUpdateOperation = convertUpdateOperation;
26
+ exports.Operations = {
27
+ set: ({ operation, sanityDocument, getModelByName, model }) => {
28
+ const { field, fieldPath, modelField, locale } = operation;
29
+ const { patchFieldPath, modelFieldPath, localizedLeaf, isInList } = getPatchPathAndModelFieldPaths({
30
+ fieldPath,
31
+ sanityDocument,
32
+ getModelByName,
33
+ addValueToI18NLeafs: true,
34
+ allowUndefinedI18NLeafArrays: true,
35
+ locale
36
+ });
37
+ let value = mapUpdateOperationFieldToSanityValue({
38
+ updateOperationField: field,
39
+ getModelByName,
40
+ modelField,
41
+ rootModel: model,
42
+ modelFieldPath,
43
+ locale,
44
+ isInList
45
+ });
46
+ if ((0, utils_1.isLocalizedModelField)(modelField) && localizedLeaf !== 'value') {
47
+ value = localizedValue({
48
+ value,
49
+ model,
50
+ modelFieldPath,
51
+ locale
52
+ });
53
+ if (localizedLeaf === 'newItem') {
54
+ return {
55
+ insert: {
56
+ after: `${patchFieldPath}[-1]`,
57
+ items: [value]
58
+ }
59
+ };
60
+ }
61
+ else if (localizedLeaf === 'undefinedArray') {
62
+ return { set: { [patchFieldPath]: [value] } };
63
+ }
64
+ }
65
+ return { set: { [patchFieldPath]: value } };
66
+ },
67
+ unset: ({ operation, sanityDocument, getModelByName }) => {
68
+ const { fieldPath, locale } = operation;
69
+ const { patchFieldPath } = getPatchPathAndModelFieldPaths({
70
+ fieldPath,
71
+ sanityDocument,
72
+ getModelByName,
73
+ locale
74
+ });
75
+ return { unset: [patchFieldPath] };
76
+ },
77
+ insert: ({ operation, sanityDocument, getModelByName, model }) => {
78
+ const { item, fieldPath, modelField, index, locale } = operation;
79
+ const listItemModelField = modelField.items ?? { type: 'string' };
80
+ const { patchFieldPath, modelFieldPath, currentValue, localizedLeaf } = getPatchPathAndModelFieldPaths({
81
+ fieldPath,
82
+ sanityDocument,
83
+ getModelByName,
84
+ addValueToI18NLeafs: true,
85
+ allowUndefinedI18NLeafArrays: true,
86
+ locale
87
+ });
88
+ let value = mapUpdateOperationFieldToSanityValue({
89
+ updateOperationField: item,
90
+ getModelByName,
91
+ modelField: listItemModelField,
92
+ rootModel: model,
93
+ modelFieldPath: modelFieldPath.concat('items'),
94
+ locale,
95
+ isInList: true
96
+ });
97
+ // In the case of a localized array field, the field will contain an
98
+ // array of objects with localized arrays:
99
+ // [
100
+ // {
101
+ // _key: 'en',
102
+ // _type: 'internationalizedArrayLocalizedArray',
103
+ // value: ['en value 1', 'en value 2', 'en value 3']
104
+ // },
105
+ // {
106
+ // _key: 'es',
107
+ // _type: 'internationalizedArrayLocalizedArray',
108
+ // value: ['es value 1', 'es value 2', 'es value 3']
109
+ // }
110
+ // ]
111
+ if ((0, utils_1.isLocalizedModelField)(modelField) && localizedLeaf !== 'value') {
112
+ // When there is no array for a given locale, create a new localized
113
+ // array with a single value and insert it into the localized array:
114
+ // {
115
+ // _key: 'es',
116
+ // _type: 'internationalizedArrayLocalizedArray',
117
+ // value: [value]
118
+ // }
119
+ value = localizedValue({
120
+ value: [value],
121
+ model,
122
+ modelFieldPath,
123
+ locale
124
+ });
125
+ if (localizedLeaf === 'newItem') {
126
+ return {
127
+ insert: {
128
+ after: `${patchFieldPath}[-1]`,
129
+ items: [value]
130
+ }
131
+ };
132
+ }
133
+ else if (localizedLeaf === 'undefinedArray') {
134
+ return { set: { [patchFieldPath]: [value] } };
135
+ }
136
+ }
137
+ if (!currentValue) {
138
+ return { set: { [patchFieldPath]: [value] } };
139
+ }
140
+ else if (lodash_1.default.isNil(index) || index >= currentValue.length) {
141
+ return {
142
+ insert: {
143
+ after: `${patchFieldPath}[-1]`,
144
+ items: [value]
145
+ }
146
+ };
147
+ }
148
+ return {
149
+ insert: {
150
+ before: `${patchFieldPath}[${index}]`,
151
+ items: [value]
152
+ }
153
+ };
154
+ },
155
+ remove: ({ sanityDocument, operation, getModelByName }) => {
156
+ const { fieldPath, index, locale } = operation;
157
+ const { patchFieldPath } = getPatchPathAndModelFieldPaths({
158
+ fieldPath: fieldPath.concat(index),
159
+ sanityDocument,
160
+ getModelByName,
161
+ locale
162
+ });
163
+ return {
164
+ unset: [patchFieldPath]
165
+ };
166
+ },
167
+ reorder: ({ sanityDocument, operation, getModelByName }) => {
168
+ const { fieldPath, order, locale } = operation;
169
+ const { patchFieldPath, currentValue } = getPatchPathAndModelFieldPaths({
170
+ fieldPath,
171
+ sanityDocument,
172
+ getModelByName,
173
+ addValueToI18NLeafs: true,
174
+ locale
175
+ });
176
+ const newEntryArr = order.map((newIndex) => currentValue[newIndex]);
177
+ return { set: { [patchFieldPath]: newEntryArr } };
178
+ }
179
+ };
180
+ function mapUpdateOperationFieldToSanityValue({ updateOperationField, getModelByName, modelField, rootModel, modelFieldPath, locale, isInList }) {
181
+ switch (updateOperationField.type) {
182
+ case 'string':
183
+ case 'url':
184
+ case 'text':
185
+ case 'markdown':
186
+ case 'html':
187
+ case 'boolean':
188
+ case 'date':
189
+ case 'datetime':
190
+ case 'enum':
191
+ case 'style':
192
+ case 'json':
193
+ case 'richText':
194
+ case 'file': {
195
+ return updateOperationField.value;
196
+ }
197
+ case 'number': {
198
+ return Number(updateOperationField.value);
199
+ }
200
+ case 'slug': {
201
+ return {
202
+ _type: (0, utils_1.getSanityAliasFieldType)({
203
+ resolvedType: 'slug',
204
+ model: rootModel,
205
+ modelFieldPath
206
+ }),
207
+ current: updateOperationField.value
208
+ };
209
+ }
210
+ case 'color': {
211
+ const color = (0, tinycolor2_1.default)(updateOperationField.value);
212
+ return {
213
+ _type: (0, utils_1.getSanityAliasFieldType)({
214
+ resolvedType: 'color',
215
+ model: rootModel,
216
+ modelFieldPath
217
+ }),
218
+ hex: color.toHexString(),
219
+ alpha: color.getAlpha(),
220
+ hsl: {
221
+ _type: 'hslaColor',
222
+ ...color.toHsl()
223
+ },
224
+ hsv: {
225
+ _type: 'hsvaColor',
226
+ ...color.toHsv()
227
+ },
228
+ rgb: {
229
+ _type: 'rgbaColor',
230
+ ...color.toRgb()
231
+ }
232
+ };
233
+ }
234
+ case 'image': {
235
+ const value = updateOperationField?.value;
236
+ if (modelField.type === 'image') {
237
+ if (modelField.source === 'cloudinary' || modelField.source === 'aprimo') {
238
+ const type = modelField.source === 'cloudinary' ? 'cloudinary.asset' : 'aprimo.cdnasset';
239
+ return addKeyIfInList({
240
+ _type: type,
241
+ ...value
242
+ }, isInList);
243
+ }
244
+ else if (modelField.source === 'bynder') {
245
+ let imageValue = value;
246
+ if (imageValue?.__typename) {
247
+ imageValue = lodash_1.default.omitBy({
248
+ id: value.id,
249
+ name: value.name,
250
+ databaseId: value.databaseId,
251
+ type: value.type,
252
+ previewUrl: value.type === 'VIDEO' ? value.previewUrls[0] : value.files.webImage.url,
253
+ previewImg: value.files.webImage.url,
254
+ datUrl: value.files.transformBaseUrl?.url,
255
+ videoUrl: value.type === 'VIDEO' ? value.files.original?.url : null,
256
+ description: value.description,
257
+ aspectRatio: value.height / value.width
258
+ }, lodash_1.default.isUndefined);
259
+ }
260
+ return addKeyIfInList({
261
+ _type: 'bynder.asset',
262
+ ...imageValue
263
+ }, isInList);
264
+ }
265
+ }
266
+ // TODO: there is a bug right now because documentField is inferred from the model which is an "image", not reference
267
+ return addKeyIfInList(linkForAssetId(value), isInList);
268
+ }
269
+ case 'object': {
270
+ if (modelField.type !== 'object') {
271
+ throw new Error(`Operation field type 'object' does not match model field type '${modelField.type}'.`);
272
+ }
273
+ // Sanity array fields may consist of anonymous 'object' with names.
274
+ // When creating such objects, the '_type' should be set to their
275
+ // name to identify them among other 'object' types.
276
+ const fieldAlias = rootModel.context?.fieldAliasMap?.[modelFieldPath.join('.')] ?? [];
277
+ const typeName = fieldAlias?.find((alias) => alias.resolvedTypeName === 'object')?.origTypeName;
278
+ const object = addKeyIfInList({
279
+ ...(typeName ? { _type: typeName } : null)
280
+ }, isInList);
281
+ return lodash_1.default.reduce(updateOperationField.fields, (result, childUpdateOperationField, fieldName) => {
282
+ const childModelField = lodash_1.default.find(modelField.fields, (field) => field.name === fieldName);
283
+ if (!childModelField) {
284
+ throw new Error(`No model field found for field '${fieldName}' in model '${rootModel.name}' at field path ${modelFieldPath.join('.')}.`);
285
+ }
286
+ const childModelFieldPath = modelFieldPath.concat(fieldName);
287
+ const value = mapUpdateOperationFieldToSanityValue({
288
+ updateOperationField: childUpdateOperationField,
289
+ getModelByName,
290
+ modelField: childModelField,
291
+ rootModel,
292
+ modelFieldPath: childModelFieldPath,
293
+ locale
294
+ });
295
+ if ((0, utils_1.isLocalizedModelField)(childModelField)) {
296
+ lodash_1.default.set(result, fieldName, [
297
+ localizedValue({
298
+ value,
299
+ model: rootModel,
300
+ modelFieldPath: childModelFieldPath,
301
+ locale
302
+ })
303
+ ]);
304
+ }
305
+ else {
306
+ lodash_1.default.set(result, fieldName, value);
307
+ }
308
+ return result;
309
+ }, object);
310
+ }
311
+ case 'model': {
312
+ if (modelField.type !== 'model') {
313
+ throw new Error(`Operation field type 'model' does not match model field type '${modelField.type}'.`);
314
+ }
315
+ const modelName = updateOperationField.modelName;
316
+ const childModel = getModelByName(modelName);
317
+ if (!childModel) {
318
+ throw new Error(`No model '${modelName}' was found for field at '${modelFieldPath.join('.')}' in model '${rootModel.name}'.`);
319
+ }
320
+ const object = addKeyIfInList({
321
+ _type: (0, utils_1.getSanityAliasFieldType)({
322
+ resolvedType: modelName,
323
+ model: rootModel,
324
+ modelFieldPath
325
+ })
326
+ }, isInList);
327
+ return lodash_1.default.reduce(updateOperationField.fields, (result, childUpdateOperationField, fieldName) => {
328
+ const childModelField = lodash_1.default.find(childModel?.fields, (field) => field.name === fieldName);
329
+ if (!childModelField) {
330
+ throw new Error(`No model field found for field '${fieldName}' in model '${childModel.name}'.`);
331
+ }
332
+ const childModelFieldPath = [fieldName];
333
+ const value = mapUpdateOperationFieldToSanityValue({
334
+ updateOperationField: childUpdateOperationField,
335
+ getModelByName,
336
+ modelField: childModelField,
337
+ rootModel: childModel,
338
+ modelFieldPath: childModelFieldPath,
339
+ locale
340
+ });
341
+ if ((0, utils_1.isLocalizedModelField)(childModelField)) {
342
+ lodash_1.default.set(result, fieldName, [
343
+ localizedValue({
344
+ value,
345
+ model: rootModel,
346
+ modelFieldPath: childModelFieldPath,
347
+ locale
348
+ })
349
+ ]);
350
+ }
351
+ else {
352
+ lodash_1.default.set(result, fieldName, value);
353
+ }
354
+ return result;
355
+ }, object);
356
+ }
357
+ case 'reference': {
358
+ const value = updateOperationField.refType === 'document'
359
+ ? {
360
+ _ref: updateOperationField.refId,
361
+ _type: (0, utils_1.getSanityAliasFieldType)({
362
+ resolvedType: 'reference',
363
+ model: rootModel,
364
+ modelFieldPath
365
+ }),
366
+ // TODO: this is a bug!
367
+ // The _weak is not always `true`. Lookup the referenced document
368
+ // by id, and the original model field's `weak` value.
369
+ // If the referenced document was published (status === 'published'
370
+ // or status === 'modified'), then the _weak value should be set to
371
+ // the `weak` value defined in Sanity model field's.
372
+ // Otherwise, if the document was not published (status === 'added'),
373
+ // then the _weak value should be set to `true` and the
374
+ // _strengthenOnPublish.weak should be set to the `weak` value
375
+ // defined in the model field's.
376
+ _weak: true,
377
+ // TODO: Lookup the referenced document by id, and the original model
378
+ // field's `weak` value. If the referenced document was never published
379
+ // (status === 'added'), then add the _strengthenOnPublish object and
380
+ // set its `weak` property to the model field's `weak` value.
381
+ // When publishing objects with reference fields having the
382
+ // _strengthenOnPublish object, update the field's _weak with that of
383
+ // _strengthenOnPublish.weak.
384
+ // For more info: https://www.sanity.io/blog/obvious-features-aren-t-obviously-made#2c38c9f38060
385
+ _strengthenOnPublish: {
386
+ // type: <type for referenced item>,
387
+ // weak: <the _weak value of the original field>
388
+ }
389
+ }
390
+ : linkForAssetId(updateOperationField.refId);
391
+ return addKeyIfInList(value, isInList);
392
+ }
393
+ case 'cross-reference': {
394
+ throw new Error('Sanity crossDatasetReference fields not supported.');
395
+ }
396
+ case 'list': {
397
+ if (modelField.type !== 'list') {
398
+ throw new Error(`Operation field type 'list' does not match model field type '${modelField.type}'.`);
399
+ }
400
+ return updateOperationField.items.map((item, index) => {
401
+ let listItemModelField = modelField.items;
402
+ if (lodash_1.default.isArray(modelField.items)) {
403
+ const itemModel = modelField.items.find((listItemsModel) => listItemsModel.type === item.type);
404
+ if (!itemModel) {
405
+ throw new Error(`No list item model found for item type '${item.type}' in model '${rootModel.name}' at field path ${modelFieldPath.join('.')}.`);
406
+ }
407
+ listItemModelField = itemModel;
408
+ }
409
+ return mapUpdateOperationFieldToSanityValue({
410
+ updateOperationField: item,
411
+ getModelByName,
412
+ modelField: listItemModelField,
413
+ rootModel,
414
+ modelFieldPath: modelFieldPath.concat('items'),
415
+ locale,
416
+ isInList: true
417
+ });
418
+ });
419
+ }
420
+ default: {
421
+ const _exhaustiveCheck = updateOperationField;
422
+ return _exhaustiveCheck;
423
+ }
424
+ }
425
+ }
426
+ exports.mapUpdateOperationFieldToSanityValue = mapUpdateOperationFieldToSanityValue;
427
+ function addKeyIfInList(object, isInList) {
428
+ if (isInList) {
429
+ lodash_1.default.set(object, '_key', (0, uuid_1.v4)());
430
+ }
431
+ return object;
432
+ }
433
+ /**
434
+ * In Sanity, a localized field is represented by an array of objects containing the localized field values.
435
+ * Each object has three properties:
436
+ * - `_key` holds the field's locale
437
+ * - `_type` holds the type of the localized value
438
+ * - `value` holds the localized value.
439
+ *
440
+ * Note: the `_type` is not the regular type such as `string` or `object`,
441
+ * but a special localized type generated by the Sanity Internationalized Array plugin.
442
+ * The map between the localized fields and these types is stored in the model's context.
443
+ * title: [
444
+ * {
445
+ * _key: 'us',
446
+ * _type: 'internationalizedArrayStringValue',
447
+ * value: 'hello',
448
+ * }, {
449
+ * _key: 'es',
450
+ * _type: 'internationalizedArrayStringValue',
451
+ * value: 'hola',
452
+ * }
453
+ * ]
454
+ */
455
+ function localizedValue({ value, model, modelFieldPath, locale }) {
456
+ const localizedFieldModelNameMap = model.context?.localizedFieldsModelMap?.[modelFieldPath.join('.')];
457
+ if (!localizedFieldModelNameMap) {
458
+ throw Error(`Internationalized array model for localized field at path ${modelFieldPath.join('.')} of model ${model.name} not found.`);
459
+ }
460
+ if (!locale) {
461
+ throw Error(`No locale provided for localized field at path ${modelFieldPath.join('.')} of model ${model.name}.`);
462
+ }
463
+ return {
464
+ _key: locale,
465
+ _type: localizedFieldModelNameMap.arrayValueModelName,
466
+ value: value
467
+ };
468
+ }
469
+ exports.localizedValue = localizedValue;
470
+ function linkForAssetId(assetId) {
471
+ return {
472
+ _type: 'image',
473
+ asset: {
474
+ _ref: assetId,
475
+ _type: 'reference'
476
+ }
477
+ };
478
+ }
479
+ /**
480
+ * Receives a `fieldPath` of a target field in a `sanityDocument` and returns
481
+ * an object with following properties:
482
+ *
483
+ * - `model`: The closest ancestor model of the target field.
484
+ * - `modelFieldPath`: The field path of the target field from the `model`.
485
+ * The model's field path doesn't identify a specific document field,
486
+ * therefore it does not include list indexes.
487
+ * - `patchFieldPath`: A Sanity specific field path for patching fields.
488
+ * For array items, the field path will include [_key="..."] when possible.
489
+ * For localized fields, which represented by arrays in Sanity, the path
490
+ * will point to the localized array item using the `_key`:
491
+ * `sections[_key=="es"].value[3].title`
492
+ * - `localizedLeaf`: A string specifying the leaf value in the `patchFieldPath`
493
+ * when the target field is localized:
494
+ * - `value`: The `patchFieldPath` points to the localized value:
495
+ * `sections[_key=="es"].value
496
+ * - `newItem`: The `patchFieldPath` points to the existing localized array,
497
+ * which does not include an item for the provided locale.
498
+ * Returned only when `allowUndefinedI18NLeafArrays` set to `true`.
499
+ * - `undefinedArray`: The `patchFieldPath` points to undefined localized array.
500
+ * Returned only when `allowUndefinedI18NLeafArrays` set to `true`.
501
+ * - `currentValue`: The value of the target field. If the target field is
502
+ * localized but doesn't have a value for the provided locale,
503
+ * the `currentValue` will be undefined.
504
+ */
505
+ function getPatchPathAndModelFieldPaths({ fieldPath, sanityDocument, getModelByName, addValueToI18NLeafs, allowUndefinedI18NLeafArrays, locale }) {
506
+ function iterateObject({ object, model, rootModel, modelFieldPath, fieldPath, first = false }) {
507
+ const [fieldName, ...fieldPathTail] = fieldPath;
508
+ if (typeof fieldName === 'undefined') {
509
+ throw new Error('The fieldPath cannot be empty.');
510
+ }
511
+ const modelField = (model.fields ?? []).find((field) => field.name === fieldName);
512
+ if (!modelField) {
513
+ throw new Error(`Model field for field '${fieldName}' not found.`);
514
+ }
515
+ let patchFieldPath = '';
516
+ if (/\W/.test(fieldName)) {
517
+ // field name is a string with non-alphanumeric characters
518
+ patchFieldPath += `['${fieldName}']`;
519
+ }
520
+ else {
521
+ if (!first) {
522
+ patchFieldPath += '.';
523
+ }
524
+ patchFieldPath += fieldName;
525
+ }
526
+ let value = object[fieldName];
527
+ let localizedLeaf;
528
+ if (modelField?.localized) {
529
+ if (Array.isArray(value)) {
530
+ const localizedItem = value.find((item) => item._key === locale);
531
+ if (localizedItem) {
532
+ patchFieldPath += `[_key=="${locale}"]`;
533
+ if (fieldPathTail.length === 0) {
534
+ localizedLeaf = 'value';
535
+ if (addValueToI18NLeafs) {
536
+ patchFieldPath += '.value';
537
+ }
538
+ }
539
+ else {
540
+ patchFieldPath += '.value';
541
+ }
542
+ value = localizedItem.value;
543
+ }
544
+ else if (fieldPathTail.length === 0 && allowUndefinedI18NLeafArrays) {
545
+ localizedLeaf = 'newItem';
546
+ value = undefined;
547
+ }
548
+ else {
549
+ throw new Error(`The localized field '${fieldName}' has no value for locale '${locale}'.`);
550
+ }
551
+ }
552
+ else if (fieldPathTail.length === 0 && allowUndefinedI18NLeafArrays) {
553
+ localizedLeaf = 'undefinedArray';
554
+ value = undefined;
555
+ }
556
+ else {
557
+ throw new Error(`The localized field '${fieldName}' has no localization array defined for locale '${locale}'.`);
558
+ }
559
+ }
560
+ const result = iterateField({
561
+ value,
562
+ modelField,
563
+ fieldPath: fieldPathTail,
564
+ rootModel,
565
+ modelFieldPath: modelFieldPath.concat(fieldName),
566
+ isInList: false
567
+ });
568
+ return {
569
+ model: result.model,
570
+ modelFieldPath: result.modelFieldPath,
571
+ patchFieldPath: patchFieldPath + result.patchFieldPath,
572
+ localizedLeaf: localizedLeaf ?? result.localizedLeaf,
573
+ currentValue: result.currentValue,
574
+ isInList: result.isInList
575
+ };
576
+ }
577
+ function iterateField({ value, modelField, fieldPath, rootModel, modelFieldPath, isInList }) {
578
+ if (fieldPath.length === 0) {
579
+ return {
580
+ patchFieldPath: '',
581
+ currentValue: value,
582
+ model: rootModel,
583
+ modelFieldPath,
584
+ isInList: isInList
585
+ };
586
+ }
587
+ if (typeof value === 'undefined') {
588
+ throw new Error(`Field path has more items [${fieldPath.join('.')}], but value is undefined.`);
589
+ }
590
+ if (modelField?.type === 'object') {
591
+ return iterateObject({
592
+ object: value,
593
+ model: modelField,
594
+ rootModel,
595
+ modelFieldPath,
596
+ fieldPath
597
+ });
598
+ }
599
+ else if (modelField?.type === 'model') {
600
+ const modelName = (0, utils_1.resolvedFieldType)({
601
+ sanityFieldType: value._type,
602
+ model: rootModel,
603
+ modelFieldPath
604
+ });
605
+ const model = getModelByName(modelName);
606
+ if (!model) {
607
+ throw new Error(`Model '${modelName}' not found.`);
608
+ }
609
+ return iterateObject({
610
+ object: value,
611
+ model,
612
+ rootModel: model,
613
+ modelFieldPath: [],
614
+ fieldPath
615
+ });
616
+ }
617
+ else if (modelField?.type === 'list') {
618
+ const [itemIndex, ...fieldPathTail] = fieldPath;
619
+ const listItem = value[itemIndex];
620
+ const itemModel = (0, utils_1.getItemTypeForListItem)(listItem, modelField);
621
+ if (!itemModel) {
622
+ throw new Error('Could not resolve type of a list item.');
623
+ }
624
+ const result = iterateField({
625
+ value: listItem,
626
+ modelField: itemModel,
627
+ rootModel,
628
+ modelFieldPath: modelFieldPath.concat('items'),
629
+ fieldPath: fieldPathTail,
630
+ isInList: true
631
+ });
632
+ // try to use Sanity _key as explicit accessor
633
+ const key = listItem?._key;
634
+ const patchFieldPath = key ? `[_key=="${key}"]` : `[${Number(itemIndex)}]`;
635
+ return {
636
+ // model fieldPath doesn't include array indexes.
637
+ // return fieldPath with field names only, no list indexes
638
+ model: result.model,
639
+ modelFieldPath: result.modelFieldPath,
640
+ // fieldPath for Sanity patches should always include array indexes.
641
+ patchFieldPath: patchFieldPath + result.patchFieldPath,
642
+ currentValue: result.currentValue,
643
+ isInList: result.isInList
644
+ };
645
+ }
646
+ else {
647
+ throw new Error(`Field path has more items [${fieldPath.join('.')}], but no objects/arrays left to iterate.`);
648
+ }
649
+ }
650
+ const modelName = sanityDocument._type;
651
+ const model = getModelByName(modelName);
652
+ if (!model) {
653
+ throw new Error(`Model '${modelName}' not found.`);
654
+ }
655
+ return iterateObject({
656
+ object: sanityDocument,
657
+ model,
658
+ rootModel: model,
659
+ modelFieldPath: [],
660
+ fieldPath,
661
+ first: true
662
+ });
663
+ }
664
+ //# sourceMappingURL=sanity-operation-converter.js.map