@sanity/agent-mutations 0.1.0
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.
- package/dist/index.d.ts +452 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1644 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1644 @@
|
|
|
1
|
+
import { isInternationalizedArrayField, isV5InternationalizedArray, createError, getValueAtPath, resolveFieldSchemaAtPath, validateValue, validateInternationalizedArraySet, validateDocumentLanguageField, validateInternationalizedArrayItems, validateUniqueItems } from "@sanity/agent-schema/validation";
|
|
2
|
+
import { resolveFieldSchema, isPrimitiveType } from "@sanity/agent-schema";
|
|
3
|
+
import { isPublishedId, isVersionId, isDraftId, getPublishedId } from "@sanity/id-utils";
|
|
4
|
+
function extractArrayPath(path) {
|
|
5
|
+
const arrayFields = [], currentPath = path;
|
|
6
|
+
let offset = 0;
|
|
7
|
+
for (; offset < currentPath.length; ) {
|
|
8
|
+
const selectorMatch = currentPath.slice(offset).match(/^([^.[]+)(\[_key==["'][^"']+["']\])/);
|
|
9
|
+
if (selectorMatch)
|
|
10
|
+
arrayFields.push(selectorMatch[1]), offset += selectorMatch[0].length, currentPath[offset] === "." && offset++;
|
|
11
|
+
else {
|
|
12
|
+
const remainingMatch = currentPath.slice(offset).match(/^([^.[]+)/);
|
|
13
|
+
remainingMatch && arrayFields.length > 0 && arrayFields.push(remainingMatch[1]);
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (arrayFields.length > 0)
|
|
18
|
+
return arrayFields.join(".");
|
|
19
|
+
const firstFieldMatch = path.match(/^([^.[]+)/);
|
|
20
|
+
return firstFieldMatch ? firstFieldMatch[1] : path;
|
|
21
|
+
}
|
|
22
|
+
function groupOperationsByArrayContext(params) {
|
|
23
|
+
const groups = [];
|
|
24
|
+
if ((params.set && params.set.length > 0 || params.unset && params.unset.length > 0 || params.insertBefore && params.insertBefore.length > 0 || params.insertAfter && params.insertAfter.length > 0) && groups.push({
|
|
25
|
+
context: "main",
|
|
26
|
+
operations: {
|
|
27
|
+
set: params.set || [],
|
|
28
|
+
unset: params.unset || [],
|
|
29
|
+
append: [],
|
|
30
|
+
insertBefore: params.insertBefore || [],
|
|
31
|
+
insertAfter: params.insertAfter || []
|
|
32
|
+
}
|
|
33
|
+
}), params.append && params.append.length > 0) {
|
|
34
|
+
const appendsByArrayPath = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const appendOp of params.append) {
|
|
36
|
+
const arrayPath = extractArrayPath(appendOp.path);
|
|
37
|
+
appendsByArrayPath.has(arrayPath) || appendsByArrayPath.set(arrayPath, []), appendsByArrayPath.get(arrayPath).push(appendOp);
|
|
38
|
+
}
|
|
39
|
+
for (const [arrayPath, appendOps] of appendsByArrayPath.entries())
|
|
40
|
+
if (appendOps.length > 1)
|
|
41
|
+
for (let i = 0; i < appendOps.length; i++)
|
|
42
|
+
groups.push({
|
|
43
|
+
context: `append:${arrayPath}:${i}`,
|
|
44
|
+
operations: {
|
|
45
|
+
set: [],
|
|
46
|
+
unset: [],
|
|
47
|
+
append: [appendOps[i]],
|
|
48
|
+
insertBefore: [],
|
|
49
|
+
insertAfter: []
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
else
|
|
53
|
+
groups.push({
|
|
54
|
+
context: `append:${arrayPath}`,
|
|
55
|
+
operations: {
|
|
56
|
+
set: [],
|
|
57
|
+
unset: [],
|
|
58
|
+
append: appendOps,
|
|
59
|
+
insertBefore: [],
|
|
60
|
+
insertAfter: []
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return groups;
|
|
65
|
+
}
|
|
66
|
+
function buildPatchTransaction(options) {
|
|
67
|
+
const { client, documentId, operations, setIfMissing } = options, transaction = client.transaction(), groups = groupOperationsByArrayContext(operations);
|
|
68
|
+
for (const [path, value] of Object.entries(setIfMissing))
|
|
69
|
+
transaction.patch(documentId, { setIfMissing: { [path]: value } });
|
|
70
|
+
for (const group of groups) {
|
|
71
|
+
for (const op of group.operations.insertBefore) {
|
|
72
|
+
const path = `${op.arrayPath}[_key=="${op.key}"]`;
|
|
73
|
+
transaction.patch(documentId, { insert: { before: path, items: op.items } });
|
|
74
|
+
}
|
|
75
|
+
for (const op of group.operations.insertAfter) {
|
|
76
|
+
const path = `${op.arrayPath}[_key=="${op.key}"]`;
|
|
77
|
+
transaction.patch(documentId, { insert: { after: path, items: op.items } });
|
|
78
|
+
}
|
|
79
|
+
for (const op of group.operations.set)
|
|
80
|
+
op.value !== void 0 && transaction.patch(documentId, { set: { [op.path]: op.value } });
|
|
81
|
+
for (const path of group.operations.unset)
|
|
82
|
+
transaction.patch(documentId, { unset: [path] });
|
|
83
|
+
for (const op of group.operations.append)
|
|
84
|
+
transaction.patch(documentId, { insert: { after: op.path, items: op.items } });
|
|
85
|
+
}
|
|
86
|
+
return transaction;
|
|
87
|
+
}
|
|
88
|
+
const PRIMITIVE_TYPES = [
|
|
89
|
+
"string",
|
|
90
|
+
"number",
|
|
91
|
+
"boolean",
|
|
92
|
+
"date",
|
|
93
|
+
"datetime",
|
|
94
|
+
"text",
|
|
95
|
+
"url",
|
|
96
|
+
"email",
|
|
97
|
+
"slug"
|
|
98
|
+
];
|
|
99
|
+
function isPrimitiveArray(field) {
|
|
100
|
+
return !field || field.type !== "array" || !field.of || field.of.length === 0 ? !1 : field.of.every((item) => {
|
|
101
|
+
const itemType = item.type || (typeof item == "string" ? item : "");
|
|
102
|
+
return !itemType || typeof itemType != "string" ? !1 : PRIMITIVE_TYPES.includes(itemType);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function hasNumericIndex(path) {
|
|
106
|
+
return /\[\d+\]/.test(path);
|
|
107
|
+
}
|
|
108
|
+
function parseArrayIndex(path) {
|
|
109
|
+
const match = path.match(/^(.*?)\[(\d+)\](.*)$/);
|
|
110
|
+
return match ? {
|
|
111
|
+
basePath: match[1],
|
|
112
|
+
index: parseInt(match[2], 10),
|
|
113
|
+
restPath: match[3]
|
|
114
|
+
} : null;
|
|
115
|
+
}
|
|
116
|
+
function getArrayAtPath(document, path) {
|
|
117
|
+
const parts = path.split(/\.(?![^[]*\])/);
|
|
118
|
+
let current = document;
|
|
119
|
+
for (const part of parts) {
|
|
120
|
+
const keyMatch = part.match(/^([^[]+)\[_key=="([^"]+)"\]$/);
|
|
121
|
+
if (keyMatch) {
|
|
122
|
+
const [, fieldName, keyValue] = keyMatch, currentObj = current;
|
|
123
|
+
if (!currentObj[fieldName] || !Array.isArray(currentObj[fieldName]) || (current = currentObj[fieldName].find((item) => item._key === keyValue), !current)) return null;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (!current || typeof current != "object")
|
|
127
|
+
return null;
|
|
128
|
+
current = current[part];
|
|
129
|
+
}
|
|
130
|
+
return Array.isArray(current) ? current : null;
|
|
131
|
+
}
|
|
132
|
+
function getKeyAtIndex(array, index) {
|
|
133
|
+
if (index < 0 || index >= array.length)
|
|
134
|
+
return null;
|
|
135
|
+
const item = array[index];
|
|
136
|
+
if (!item || typeof item != "object")
|
|
137
|
+
return null;
|
|
138
|
+
const key = item._key;
|
|
139
|
+
return typeof key == "string" ? key : null;
|
|
140
|
+
}
|
|
141
|
+
const BUILT_IN_BLOCK_SCHEMA = {
|
|
142
|
+
name: "block",
|
|
143
|
+
type: "object",
|
|
144
|
+
fields: [
|
|
145
|
+
{ name: "style", type: "string" },
|
|
146
|
+
{ name: "children", type: "array", of: [{ type: "span" }] },
|
|
147
|
+
{ name: "markDefs", type: "array", of: [{ type: "object" }] },
|
|
148
|
+
{ name: "listItem", type: "string" },
|
|
149
|
+
{ name: "level", type: "number" }
|
|
150
|
+
]
|
|
151
|
+
}, BUILT_IN_SPAN_SCHEMA = {
|
|
152
|
+
name: "span",
|
|
153
|
+
type: "object",
|
|
154
|
+
fields: [
|
|
155
|
+
{ name: "text", type: "string" },
|
|
156
|
+
{ name: "marks", type: "array", of: [{ type: "string" }] }
|
|
157
|
+
]
|
|
158
|
+
}, BUILT_IN_SCHEMAS = {
|
|
159
|
+
block: BUILT_IN_BLOCK_SCHEMA,
|
|
160
|
+
span: BUILT_IN_SPAN_SCHEMA
|
|
161
|
+
};
|
|
162
|
+
function getDocumentItemAtPath(document, pathParts, upToIndex) {
|
|
163
|
+
let current = document;
|
|
164
|
+
for (let i = 0; i <= upToIndex; i++) {
|
|
165
|
+
const part = pathParts[i];
|
|
166
|
+
if (current == null || typeof current != "object")
|
|
167
|
+
return null;
|
|
168
|
+
const keyMatch = part.match(/^([^[]+)\[_key=="([^"]+)"\]$/);
|
|
169
|
+
if (keyMatch) {
|
|
170
|
+
const [, fieldName, keyValue] = keyMatch, arr = current[fieldName];
|
|
171
|
+
if (!Array.isArray(arr)) return null;
|
|
172
|
+
current = arr.find((item) => item && typeof item == "object" ? item._key === keyValue : !1);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const indexMatch = part.match(/^([^[]+)\[(\d+)\]$/);
|
|
176
|
+
if (indexMatch) {
|
|
177
|
+
const [, fieldName, indexStr] = indexMatch, arr = current[fieldName];
|
|
178
|
+
if (!Array.isArray(arr)) return null;
|
|
179
|
+
const index = parseInt(indexStr, 10);
|
|
180
|
+
if (index < 0 || index >= arr.length) return null;
|
|
181
|
+
current = arr[index];
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
current = current[part];
|
|
185
|
+
}
|
|
186
|
+
return current;
|
|
187
|
+
}
|
|
188
|
+
function getFieldAtPath(path, typeSchema, allSchemas, document) {
|
|
189
|
+
const parts = path.split(/\.(?![^[]*\])/);
|
|
190
|
+
let currentSchema = typeSchema;
|
|
191
|
+
for (let i = 0; i < parts.length; i++) {
|
|
192
|
+
const part = parts[i], keyMatch = part.match(/^([^[]+)\[_key=="([^"]+)"\]$/), indexMatch = part.match(/^([^[]+)\[(\d+)\]$/), fieldName = keyMatch ? keyMatch[1] : indexMatch ? indexMatch[1] : part, field = currentSchema.fields?.find((f) => f.name === fieldName);
|
|
193
|
+
if (!field) return null;
|
|
194
|
+
if (i === parts.length - 1)
|
|
195
|
+
return field;
|
|
196
|
+
let resolvedField = field;
|
|
197
|
+
if (field.type !== "array" && field.type !== "object" && field.type !== "string" && field.type !== "number" && field.type !== "boolean") {
|
|
198
|
+
const aliasSchema = allSchemas.find((s) => s.name === field.type);
|
|
199
|
+
aliasSchema && aliasSchema.type === "array" && aliasSchema.of && (resolvedField = { ...field, type: "array", of: aliasSchema.of });
|
|
200
|
+
}
|
|
201
|
+
if (resolvedField.type === "array" && resolvedField.of && resolvedField.of.length > 0) {
|
|
202
|
+
let itemSchema;
|
|
203
|
+
if (document && (keyMatch || indexMatch)) {
|
|
204
|
+
const docItem = getDocumentItemAtPath(document, parts, i);
|
|
205
|
+
if (docItem && typeof docItem == "object" && "_type" in docItem) {
|
|
206
|
+
const actualType = docItem._type;
|
|
207
|
+
if (itemSchema = allSchemas.find((s) => s.name === actualType) || BUILT_IN_SCHEMAS[actualType], !itemSchema) {
|
|
208
|
+
for (const itemTypeRef of resolvedField.of)
|
|
209
|
+
if (typeof itemTypeRef == "object" && itemTypeRef.name === actualType) {
|
|
210
|
+
itemSchema = itemTypeRef;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!itemSchema)
|
|
217
|
+
for (const itemTypeRef of resolvedField.of) {
|
|
218
|
+
const itemTypeName = typeof itemTypeRef == "string" ? itemTypeRef : itemTypeRef.type;
|
|
219
|
+
if (itemTypeName && (itemSchema = allSchemas.find((s) => s.name === itemTypeName) || BUILT_IN_SCHEMAS[itemTypeName], itemSchema))
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (!itemSchema)
|
|
223
|
+
return null;
|
|
224
|
+
currentSchema = itemSchema;
|
|
225
|
+
} else if (resolvedField.type === "object" && resolvedField.fields)
|
|
226
|
+
currentSchema = resolvedField;
|
|
227
|
+
else
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
function convertArrayIndexOperations(operations, typeSchema, allSchemas, document) {
|
|
233
|
+
const primitiveArrayOps = /* @__PURE__ */ new Map(), passThroughSets = [], passThroughUnsets = [];
|
|
234
|
+
if (operations.set && operations.set.length > 0)
|
|
235
|
+
for (const op of operations.set) {
|
|
236
|
+
if (!hasNumericIndex(op.path)) {
|
|
237
|
+
passThroughSets.push(op);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const parsed = parseArrayIndex(op.path);
|
|
241
|
+
if (!parsed) {
|
|
242
|
+
passThroughSets.push(op);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const { basePath, index, restPath } = parsed, field = getFieldAtPath(basePath, typeSchema, allSchemas, document);
|
|
246
|
+
if (!field)
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Field validation failed:
|
|
249
|
+
- ${op.path}: Field "${basePath}" does not exist in schema "${typeSchema.name}"
|
|
250
|
+
|
|
251
|
+
Please correct the invalid field paths and try again.`
|
|
252
|
+
);
|
|
253
|
+
if (field.type !== "array")
|
|
254
|
+
throw new Error(
|
|
255
|
+
`Field validation failed:
|
|
256
|
+
- ${op.path}: Field "${basePath}" is not an array (type: ${field.type})
|
|
257
|
+
|
|
258
|
+
Please correct the invalid field paths and try again.`
|
|
259
|
+
);
|
|
260
|
+
if (isPrimitiveArray(field)) {
|
|
261
|
+
if (restPath)
|
|
262
|
+
throw new Error(
|
|
263
|
+
`Field validation failed:
|
|
264
|
+
- ${op.path}: Cannot access nested path "${restPath}" on primitive array item at "${basePath}[${index}]"
|
|
265
|
+
|
|
266
|
+
Primitive arrays (strings, numbers, etc.) don't have nested fields.`
|
|
267
|
+
);
|
|
268
|
+
const group = primitiveArrayOps.get(basePath) || { sets: [], unsets: [] };
|
|
269
|
+
group.sets.push({ index, value: op.value }), primitiveArrayOps.set(basePath, group);
|
|
270
|
+
} else {
|
|
271
|
+
const converted = convertObjectArraySetOperation(
|
|
272
|
+
op,
|
|
273
|
+
basePath,
|
|
274
|
+
index,
|
|
275
|
+
restPath,
|
|
276
|
+
typeSchema,
|
|
277
|
+
allSchemas,
|
|
278
|
+
document
|
|
279
|
+
);
|
|
280
|
+
passThroughSets.push(...converted);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (operations.unset && operations.unset.length > 0)
|
|
284
|
+
for (const path of operations.unset) {
|
|
285
|
+
if (!hasNumericIndex(path)) {
|
|
286
|
+
passThroughUnsets.push(path);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const parsed = parseArrayIndex(path);
|
|
290
|
+
if (!parsed) {
|
|
291
|
+
passThroughUnsets.push(path);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const { basePath, index, restPath } = parsed, field = getFieldAtPath(basePath, typeSchema, allSchemas, document);
|
|
295
|
+
if (!field)
|
|
296
|
+
throw new Error(
|
|
297
|
+
`Field validation failed:
|
|
298
|
+
- ${path}: Field "${basePath}" does not exist in schema "${typeSchema.name}"
|
|
299
|
+
|
|
300
|
+
Please correct the invalid field paths and try again.`
|
|
301
|
+
);
|
|
302
|
+
if (field.type !== "array")
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Field validation failed:
|
|
305
|
+
- ${path}: Field "${basePath}" is not an array (type: ${field.type})
|
|
306
|
+
|
|
307
|
+
Please correct the invalid field paths and try again.`
|
|
308
|
+
);
|
|
309
|
+
if (isPrimitiveArray(field)) {
|
|
310
|
+
if (restPath)
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Field validation failed:
|
|
313
|
+
- ${path}: Cannot access nested path "${restPath}" on primitive array item at "${basePath}[${index}]"
|
|
314
|
+
|
|
315
|
+
Primitive arrays (strings, numbers, etc.) don't have nested fields.`
|
|
316
|
+
);
|
|
317
|
+
const group = primitiveArrayOps.get(basePath) || { sets: [], unsets: [] };
|
|
318
|
+
group.unsets.push(index), primitiveArrayOps.set(basePath, group);
|
|
319
|
+
} else
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Unsetting object array items by index is not supported. Path: ${path}
|
|
322
|
+
|
|
323
|
+
To remove an item from an object array, use the unset operation with _key notation: items[_key=="..."]`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
const appendsToMerge = /* @__PURE__ */ new Map(), passThroughAppends = [];
|
|
327
|
+
if (operations.append)
|
|
328
|
+
for (const appendOp of operations.append)
|
|
329
|
+
if (primitiveArrayOps.has(appendOp.path)) {
|
|
330
|
+
const existingItems = appendsToMerge.get(appendOp.path) ?? [];
|
|
331
|
+
appendsToMerge.set(appendOp.path, [...existingItems, ...appendOp.items]);
|
|
332
|
+
} else
|
|
333
|
+
passThroughAppends.push(appendOp);
|
|
334
|
+
const mergedSets = mergePrimitiveArrayOperations(primitiveArrayOps, appendsToMerge, document), result = {}, allSets = [...passThroughSets, ...mergedSets];
|
|
335
|
+
return allSets.length > 0 && (result.set = allSets), passThroughUnsets.length > 0 && (result.unset = passThroughUnsets), passThroughAppends.length > 0 && (result.append = passThroughAppends), operations.insertBefore && operations.insertBefore.length > 0 && (result.insertBefore = operations.insertBefore), operations.insertAfter && operations.insertAfter.length > 0 && (result.insertAfter = operations.insertAfter), result;
|
|
336
|
+
}
|
|
337
|
+
function mergePrimitiveArrayOperations(primitiveArrayOps, appendsToMerge, document) {
|
|
338
|
+
const mergedSets = [];
|
|
339
|
+
for (const [basePath, ops] of primitiveArrayOps) {
|
|
340
|
+
const currentArray = getArrayAtPath(document, basePath);
|
|
341
|
+
if (!currentArray)
|
|
342
|
+
throw new Error(
|
|
343
|
+
`Field validation failed:
|
|
344
|
+
- ${basePath}: Array "${basePath}" not found in document or is empty
|
|
345
|
+
|
|
346
|
+
Please ensure the array exists before modifying items by index.`
|
|
347
|
+
);
|
|
348
|
+
const maxIndex = currentArray.length - 1;
|
|
349
|
+
for (const { index } of ops.sets)
|
|
350
|
+
if (index < 0 || index > maxIndex)
|
|
351
|
+
throw new Error(
|
|
352
|
+
`Field validation failed:
|
|
353
|
+
- ${basePath}[${index}]: Index ${index} is out of bounds for array "${basePath}" (length: ${currentArray.length})
|
|
354
|
+
|
|
355
|
+
Please use a valid index between 0 and ${maxIndex}.`
|
|
356
|
+
);
|
|
357
|
+
for (const index of ops.unsets)
|
|
358
|
+
if (index < 0 || index > maxIndex)
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Field validation failed:
|
|
361
|
+
- ${basePath}[${index}]: Index ${index} is out of bounds for array "${basePath}" (length: ${currentArray.length})
|
|
362
|
+
|
|
363
|
+
Please use a valid index between 0 and ${maxIndex}.`
|
|
364
|
+
);
|
|
365
|
+
const newArray = [...currentArray];
|
|
366
|
+
for (const { index, value } of ops.sets)
|
|
367
|
+
newArray[index] = value;
|
|
368
|
+
const uniqueUnsets = [...new Set(ops.unsets)].sort((a, b) => b - a);
|
|
369
|
+
for (const index of uniqueUnsets)
|
|
370
|
+
newArray.splice(index, 1);
|
|
371
|
+
const itemsToAppend = appendsToMerge.get(basePath);
|
|
372
|
+
itemsToAppend && newArray.push(...itemsToAppend), mergedSets.push({ path: basePath, value: newArray });
|
|
373
|
+
}
|
|
374
|
+
return mergedSets;
|
|
375
|
+
}
|
|
376
|
+
function convertObjectArraySetOperation(op, basePath, index, restPath, typeSchema, allSchemas, document) {
|
|
377
|
+
const currentArray = getArrayAtPath(document, basePath);
|
|
378
|
+
if (!currentArray)
|
|
379
|
+
throw new Error(
|
|
380
|
+
`Field validation failed:
|
|
381
|
+
- ${op.path}: Array "${basePath}" not found in document or is empty
|
|
382
|
+
|
|
383
|
+
Please ensure the array exists before modifying items by index.`
|
|
384
|
+
);
|
|
385
|
+
if (index < 0 || index >= currentArray.length)
|
|
386
|
+
throw new Error(
|
|
387
|
+
`Field validation failed:
|
|
388
|
+
- ${op.path}: Index ${index} is out of bounds for array "${basePath}" (length: ${currentArray.length})
|
|
389
|
+
|
|
390
|
+
Please use a valid index between 0 and ${currentArray.length - 1}.`
|
|
391
|
+
);
|
|
392
|
+
const key = getKeyAtIndex(currentArray, index);
|
|
393
|
+
if (!key)
|
|
394
|
+
throw new Error(
|
|
395
|
+
`Field validation failed:
|
|
396
|
+
- ${op.path}: Array item at "${basePath}[${index}]" does not have a _key field
|
|
397
|
+
|
|
398
|
+
Object arrays require _key fields for addressing. Please ensure all array items have _key fields.`
|
|
399
|
+
);
|
|
400
|
+
const newPath = `${basePath}[_key=="${key}"]${restPath}`;
|
|
401
|
+
if (restPath && hasNumericIndex(restPath)) {
|
|
402
|
+
const nestedParsed = parseArrayIndex(newPath);
|
|
403
|
+
if (nestedParsed) {
|
|
404
|
+
const nestedField = getFieldAtPath(nestedParsed.basePath, typeSchema, allSchemas, document);
|
|
405
|
+
if (nestedField && nestedField.type === "array")
|
|
406
|
+
if (isPrimitiveArray(nestedField)) {
|
|
407
|
+
const nestedArray = getArrayAtPath(document, nestedParsed.basePath);
|
|
408
|
+
if (!nestedArray)
|
|
409
|
+
throw new Error(
|
|
410
|
+
`Field validation failed:
|
|
411
|
+
- ${op.path}: Array "${nestedParsed.basePath}" not found in document
|
|
412
|
+
|
|
413
|
+
Please ensure the array exists before modifying items by index.`
|
|
414
|
+
);
|
|
415
|
+
const newArray = [...nestedArray];
|
|
416
|
+
return newArray[nestedParsed.index] = op.value, [{ path: nestedParsed.basePath, value: newArray }];
|
|
417
|
+
} else
|
|
418
|
+
return convertObjectArraySetOperation(
|
|
419
|
+
{ path: newPath, value: op.value },
|
|
420
|
+
nestedParsed.basePath,
|
|
421
|
+
nestedParsed.index,
|
|
422
|
+
nestedParsed.restPath,
|
|
423
|
+
typeSchema,
|
|
424
|
+
allSchemas,
|
|
425
|
+
document
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return [
|
|
430
|
+
{
|
|
431
|
+
path: newPath,
|
|
432
|
+
value: op.value
|
|
433
|
+
}
|
|
434
|
+
];
|
|
435
|
+
}
|
|
436
|
+
function filterInvalidArrayItems(ops, errors) {
|
|
437
|
+
const perItemErrors = [], structuralErrors = [];
|
|
438
|
+
for (const error of errors)
|
|
439
|
+
error.itemIndex !== void 0 && error.arrayPath !== void 0 ? perItemErrors.push(error) : structuralErrors.push(error);
|
|
440
|
+
if (structuralErrors.length > 0)
|
|
441
|
+
return { hasStructuralErrors: !0, skippedItems: [] };
|
|
442
|
+
if (perItemErrors.length === 0)
|
|
443
|
+
return { hasStructuralErrors: !1, skippedItems: [] };
|
|
444
|
+
const invalidByPath = /* @__PURE__ */ new Map();
|
|
445
|
+
for (const error of perItemErrors) {
|
|
446
|
+
const existing = invalidByPath.get(error.arrayPath) ?? /* @__PURE__ */ new Set();
|
|
447
|
+
existing.add(error.itemIndex), invalidByPath.set(error.arrayPath, existing);
|
|
448
|
+
}
|
|
449
|
+
const skippedItems = [], seen = /* @__PURE__ */ new Set();
|
|
450
|
+
for (const error of perItemErrors) {
|
|
451
|
+
const key = `${error.arrayPath}:${error.itemIndex}`;
|
|
452
|
+
seen.has(key) || (seen.add(key), skippedItems.push({
|
|
453
|
+
arrayPath: error.arrayPath,
|
|
454
|
+
index: error.itemIndex,
|
|
455
|
+
type: void 0,
|
|
456
|
+
// filled below from the actual item
|
|
457
|
+
reason: error.message
|
|
458
|
+
}));
|
|
459
|
+
}
|
|
460
|
+
const cleanedSet = ops.set?.map((op) => {
|
|
461
|
+
const invalidIndices = invalidByPath.get(op.path);
|
|
462
|
+
if (!invalidIndices || !Array.isArray(op.value)) return op;
|
|
463
|
+
for (const idx of invalidIndices) {
|
|
464
|
+
const item = op.value[idx], skipped = skippedItems.find((s) => s.arrayPath === op.path && s.index === idx);
|
|
465
|
+
skipped && item && (skipped.type = item._type);
|
|
466
|
+
}
|
|
467
|
+
const filtered = op.value.filter((_, idx) => !invalidIndices.has(idx));
|
|
468
|
+
return { ...op, value: filtered };
|
|
469
|
+
}), cleanedAppend = ops.append?.map((op) => {
|
|
470
|
+
const invalidIndices = invalidByPath.get(op.path);
|
|
471
|
+
if (!invalidIndices) return op;
|
|
472
|
+
for (const idx of invalidIndices) {
|
|
473
|
+
const item = op.items[idx], skipped = skippedItems.find((s) => s.arrayPath === op.path && s.index === idx);
|
|
474
|
+
skipped && item && (skipped.type = item._type);
|
|
475
|
+
}
|
|
476
|
+
const filtered = op.items.filter((_, idx) => !invalidIndices.has(idx));
|
|
477
|
+
return { ...op, items: filtered };
|
|
478
|
+
}), filterInsert = (insertOps) => insertOps?.map((op) => {
|
|
479
|
+
const invalidIndices = invalidByPath.get(op.arrayPath);
|
|
480
|
+
if (!invalidIndices) return op;
|
|
481
|
+
for (const idx of invalidIndices) {
|
|
482
|
+
const item = op.items[idx], skipped = skippedItems.find((s) => s.arrayPath === op.arrayPath && s.index === idx);
|
|
483
|
+
skipped && item && (skipped.type = item._type);
|
|
484
|
+
}
|
|
485
|
+
const filtered = op.items.filter((_, idx) => !invalidIndices.has(idx));
|
|
486
|
+
return { ...op, items: filtered };
|
|
487
|
+
});
|
|
488
|
+
return {
|
|
489
|
+
hasStructuralErrors: !1,
|
|
490
|
+
skippedItems,
|
|
491
|
+
set: cleanedSet,
|
|
492
|
+
append: cleanedAppend,
|
|
493
|
+
insertBefore: filterInsert(ops.insertBefore),
|
|
494
|
+
insertAfter: filterInsert(ops.insertAfter)
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function formatPartialWriteError(skippedItems, totalItemsByPath) {
|
|
498
|
+
const lines = [], byPath = /* @__PURE__ */ new Map();
|
|
499
|
+
for (const item of skippedItems) {
|
|
500
|
+
const existing = byPath.get(item.arrayPath) ?? [];
|
|
501
|
+
existing.push(item), byPath.set(item.arrayPath, existing);
|
|
502
|
+
}
|
|
503
|
+
for (const [arrayPath, items] of byPath) {
|
|
504
|
+
const total = totalItemsByPath.get(arrayPath) ?? 0, written = total - items.length, details = items.map((s) => `${s.type ?? "unknown"} at index ${s.index} (${s.reason})`).join("; ");
|
|
505
|
+
lines.push(
|
|
506
|
+
`Partial write to "${arrayPath}": wrote ${written}/${total} items. Skipped ${items.length}: ${details}.`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
return lines.push("Read the document and use insertAfter to add the missing items."), lines.join(`
|
|
510
|
+
`);
|
|
511
|
+
}
|
|
512
|
+
function generateKey() {
|
|
513
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
514
|
+
let key = "";
|
|
515
|
+
for (let i = 0; i < 12; i++)
|
|
516
|
+
key += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
517
|
+
return key;
|
|
518
|
+
}
|
|
519
|
+
function addMissingKeys(value, options) {
|
|
520
|
+
if (value == null)
|
|
521
|
+
return value;
|
|
522
|
+
if (Array.isArray(value))
|
|
523
|
+
return value.map((item) => {
|
|
524
|
+
if (item && typeof item == "object" && !Array.isArray(item)) {
|
|
525
|
+
const obj = item, processedObj = {};
|
|
526
|
+
for (const [key, val] of Object.entries(obj))
|
|
527
|
+
processedObj[key] = addMissingKeys(val);
|
|
528
|
+
return processedObj._key ? processedObj : {
|
|
529
|
+
_key: generateKey(),
|
|
530
|
+
...processedObj
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
return addMissingKeys(item);
|
|
534
|
+
});
|
|
535
|
+
if (typeof value == "object") {
|
|
536
|
+
const obj = value, result = {};
|
|
537
|
+
for (const [key, val] of Object.entries(obj))
|
|
538
|
+
result[key] = addMissingKeys(val);
|
|
539
|
+
return options?.ensureRootKey && !result._key ? {
|
|
540
|
+
_key: generateKey(),
|
|
541
|
+
...result
|
|
542
|
+
} : result;
|
|
543
|
+
}
|
|
544
|
+
return value;
|
|
545
|
+
}
|
|
546
|
+
function collectKeys(value, currentPath, keys) {
|
|
547
|
+
if (value != null) {
|
|
548
|
+
if (Array.isArray(value))
|
|
549
|
+
value.forEach((item, index) => {
|
|
550
|
+
if (item && typeof item == "object" && !Array.isArray(item)) {
|
|
551
|
+
const obj = item, itemPath = currentPath ? `${currentPath}.${index}` : `${index}`;
|
|
552
|
+
if (typeof obj._key == "string") {
|
|
553
|
+
const existing = keys.get(obj._key) || [];
|
|
554
|
+
existing.push(itemPath), keys.set(obj._key, existing);
|
|
555
|
+
}
|
|
556
|
+
for (const [key, val] of Object.entries(obj))
|
|
557
|
+
key !== "_key" && collectKeys(val, `${itemPath}.${key}`, keys);
|
|
558
|
+
} else {
|
|
559
|
+
const itemPath = currentPath ? `${currentPath}.${index}` : `${index}`;
|
|
560
|
+
collectKeys(item, itemPath, keys);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
else if (typeof value == "object") {
|
|
564
|
+
const obj = value;
|
|
565
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
566
|
+
const nestedPath = currentPath ? `${currentPath}.${key}` : key;
|
|
567
|
+
collectKeys(val, nestedPath, keys);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function regenerateDuplicatesInternal(value, currentPath, duplicateKeys, seenKeys, keyMapping) {
|
|
573
|
+
if (value == null)
|
|
574
|
+
return value;
|
|
575
|
+
if (Array.isArray(value))
|
|
576
|
+
return value.map((item, index) => {
|
|
577
|
+
if (item && typeof item == "object" && !Array.isArray(item)) {
|
|
578
|
+
const obj = item, itemPath2 = currentPath ? `${currentPath}.${index}` : `${index}`, oldKey = obj._key;
|
|
579
|
+
let newKey = oldKey;
|
|
580
|
+
if (oldKey && duplicateKeys.has(oldKey))
|
|
581
|
+
if (seenKeys.has(oldKey)) {
|
|
582
|
+
newKey = generateKey();
|
|
583
|
+
const oldKeyPath = `${itemPath2.split(".").slice(0, -1).join(".")}[_key=="${oldKey}"]`.replace(/^\./, ""), newKeyPath = `${itemPath2.split(".").slice(0, -1).join(".")}[_key=="${newKey}"]`.replace(/^\./, "");
|
|
584
|
+
keyMapping.set(itemPath2, newKeyPath || `[_key=="${newKey}"]`), oldKeyPath && keyMapping.set(oldKeyPath, newKeyPath || `[_key=="${newKey}"]`);
|
|
585
|
+
} else
|
|
586
|
+
seenKeys.add(oldKey);
|
|
587
|
+
const processedObj = {};
|
|
588
|
+
for (const [key, val] of Object.entries(obj))
|
|
589
|
+
key !== "_key" && (processedObj[key] = regenerateDuplicatesInternal(
|
|
590
|
+
val,
|
|
591
|
+
`${itemPath2}.${key}`,
|
|
592
|
+
duplicateKeys,
|
|
593
|
+
seenKeys,
|
|
594
|
+
keyMapping
|
|
595
|
+
));
|
|
596
|
+
return {
|
|
597
|
+
_key: newKey,
|
|
598
|
+
...processedObj
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
const itemPath = currentPath ? `${currentPath}.${index}` : `${index}`;
|
|
602
|
+
return regenerateDuplicatesInternal(item, itemPath, duplicateKeys, seenKeys, keyMapping);
|
|
603
|
+
});
|
|
604
|
+
if (typeof value == "object") {
|
|
605
|
+
const obj = value, result = {};
|
|
606
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
607
|
+
const nestedPath = currentPath ? `${currentPath}.${key}` : key;
|
|
608
|
+
result[key] = regenerateDuplicatesInternal(
|
|
609
|
+
val,
|
|
610
|
+
nestedPath,
|
|
611
|
+
duplicateKeys,
|
|
612
|
+
seenKeys,
|
|
613
|
+
keyMapping
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
return value;
|
|
619
|
+
}
|
|
620
|
+
function regenerateDuplicateKeys(value) {
|
|
621
|
+
const keyPaths = /* @__PURE__ */ new Map();
|
|
622
|
+
collectKeys(value, "", keyPaths);
|
|
623
|
+
const duplicateKeys = /* @__PURE__ */ new Set();
|
|
624
|
+
let duplicatesFound = 0;
|
|
625
|
+
for (const [key, paths] of keyPaths)
|
|
626
|
+
paths.length > 1 && (duplicateKeys.add(key), duplicatesFound += paths.length - 1);
|
|
627
|
+
if (duplicateKeys.size === 0)
|
|
628
|
+
return {
|
|
629
|
+
value,
|
|
630
|
+
keyMapping: /* @__PURE__ */ new Map(),
|
|
631
|
+
duplicatesFound: 0
|
|
632
|
+
};
|
|
633
|
+
const keyMapping = /* @__PURE__ */ new Map();
|
|
634
|
+
return {
|
|
635
|
+
value: regenerateDuplicatesInternal(
|
|
636
|
+
value,
|
|
637
|
+
"",
|
|
638
|
+
duplicateKeys,
|
|
639
|
+
/* @__PURE__ */ new Set(),
|
|
640
|
+
keyMapping
|
|
641
|
+
),
|
|
642
|
+
keyMapping,
|
|
643
|
+
duplicatesFound
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function normalizeKeyQuotes(path) {
|
|
647
|
+
return path.replace(/\[_key=='([^']+)'\]/g, '[_key=="$1"]');
|
|
648
|
+
}
|
|
649
|
+
function normalizeArrayIndices(path) {
|
|
650
|
+
return path.replace(/\[(\d+)\]/g, ".$1");
|
|
651
|
+
}
|
|
652
|
+
function applySingleTransformation(path, keyMapping) {
|
|
653
|
+
if (keyMapping.has(path))
|
|
654
|
+
return { path: keyMapping.get(path), transformed: !0 };
|
|
655
|
+
let longestMatch = "", longestMatchValue = "";
|
|
656
|
+
for (const [oldPath, newPath] of keyMapping.entries())
|
|
657
|
+
(path === oldPath || path.startsWith(`${oldPath}.`) || path.startsWith(`${oldPath}[`)) && oldPath.length > longestMatch.length && (longestMatch = oldPath, longestMatchValue = newPath);
|
|
658
|
+
if (longestMatch) {
|
|
659
|
+
const remainder = path.slice(longestMatch.length);
|
|
660
|
+
return { path: longestMatchValue + remainder, transformed: !0 };
|
|
661
|
+
}
|
|
662
|
+
return { path, transformed: !1 };
|
|
663
|
+
}
|
|
664
|
+
function transformPathWithKeyMapping(path, keyMapping) {
|
|
665
|
+
if (!keyMapping || keyMapping.size === 0)
|
|
666
|
+
return path;
|
|
667
|
+
let currentPath = normalizeArrayIndices(path), iterations = 0;
|
|
668
|
+
const maxIterations = 10;
|
|
669
|
+
for (; iterations < maxIterations; ) {
|
|
670
|
+
const result = applySingleTransformation(currentPath, keyMapping);
|
|
671
|
+
if (!result.transformed)
|
|
672
|
+
break;
|
|
673
|
+
currentPath = result.path, iterations++;
|
|
674
|
+
}
|
|
675
|
+
return currentPath;
|
|
676
|
+
}
|
|
677
|
+
function mapKeyThroughKeyMapping(key, keyMapping) {
|
|
678
|
+
return !keyMapping || typeof keyMapping != "object" ? key : keyMapping[key] ?? key;
|
|
679
|
+
}
|
|
680
|
+
function resolveTopLevelFieldSchema(path, typeSchema, allSchemas) {
|
|
681
|
+
const topField = path.split(".")[0].split("[")[0], field = typeSchema.fields?.find((f) => f.name === topField);
|
|
682
|
+
return field ? allSchemas.find((s) => s.name === field.type) ?? field : void 0;
|
|
683
|
+
}
|
|
684
|
+
function resolveLanguageIds(fieldSchema, allSchemas) {
|
|
685
|
+
let languages = fieldSchema.options?.languages;
|
|
686
|
+
return languages?.length || (languages = allSchemas.find((s) => s.name === fieldSchema.type)?.options?.languages), languages?.length ? new Set(languages.map((l) => l.id)) : /* @__PURE__ */ new Set();
|
|
687
|
+
}
|
|
688
|
+
function isV4PatternItem(item, languageIds) {
|
|
689
|
+
const key = item._key;
|
|
690
|
+
return typeof key == "string" && languageIds.has(key) && !("language" in item);
|
|
691
|
+
}
|
|
692
|
+
function isV5PatternItem(item) {
|
|
693
|
+
return "language" in item && typeof item.language == "string";
|
|
694
|
+
}
|
|
695
|
+
function normalizeItemV4ToV5(item) {
|
|
696
|
+
const languageCode = item._key;
|
|
697
|
+
return { ...item, _key: generateKey(), language: languageCode };
|
|
698
|
+
}
|
|
699
|
+
function normalizeItemV5ToV4(item) {
|
|
700
|
+
const { language: languageCode, ...rest } = item;
|
|
701
|
+
return { ...rest, _key: languageCode };
|
|
702
|
+
}
|
|
703
|
+
function normalizeItems(items, fieldSchema, allSchemas) {
|
|
704
|
+
const isV5 = isV5InternationalizedArray(fieldSchema, allSchemas), languageIds = resolveLanguageIds(fieldSchema, allSchemas);
|
|
705
|
+
return languageIds.size === 0 ? items : items.map((item) => {
|
|
706
|
+
if (typeof item != "object" || item === null || Array.isArray(item)) return item;
|
|
707
|
+
const obj = item;
|
|
708
|
+
if (isV5) {
|
|
709
|
+
if (isV4PatternItem(obj, languageIds))
|
|
710
|
+
return normalizeItemV4ToV5(obj);
|
|
711
|
+
} else if (isV5PatternItem(obj))
|
|
712
|
+
return normalizeItemV5ToV4(obj);
|
|
713
|
+
return item;
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
function normalizeInternationalizedArrays(operations, options) {
|
|
717
|
+
const { typeSchema, allSchemas } = options;
|
|
718
|
+
return {
|
|
719
|
+
set: operations.set?.map((op) => {
|
|
720
|
+
const fieldSchema = resolveTopLevelFieldSchema(op.path, typeSchema, allSchemas);
|
|
721
|
+
return !fieldSchema || !isInternationalizedArrayField(fieldSchema) || !Array.isArray(op.value) ? op : { ...op, value: normalizeItems(op.value, fieldSchema, allSchemas) };
|
|
722
|
+
}),
|
|
723
|
+
unset: operations.unset,
|
|
724
|
+
append: operations.append?.map((op) => {
|
|
725
|
+
const fieldSchema = resolveTopLevelFieldSchema(op.path, typeSchema, allSchemas);
|
|
726
|
+
return !fieldSchema || !isInternationalizedArrayField(fieldSchema) ? op : {
|
|
727
|
+
...op,
|
|
728
|
+
items: normalizeItems(op.items, fieldSchema, allSchemas)
|
|
729
|
+
};
|
|
730
|
+
}),
|
|
731
|
+
insertBefore: operations.insertBefore?.map((op) => {
|
|
732
|
+
const fieldSchema = resolveTopLevelFieldSchema(op.arrayPath, typeSchema, allSchemas);
|
|
733
|
+
return !fieldSchema || !isInternationalizedArrayField(fieldSchema) ? op : { ...op, items: normalizeItems(op.items, fieldSchema, allSchemas) };
|
|
734
|
+
}),
|
|
735
|
+
insertAfter: operations.insertAfter?.map((op) => {
|
|
736
|
+
const fieldSchema = resolveTopLevelFieldSchema(op.arrayPath, typeSchema, allSchemas);
|
|
737
|
+
return !fieldSchema || !isInternationalizedArrayField(fieldSchema) ? op : { ...op, items: normalizeItems(op.items, fieldSchema, allSchemas) };
|
|
738
|
+
})
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function isReferenceObject(value) {
|
|
742
|
+
return value !== null && typeof value == "object" && "_ref" in value && typeof value._ref == "string" && "_type" in value && typeof value._type == "string";
|
|
743
|
+
}
|
|
744
|
+
function getReferenceFieldDefinition(fieldPath, typeSchema, fullSchema) {
|
|
745
|
+
return resolveFieldSchema(fieldPath, typeSchema, fullSchema);
|
|
746
|
+
}
|
|
747
|
+
function getArrayItemSchema(arrayFieldPath, typeSchema, fullSchema) {
|
|
748
|
+
const field = getReferenceFieldDefinition(arrayFieldPath, typeSchema, fullSchema);
|
|
749
|
+
return !field || field.type !== "array" || !field.of || field.of.length === 0 || field.of.length > 1 ? null : field.of[0];
|
|
750
|
+
}
|
|
751
|
+
function isWeakReferenceField(fieldDefinition) {
|
|
752
|
+
return fieldDefinition.type === "reference" && fieldDefinition.weak === !0 ? !0 : fieldDefinition.type === "array" && fieldDefinition.of ? fieldDefinition.of.some((item) => item.type === "reference" && item.weak === !0) : !1;
|
|
753
|
+
}
|
|
754
|
+
const documentCache = /* @__PURE__ */ new Map();
|
|
755
|
+
function clearDocumentCache() {
|
|
756
|
+
documentCache.clear();
|
|
757
|
+
}
|
|
758
|
+
async function lookupDocument(client, docId) {
|
|
759
|
+
if (documentCache.has(docId))
|
|
760
|
+
return documentCache.get(docId);
|
|
761
|
+
try {
|
|
762
|
+
const doc = await client.getDocument(docId), result = doc ? { _type: doc._type } : null;
|
|
763
|
+
return documentCache.set(docId, result), result;
|
|
764
|
+
} catch {
|
|
765
|
+
return documentCache.set(docId, null), null;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
async function normalizeReferenceObject(client, ref, fieldPath, typeSchema, fullSchema, targetDocumentId, validationErrors, applyStrengthenOnPublish, parentObjectType) {
|
|
769
|
+
let fieldDefinition;
|
|
770
|
+
if (typeSchema.type === "reference" ? fieldDefinition = typeSchema : fieldDefinition = getReferenceFieldDefinition(
|
|
771
|
+
fieldPath,
|
|
772
|
+
typeSchema,
|
|
773
|
+
fullSchema
|
|
774
|
+
), !fieldDefinition && parentObjectType) {
|
|
775
|
+
const globalType = fullSchema.find((s) => s.name === parentObjectType);
|
|
776
|
+
if (globalType?.fields) {
|
|
777
|
+
const fieldName = fieldPath.split(".").pop()?.replace(/\[\d+\]$/, "");
|
|
778
|
+
if (fieldName) {
|
|
779
|
+
const field = globalType.fields.find((f) => f.name === fieldName);
|
|
780
|
+
field && (fieldDefinition = field);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (fieldDefinition?.type === "array" && fieldDefinition.of) {
|
|
785
|
+
const referenceSchemas = fieldDefinition.of.filter((item) => item.type === "reference");
|
|
786
|
+
referenceSchemas.length === 1 && (fieldDefinition = referenceSchemas[0]);
|
|
787
|
+
}
|
|
788
|
+
const isSchemaWeak = fieldDefinition ? isWeakReferenceField(fieldDefinition) : !1, isTargetPublished = isPublishedId(targetDocumentId), originalRefId = ref._ref;
|
|
789
|
+
if (ref._type === "globalDocumentReference")
|
|
790
|
+
return { ...ref, _weak: !0 };
|
|
791
|
+
if (ref._weak) {
|
|
792
|
+
const publishedId2 = isVersionId(originalRefId) || isDraftId(originalRefId) ? getPublishedId(originalRefId) : originalRefId;
|
|
793
|
+
if (isSchemaWeak || ref._strengthenOnPublish)
|
|
794
|
+
return { ...ref, _ref: publishedId2 };
|
|
795
|
+
if (applyStrengthenOnPublish && !isTargetPublished) {
|
|
796
|
+
const allPossibleTypes2 = [];
|
|
797
|
+
fieldDefinition?.to && Array.isArray(fieldDefinition.to) && allPossibleTypes2.push(...fieldDefinition.to.map((t) => t.type).filter(Boolean));
|
|
798
|
+
const targetType = allPossibleTypes2.length === 1 ? allPossibleTypes2[0] : ref._type !== "reference" ? ref._type : allPossibleTypes2[0];
|
|
799
|
+
if (targetType)
|
|
800
|
+
return {
|
|
801
|
+
...ref,
|
|
802
|
+
_ref: publishedId2,
|
|
803
|
+
_strengthenOnPublish: { type: targetType, template: { id: targetType } }
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
return { ...ref, _ref: publishedId2 };
|
|
807
|
+
}
|
|
808
|
+
let publishedId, needsWeakAndStrengthen = !1, documentType = null;
|
|
809
|
+
if (isVersionId(originalRefId)) {
|
|
810
|
+
if (isTargetPublished)
|
|
811
|
+
return validationErrors.push(
|
|
812
|
+
createError(
|
|
813
|
+
fieldPath,
|
|
814
|
+
"INVALID_REFERENCE",
|
|
815
|
+
"Cannot reference versioned document from published document",
|
|
816
|
+
`"${originalRefId}" (versioned)`,
|
|
817
|
+
"Published document reference",
|
|
818
|
+
"Use a published document ID instead of a versioned one"
|
|
819
|
+
)
|
|
820
|
+
), null;
|
|
821
|
+
const versionedDoc = await lookupDocument(client, originalRefId);
|
|
822
|
+
if (!versionedDoc)
|
|
823
|
+
return validationErrors.push(
|
|
824
|
+
createError(
|
|
825
|
+
fieldPath,
|
|
826
|
+
"REFERENCE_NOT_FOUND",
|
|
827
|
+
"Referenced versioned document does not exist",
|
|
828
|
+
`"${originalRefId}"`,
|
|
829
|
+
"Valid document ID",
|
|
830
|
+
"Provide a document ID that exists in the dataset"
|
|
831
|
+
)
|
|
832
|
+
), null;
|
|
833
|
+
documentType = versionedDoc._type, publishedId = getPublishedId(originalRefId), needsWeakAndStrengthen = !0;
|
|
834
|
+
} else if (isDraftId(originalRefId)) {
|
|
835
|
+
if (isTargetPublished)
|
|
836
|
+
return validationErrors.push(
|
|
837
|
+
createError(
|
|
838
|
+
fieldPath,
|
|
839
|
+
"INVALID_REFERENCE",
|
|
840
|
+
"Cannot reference draft document from published document",
|
|
841
|
+
`"${originalRefId}" (draft)`,
|
|
842
|
+
"Published document reference",
|
|
843
|
+
"Use a published document ID instead of a draft one"
|
|
844
|
+
)
|
|
845
|
+
), null;
|
|
846
|
+
const draftDoc = await lookupDocument(client, originalRefId);
|
|
847
|
+
if (!draftDoc)
|
|
848
|
+
return validationErrors.push(
|
|
849
|
+
createError(
|
|
850
|
+
fieldPath,
|
|
851
|
+
"REFERENCE_NOT_FOUND",
|
|
852
|
+
"Referenced draft document does not exist",
|
|
853
|
+
`"${originalRefId}"`,
|
|
854
|
+
"Valid document ID",
|
|
855
|
+
"Provide a document ID that exists in the dataset"
|
|
856
|
+
)
|
|
857
|
+
), null;
|
|
858
|
+
documentType = draftDoc._type, publishedId = getPublishedId(originalRefId), needsWeakAndStrengthen = !0;
|
|
859
|
+
} else {
|
|
860
|
+
publishedId = originalRefId;
|
|
861
|
+
const publishedDoc = await lookupDocument(client, publishedId);
|
|
862
|
+
if (!publishedDoc)
|
|
863
|
+
return validationErrors.push(
|
|
864
|
+
createError(
|
|
865
|
+
fieldPath,
|
|
866
|
+
"REFERENCE_NOT_FOUND",
|
|
867
|
+
"Referenced document does not exist",
|
|
868
|
+
`"${originalRefId}"`,
|
|
869
|
+
"Valid document ID",
|
|
870
|
+
"Provide a document ID that exists in the dataset"
|
|
871
|
+
)
|
|
872
|
+
), null;
|
|
873
|
+
documentType = publishedDoc._type, needsWeakAndStrengthen = !1;
|
|
874
|
+
}
|
|
875
|
+
const normalized = {
|
|
876
|
+
...ref
|
|
877
|
+
};
|
|
878
|
+
if (isSchemaWeak && (normalized._weak = !0), !applyStrengthenOnPublish)
|
|
879
|
+
return normalized;
|
|
880
|
+
const allPossibleTypes = [];
|
|
881
|
+
if (fieldDefinition?.to && Array.isArray(fieldDefinition.to) && allPossibleTypes.push(...fieldDefinition.to.map((t) => t.type).filter(Boolean)), allPossibleTypes.length > 0 && documentType && !allPossibleTypes.includes(documentType))
|
|
882
|
+
return validationErrors.push(
|
|
883
|
+
createError(
|
|
884
|
+
fieldPath,
|
|
885
|
+
"REFERENCE_WRONG_TYPE",
|
|
886
|
+
"Reference points to wrong document type",
|
|
887
|
+
`"${originalRefId}" (type: ${documentType})`,
|
|
888
|
+
`Reference to: ${allPossibleTypes.join(", ")}`,
|
|
889
|
+
`Change the reference to point to a document of type ${allPossibleTypes.join(" or ")}`
|
|
890
|
+
)
|
|
891
|
+
), null;
|
|
892
|
+
if (!isSchemaWeak && needsWeakAndStrengthen && !isTargetPublished && documentType) {
|
|
893
|
+
let targetType = documentType;
|
|
894
|
+
allPossibleTypes.length === 1 && (targetType = allPossibleTypes[0]), normalized._ref = publishedId, normalized._weak = !0, normalized._strengthenOnPublish = {
|
|
895
|
+
type: targetType,
|
|
896
|
+
template: {
|
|
897
|
+
id: targetType
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
return normalized;
|
|
902
|
+
}
|
|
903
|
+
async function traverseAndNormalize(client, value, currentPath, typeSchema, fullSchema, targetDocumentId, validationErrors, applyStrengthenOnPublish, parentObjectType) {
|
|
904
|
+
if (value == null)
|
|
905
|
+
return value;
|
|
906
|
+
if (Array.isArray(value))
|
|
907
|
+
return (await Promise.all(
|
|
908
|
+
value.map(async (item, index) => {
|
|
909
|
+
const itemPath = `${currentPath}[${index}]`;
|
|
910
|
+
return await traverseAndNormalize(
|
|
911
|
+
client,
|
|
912
|
+
item,
|
|
913
|
+
itemPath,
|
|
914
|
+
typeSchema,
|
|
915
|
+
fullSchema,
|
|
916
|
+
targetDocumentId,
|
|
917
|
+
validationErrors,
|
|
918
|
+
applyStrengthenOnPublish,
|
|
919
|
+
parentObjectType
|
|
920
|
+
);
|
|
921
|
+
})
|
|
922
|
+
)).filter((item) => item !== null);
|
|
923
|
+
if (isReferenceObject(value))
|
|
924
|
+
return await normalizeReferenceObject(
|
|
925
|
+
client,
|
|
926
|
+
value,
|
|
927
|
+
currentPath,
|
|
928
|
+
typeSchema,
|
|
929
|
+
fullSchema,
|
|
930
|
+
targetDocumentId,
|
|
931
|
+
validationErrors,
|
|
932
|
+
applyStrengthenOnPublish,
|
|
933
|
+
parentObjectType
|
|
934
|
+
);
|
|
935
|
+
if (typeof value == "object") {
|
|
936
|
+
const obj = value, normalized = {}, thisObjectType = typeof obj._type == "string" ? obj._type : void 0;
|
|
937
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
938
|
+
if (key.startsWith("_")) {
|
|
939
|
+
normalized[key] = val;
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
const fieldPath = currentPath ? `${currentPath}.${key}` : key, result = await traverseAndNormalize(
|
|
943
|
+
client,
|
|
944
|
+
val,
|
|
945
|
+
fieldPath,
|
|
946
|
+
typeSchema,
|
|
947
|
+
fullSchema,
|
|
948
|
+
targetDocumentId,
|
|
949
|
+
validationErrors,
|
|
950
|
+
applyStrengthenOnPublish,
|
|
951
|
+
thisObjectType
|
|
952
|
+
);
|
|
953
|
+
result !== null && (normalized[key] = result);
|
|
954
|
+
}
|
|
955
|
+
return normalized;
|
|
956
|
+
}
|
|
957
|
+
return value;
|
|
958
|
+
}
|
|
959
|
+
async function normalizeDocumentReferences(client, document, schema, applyStrengthenOnPublish = !0) {
|
|
960
|
+
if (clearDocumentCache(), !document || !schema || schema.length === 0)
|
|
961
|
+
return {
|
|
962
|
+
normalized: document,
|
|
963
|
+
validationErrors: []
|
|
964
|
+
};
|
|
965
|
+
const typeSchema = schema.find((t) => t.name === document._type);
|
|
966
|
+
if (!typeSchema)
|
|
967
|
+
return {
|
|
968
|
+
normalized: document,
|
|
969
|
+
validationErrors: []
|
|
970
|
+
};
|
|
971
|
+
const validationErrors = [];
|
|
972
|
+
return {
|
|
973
|
+
normalized: await traverseAndNormalize(
|
|
974
|
+
client,
|
|
975
|
+
document,
|
|
976
|
+
"",
|
|
977
|
+
typeSchema,
|
|
978
|
+
schema,
|
|
979
|
+
document._id,
|
|
980
|
+
validationErrors,
|
|
981
|
+
applyStrengthenOnPublish
|
|
982
|
+
),
|
|
983
|
+
validationErrors
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
function getLastItemKey(arrayValue, excludeKeys) {
|
|
987
|
+
if (!(!Array.isArray(arrayValue) || arrayValue.length === 0))
|
|
988
|
+
for (let i = arrayValue.length - 1; i >= 0; i--) {
|
|
989
|
+
const item = arrayValue[i];
|
|
990
|
+
if (item && typeof item == "object" && "_key" in item) {
|
|
991
|
+
const key = item._key;
|
|
992
|
+
if (!excludeKeys || !excludeKeys.has(key))
|
|
993
|
+
return key;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function extractUnsetKeysForArray(unsetPaths, arrayPath) {
|
|
998
|
+
const keys = /* @__PURE__ */ new Set(), regex = new RegExp(`^${escapeRegex(arrayPath)}\\[_key==["']([^"']+)["']\\]$`);
|
|
999
|
+
for (const path of unsetPaths) {
|
|
1000
|
+
const match = path.match(regex);
|
|
1001
|
+
match && keys.add(match[1]);
|
|
1002
|
+
}
|
|
1003
|
+
return keys;
|
|
1004
|
+
}
|
|
1005
|
+
function escapeRegex(str) {
|
|
1006
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1007
|
+
}
|
|
1008
|
+
function collectExistingKeys(arrayValue) {
|
|
1009
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1010
|
+
if (!Array.isArray(arrayValue)) return keys;
|
|
1011
|
+
for (const item of arrayValue)
|
|
1012
|
+
if (item && typeof item == "object" && "_key" in item) {
|
|
1013
|
+
const key = item._key;
|
|
1014
|
+
typeof key == "string" && keys.add(key);
|
|
1015
|
+
}
|
|
1016
|
+
return keys;
|
|
1017
|
+
}
|
|
1018
|
+
async function normalizeArrayItems(client, items, arrayPath, typeSchema, fullSchema, targetDocumentId, validationErrors, applyStrengthenOnPublish) {
|
|
1019
|
+
const arrayItemSchema = getArrayItemSchema(arrayPath, typeSchema, fullSchema), arrayFieldDef = getReferenceFieldDefinition(arrayPath, typeSchema, fullSchema), normalizedItems = [];
|
|
1020
|
+
for (const item of items) {
|
|
1021
|
+
let schemaForItem = arrayItemSchema;
|
|
1022
|
+
if (!schemaForItem && arrayFieldDef?.of) {
|
|
1023
|
+
const itemRecord = item;
|
|
1024
|
+
if (typeof itemRecord._ref == "string") {
|
|
1025
|
+
const matchingRefSchema = arrayFieldDef.of.find((s) => s.type !== "reference" ? !1 : s.name === itemRecord._type || s.type === itemRecord._type && !s.name);
|
|
1026
|
+
if (matchingRefSchema)
|
|
1027
|
+
schemaForItem = matchingRefSchema;
|
|
1028
|
+
else {
|
|
1029
|
+
const refSchemas = arrayFieldDef.of.filter((s) => s.type === "reference");
|
|
1030
|
+
if (refSchemas.length > 0) {
|
|
1031
|
+
const allWeak = refSchemas.every((s) => s.weak === !0), allStrong = refSchemas.every((s) => s.weak !== !0);
|
|
1032
|
+
if (allWeak || allStrong)
|
|
1033
|
+
schemaForItem = refSchemas[0];
|
|
1034
|
+
else {
|
|
1035
|
+
normalizedItems.push(item);
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
} else
|
|
1041
|
+
schemaForItem = arrayFieldDef.of.find((ofSchema) => ofSchema.name === itemRecord._type || ofSchema.type === itemRecord._type) || null;
|
|
1042
|
+
}
|
|
1043
|
+
const normalizedItem = await traverseAndNormalize(
|
|
1044
|
+
client,
|
|
1045
|
+
item,
|
|
1046
|
+
"",
|
|
1047
|
+
schemaForItem || typeSchema,
|
|
1048
|
+
fullSchema,
|
|
1049
|
+
targetDocumentId,
|
|
1050
|
+
validationErrors,
|
|
1051
|
+
applyStrengthenOnPublish
|
|
1052
|
+
);
|
|
1053
|
+
normalizedItem !== null && normalizedItems.push(normalizedItem);
|
|
1054
|
+
}
|
|
1055
|
+
return normalizedItems;
|
|
1056
|
+
}
|
|
1057
|
+
function collectAllKeys(value, keys) {
|
|
1058
|
+
if (value != null) {
|
|
1059
|
+
if (Array.isArray(value))
|
|
1060
|
+
for (const item of value)
|
|
1061
|
+
if (item && typeof item == "object" && !Array.isArray(item)) {
|
|
1062
|
+
const obj = item;
|
|
1063
|
+
if (typeof obj._key == "string") {
|
|
1064
|
+
const count = keys.get(obj._key) || 0;
|
|
1065
|
+
keys.set(obj._key, count + 1);
|
|
1066
|
+
}
|
|
1067
|
+
for (const val of Object.values(obj))
|
|
1068
|
+
collectAllKeys(val, keys);
|
|
1069
|
+
} else
|
|
1070
|
+
collectAllKeys(item, keys);
|
|
1071
|
+
else if (typeof value == "object")
|
|
1072
|
+
for (const val of Object.values(value))
|
|
1073
|
+
collectAllKeys(val, keys);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
function regenerateDuplicateKeysInValue(value, duplicateKeys, conflictingKeys, seenKeys) {
|
|
1077
|
+
if (value == null) return value;
|
|
1078
|
+
if (Array.isArray(value))
|
|
1079
|
+
return value.map((item) => {
|
|
1080
|
+
if (item && typeof item == "object" && !Array.isArray(item)) {
|
|
1081
|
+
const obj = item, oldKey = obj._key;
|
|
1082
|
+
let newKey = oldKey;
|
|
1083
|
+
oldKey && (conflictingKeys.has(oldKey) || duplicateKeys.has(oldKey) && seenKeys.has(oldKey) ? newKey = generateKey() : seenKeys.add(oldKey));
|
|
1084
|
+
const processedObj = {};
|
|
1085
|
+
for (const [key, val] of Object.entries(obj))
|
|
1086
|
+
key !== "_key" && (processedObj[key] = regenerateDuplicateKeysInValue(
|
|
1087
|
+
val,
|
|
1088
|
+
duplicateKeys,
|
|
1089
|
+
conflictingKeys,
|
|
1090
|
+
seenKeys
|
|
1091
|
+
));
|
|
1092
|
+
return { _key: newKey, ...processedObj };
|
|
1093
|
+
}
|
|
1094
|
+
return regenerateDuplicateKeysInValue(item, duplicateKeys, conflictingKeys, seenKeys);
|
|
1095
|
+
});
|
|
1096
|
+
if (typeof value == "object") {
|
|
1097
|
+
const obj = value, result = {};
|
|
1098
|
+
for (const [key, val] of Object.entries(obj))
|
|
1099
|
+
result[key] = regenerateDuplicateKeysInValue(val, duplicateKeys, conflictingKeys, seenKeys);
|
|
1100
|
+
return result;
|
|
1101
|
+
}
|
|
1102
|
+
return value;
|
|
1103
|
+
}
|
|
1104
|
+
function getParentPaths(path) {
|
|
1105
|
+
const parentPaths = [], arrayMatch = path.match(/\[_key==[^\]]+\]|\[\d+\]/);
|
|
1106
|
+
let pathToAnalyze;
|
|
1107
|
+
arrayMatch && arrayMatch.index !== void 0 ? pathToAnalyze = path.slice(0, arrayMatch.index) : pathToAnalyze = path;
|
|
1108
|
+
const segments = pathToAnalyze.split(".").filter(Boolean);
|
|
1109
|
+
for (let i = 0; i < segments.length - 1; i++)
|
|
1110
|
+
parentPaths.push(segments.slice(0, i + 1).join("."));
|
|
1111
|
+
return parentPaths;
|
|
1112
|
+
}
|
|
1113
|
+
function computeSetIfMissing(paths, typeSchema, fullSchema) {
|
|
1114
|
+
const setIfMissing = {};
|
|
1115
|
+
for (const path of paths) {
|
|
1116
|
+
const parentPaths = getParentPaths(path);
|
|
1117
|
+
for (const parentPath of parentPaths) {
|
|
1118
|
+
if (setIfMissing[parentPath] !== void 0) continue;
|
|
1119
|
+
const fieldSchema = resolveFieldSchema(parentPath, typeSchema, fullSchema);
|
|
1120
|
+
fieldSchema && (fieldSchema.type === "array" ? setIfMissing[parentPath] = [] : (fieldSchema.type === "object" || fieldSchema.fields) && (setIfMissing[parentPath] = fieldSchema.name ? { _type: fieldSchema.name } : {}));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return setIfMissing;
|
|
1124
|
+
}
|
|
1125
|
+
function regenerateDuplicateKeysForAppend(items, existingArrayKeys) {
|
|
1126
|
+
const keyCounts = /* @__PURE__ */ new Map();
|
|
1127
|
+
collectAllKeys(items, keyCounts);
|
|
1128
|
+
const duplicateKeys = /* @__PURE__ */ new Set();
|
|
1129
|
+
for (const [key, count] of keyCounts)
|
|
1130
|
+
count > 1 && duplicateKeys.add(key);
|
|
1131
|
+
return duplicateKeys.size === 0 && existingArrayKeys.size === 0 ? items : regenerateDuplicateKeysInValue(
|
|
1132
|
+
items,
|
|
1133
|
+
duplicateKeys,
|
|
1134
|
+
existingArrayKeys,
|
|
1135
|
+
/* @__PURE__ */ new Set()
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
async function normalizePatchOperations(client, operations, documentType, fullSchema, targetDocumentId, options = {}) {
|
|
1139
|
+
const { applyStrengthenOnPublish = !0, existingDocument } = options;
|
|
1140
|
+
if (clearDocumentCache(), !operations || !documentType || !fullSchema || fullSchema.length === 0)
|
|
1141
|
+
return {
|
|
1142
|
+
normalized: operations,
|
|
1143
|
+
validationErrors: [],
|
|
1144
|
+
setIfMissing: {}
|
|
1145
|
+
};
|
|
1146
|
+
const typeSchema = fullSchema.find((t) => t.name === documentType);
|
|
1147
|
+
if (!typeSchema)
|
|
1148
|
+
return {
|
|
1149
|
+
normalized: operations,
|
|
1150
|
+
validationErrors: [],
|
|
1151
|
+
setIfMissing: {}
|
|
1152
|
+
};
|
|
1153
|
+
const validationErrors = [], normalized = {};
|
|
1154
|
+
for (const [opType, opValue] of Object.entries(operations))
|
|
1155
|
+
if (opType === "set" && Array.isArray(opValue)) {
|
|
1156
|
+
const normalizedSet = [];
|
|
1157
|
+
for (const op of opValue) {
|
|
1158
|
+
const normalizedValue = await traverseAndNormalize(
|
|
1159
|
+
client,
|
|
1160
|
+
op.value,
|
|
1161
|
+
op.path,
|
|
1162
|
+
typeSchema,
|
|
1163
|
+
fullSchema,
|
|
1164
|
+
targetDocumentId,
|
|
1165
|
+
validationErrors,
|
|
1166
|
+
applyStrengthenOnPublish
|
|
1167
|
+
);
|
|
1168
|
+
normalizedValue !== null && normalizedSet.push({
|
|
1169
|
+
path: op.path,
|
|
1170
|
+
value: normalizedValue
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
normalized[opType] = normalizedSet;
|
|
1174
|
+
} else if (opType === "append" && Array.isArray(opValue)) {
|
|
1175
|
+
const normalizedAppend = [], appendAfterKeyForPath = {};
|
|
1176
|
+
for (const op of opValue) {
|
|
1177
|
+
const normalizedItems = Array.isArray(op.items) ? await normalizeArrayItems(
|
|
1178
|
+
client,
|
|
1179
|
+
op.items,
|
|
1180
|
+
op.path,
|
|
1181
|
+
typeSchema,
|
|
1182
|
+
fullSchema,
|
|
1183
|
+
targetDocumentId,
|
|
1184
|
+
validationErrors,
|
|
1185
|
+
applyStrengthenOnPublish
|
|
1186
|
+
) : [];
|
|
1187
|
+
if (normalizedItems.length > 0) {
|
|
1188
|
+
let resolvedPath = op.path, existingArrayKeys = /* @__PURE__ */ new Set();
|
|
1189
|
+
if (existingDocument) {
|
|
1190
|
+
const arrayValue = getValueAtPath(existingDocument, op.path);
|
|
1191
|
+
existingArrayKeys = collectExistingKeys(arrayValue);
|
|
1192
|
+
const unsetKeys = extractUnsetKeysForArray(operations.unset || [], op.path);
|
|
1193
|
+
let appendAfterKey = appendAfterKeyForPath[op.path];
|
|
1194
|
+
appendAfterKey || (appendAfterKey = getLastItemKey(arrayValue, unsetKeys)), appendAfterKey && (resolvedPath = `${op.path}[_key=="${appendAfterKey}"]`);
|
|
1195
|
+
}
|
|
1196
|
+
const deduplicatedItems = regenerateDuplicateKeysForAppend(
|
|
1197
|
+
normalizedItems,
|
|
1198
|
+
existingArrayKeys
|
|
1199
|
+
), lastItem = deduplicatedItems[deduplicatedItems.length - 1];
|
|
1200
|
+
lastItem && typeof lastItem == "object" && "_key" in lastItem && (appendAfterKeyForPath[op.path] = lastItem._key), normalizedAppend.push({
|
|
1201
|
+
path: resolvedPath,
|
|
1202
|
+
items: deduplicatedItems
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
normalized[opType] = normalizedAppend;
|
|
1207
|
+
} else if ((opType === "insertBefore" || opType === "insertAfter") && Array.isArray(opValue)) {
|
|
1208
|
+
const normalizedInserts = [];
|
|
1209
|
+
for (const op of opValue) {
|
|
1210
|
+
const normalizedItems = Array.isArray(op.items) ? await normalizeArrayItems(
|
|
1211
|
+
client,
|
|
1212
|
+
op.items,
|
|
1213
|
+
op.arrayPath,
|
|
1214
|
+
typeSchema,
|
|
1215
|
+
fullSchema,
|
|
1216
|
+
targetDocumentId,
|
|
1217
|
+
validationErrors,
|
|
1218
|
+
applyStrengthenOnPublish
|
|
1219
|
+
) : [];
|
|
1220
|
+
if (normalizedItems.length > 0) {
|
|
1221
|
+
let existingArrayKeys = /* @__PURE__ */ new Set();
|
|
1222
|
+
if (existingDocument) {
|
|
1223
|
+
const arrayValue = getValueAtPath(existingDocument, op.arrayPath);
|
|
1224
|
+
existingArrayKeys = collectExistingKeys(arrayValue);
|
|
1225
|
+
}
|
|
1226
|
+
const deduplicatedItems = regenerateDuplicateKeysForAppend(
|
|
1227
|
+
normalizedItems,
|
|
1228
|
+
existingArrayKeys
|
|
1229
|
+
);
|
|
1230
|
+
normalizedInserts.push({
|
|
1231
|
+
arrayPath: op.arrayPath,
|
|
1232
|
+
key: op.key,
|
|
1233
|
+
items: deduplicatedItems
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
normalized[opType] = normalizedInserts;
|
|
1238
|
+
} else
|
|
1239
|
+
normalized[opType] = opValue;
|
|
1240
|
+
const allPaths = [];
|
|
1241
|
+
if (normalized.set && Array.isArray(normalized.set))
|
|
1242
|
+
for (const op of normalized.set)
|
|
1243
|
+
allPaths.push(op.path);
|
|
1244
|
+
if (normalized.append && Array.isArray(normalized.append))
|
|
1245
|
+
for (const op of normalized.append)
|
|
1246
|
+
allPaths.push(op.path);
|
|
1247
|
+
const setIfMissing = computeSetIfMissing(allPaths, typeSchema, fullSchema);
|
|
1248
|
+
return {
|
|
1249
|
+
normalized,
|
|
1250
|
+
validationErrors,
|
|
1251
|
+
setIfMissing
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
function acceptsFlatPortableText(fieldPath, typeSchema, allSchemas) {
|
|
1255
|
+
const fieldSchema = resolveFieldSchema(fieldPath, typeSchema, allSchemas);
|
|
1256
|
+
return fieldSchema ? arrayAcceptsBlocks(fieldSchema, allSchemas) : !1;
|
|
1257
|
+
}
|
|
1258
|
+
function acceptsOnlyFlatPortableText(fieldPath, typeSchema, allSchemas) {
|
|
1259
|
+
const fieldSchema = resolveFieldSchema(fieldPath, typeSchema, allSchemas);
|
|
1260
|
+
return fieldSchema ? arrayIsBlockOnly(fieldSchema, allSchemas) : !1;
|
|
1261
|
+
}
|
|
1262
|
+
function hasAnyFlatPortableTextField(typeSchema, allSchemas) {
|
|
1263
|
+
return typeSchema.fields ? typeSchema.fields.some((field) => {
|
|
1264
|
+
const resolved = field.type ? allSchemas.find((s) => s.name === field.type) ?? field : field;
|
|
1265
|
+
return arrayAcceptsBlocks(resolved, allSchemas);
|
|
1266
|
+
}) : !1;
|
|
1267
|
+
}
|
|
1268
|
+
function arrayAcceptsBlocks(fieldSchema, allSchemas) {
|
|
1269
|
+
let schema = fieldSchema;
|
|
1270
|
+
if (!schema.of && schema.type) {
|
|
1271
|
+
const resolved = allSchemas.find((s) => s.name === schema.type);
|
|
1272
|
+
resolved && (schema = resolved);
|
|
1273
|
+
}
|
|
1274
|
+
const arrayOf = schema.of;
|
|
1275
|
+
return !arrayOf || arrayOf.length === 0 ? !1 : arrayOf.some((item) => resolveBlockMember(item, allSchemas) !== void 0);
|
|
1276
|
+
}
|
|
1277
|
+
function arrayIsBlockOnly(fieldSchema, allSchemas) {
|
|
1278
|
+
let schema = fieldSchema;
|
|
1279
|
+
if (!schema.of && schema.type) {
|
|
1280
|
+
const resolved = allSchemas.find((s) => s.name === schema.type);
|
|
1281
|
+
resolved && (schema = resolved);
|
|
1282
|
+
}
|
|
1283
|
+
const arrayOf = schema.of;
|
|
1284
|
+
return !arrayOf || arrayOf.length === 0 ? !1 : arrayOf.every((item) => {
|
|
1285
|
+
const block = resolveBlockMember(item, allSchemas);
|
|
1286
|
+
if (!block) return !1;
|
|
1287
|
+
const annotations = block.marks?.annotations;
|
|
1288
|
+
return !annotations || annotations.length === 0;
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
function resolveBlockMember(item, allSchemas) {
|
|
1292
|
+
if (item.type === "block") return item;
|
|
1293
|
+
if (item.type) {
|
|
1294
|
+
const resolved = allSchemas.find((s) => s.name === item.type);
|
|
1295
|
+
if (resolved?.type === "block") return resolved;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
function normalizeOperationPaths(operations) {
|
|
1299
|
+
return {
|
|
1300
|
+
set: operations.set?.map((op) => ({
|
|
1301
|
+
...op,
|
|
1302
|
+
path: normalizeKeyQuotes(op.path)
|
|
1303
|
+
})),
|
|
1304
|
+
unset: operations.unset?.map((path) => normalizeKeyQuotes(path)),
|
|
1305
|
+
append: operations.append?.map((op) => ({
|
|
1306
|
+
...op,
|
|
1307
|
+
path: normalizeKeyQuotes(op.path)
|
|
1308
|
+
})),
|
|
1309
|
+
insertBefore: operations.insertBefore?.map((op) => ({
|
|
1310
|
+
...op,
|
|
1311
|
+
arrayPath: normalizeKeyQuotes(op.arrayPath)
|
|
1312
|
+
})),
|
|
1313
|
+
insertAfter: operations.insertAfter?.map((op) => ({
|
|
1314
|
+
...op,
|
|
1315
|
+
arrayPath: normalizeKeyQuotes(op.arrayPath)
|
|
1316
|
+
}))
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
function applyKeyMapping(operations, keyMapping) {
|
|
1320
|
+
return {
|
|
1321
|
+
set: operations.set?.map((op) => ({
|
|
1322
|
+
...op,
|
|
1323
|
+
path: transformPathWithKeyMapping(op.path, keyMapping)
|
|
1324
|
+
})),
|
|
1325
|
+
unset: operations.unset?.map((path) => transformPathWithKeyMapping(path, keyMapping)),
|
|
1326
|
+
append: operations.append?.map((op) => ({
|
|
1327
|
+
...op,
|
|
1328
|
+
path: transformPathWithKeyMapping(op.path, keyMapping)
|
|
1329
|
+
})),
|
|
1330
|
+
insertBefore: operations.insertBefore?.map((op) => ({
|
|
1331
|
+
...op,
|
|
1332
|
+
arrayPath: transformPathWithKeyMapping(op.arrayPath, keyMapping),
|
|
1333
|
+
key: mapKeyThroughKeyMapping(op.key, keyMapping)
|
|
1334
|
+
})),
|
|
1335
|
+
insertAfter: operations.insertAfter?.map((op) => ({
|
|
1336
|
+
...op,
|
|
1337
|
+
arrayPath: transformPathWithKeyMapping(op.arrayPath, keyMapping),
|
|
1338
|
+
key: mapKeyThroughKeyMapping(op.key, keyMapping)
|
|
1339
|
+
}))
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
function addMissingKeysToOperations(operations) {
|
|
1343
|
+
return {
|
|
1344
|
+
set: operations.set?.map((op) => ({
|
|
1345
|
+
...op,
|
|
1346
|
+
value: addMissingKeys(op.value)
|
|
1347
|
+
})),
|
|
1348
|
+
unset: operations.unset,
|
|
1349
|
+
append: operations.append?.map((op) => ({
|
|
1350
|
+
...op,
|
|
1351
|
+
items: op.items.map((item) => addMissingKeys(item, { ensureRootKey: !0 }))
|
|
1352
|
+
})),
|
|
1353
|
+
insertBefore: operations.insertBefore?.map((op) => ({
|
|
1354
|
+
...op,
|
|
1355
|
+
items: op.items.map((item) => addMissingKeys(item, { ensureRootKey: !0 }))
|
|
1356
|
+
})),
|
|
1357
|
+
insertAfter: operations.insertAfter?.map((op) => ({
|
|
1358
|
+
...op,
|
|
1359
|
+
items: op.items.map((item) => addMissingKeys(item, { ensureRootKey: !0 }))
|
|
1360
|
+
}))
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function convertAppendToSetForMissingArrays(operations, document) {
|
|
1364
|
+
if (!operations.append || operations.append.length === 0)
|
|
1365
|
+
return operations;
|
|
1366
|
+
const remainingAppends = [], convertedSets = [];
|
|
1367
|
+
for (const op of operations.append) {
|
|
1368
|
+
const isSimplePath = !op.path.includes("["), existingValue = getValueAtPath(document, op.path), isMissingOrEmpty = existingValue == null || Array.isArray(existingValue) && existingValue.length === 0;
|
|
1369
|
+
isSimplePath && isMissingOrEmpty ? convertedSets.push({ path: op.path, value: [...op.items] }) : remainingAppends.push(op);
|
|
1370
|
+
}
|
|
1371
|
+
return convertedSets.length === 0 ? operations : {
|
|
1372
|
+
...operations,
|
|
1373
|
+
set: [...operations.set ?? [], ...convertedSets],
|
|
1374
|
+
append: remainingAppends.length > 0 ? remainingAppends : void 0
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
function prepareOperations(operations, options) {
|
|
1378
|
+
let prepared = normalizeOperationPaths(operations);
|
|
1379
|
+
return options.keyMapping && (prepared = applyKeyMapping(prepared, options.keyMapping)), prepared = convertArrayIndexOperations(
|
|
1380
|
+
prepared,
|
|
1381
|
+
options.typeSchema,
|
|
1382
|
+
options.allSchemas,
|
|
1383
|
+
options.document
|
|
1384
|
+
), prepared = convertAppendToSetForMissingArrays(prepared, options.document), prepared = normalizeInternationalizedArrays(prepared, {
|
|
1385
|
+
typeSchema: options.typeSchema,
|
|
1386
|
+
allSchemas: options.allSchemas
|
|
1387
|
+
}), prepared = addMissingKeysToOperations(prepared), prepared;
|
|
1388
|
+
}
|
|
1389
|
+
function pathEndsWithArrayIndex(path) {
|
|
1390
|
+
return /\[\d+\]$/.test(path) || /\[_key==[^\]]+\]$/.test(path);
|
|
1391
|
+
}
|
|
1392
|
+
function resolveToArraySchema(fieldSchema, allSchemas) {
|
|
1393
|
+
if (fieldSchema.type === "array")
|
|
1394
|
+
return fieldSchema;
|
|
1395
|
+
if (fieldSchema.type && !isPrimitiveType(fieldSchema.type)) {
|
|
1396
|
+
const referencedSchema = allSchemas.find((s) => s.name === fieldSchema.type);
|
|
1397
|
+
if (referencedSchema)
|
|
1398
|
+
return referencedSchema;
|
|
1399
|
+
}
|
|
1400
|
+
return fieldSchema;
|
|
1401
|
+
}
|
|
1402
|
+
function getSchemaForSetValue(fieldSchema, path, value, allSchemas) {
|
|
1403
|
+
if (!pathEndsWithArrayIndex(path))
|
|
1404
|
+
return fieldSchema;
|
|
1405
|
+
const resolvedSchema = resolveToArraySchema(fieldSchema, allSchemas);
|
|
1406
|
+
if (resolvedSchema.type !== "array" || !resolvedSchema.of?.length)
|
|
1407
|
+
return fieldSchema;
|
|
1408
|
+
const itemSchemas = resolvedSchema.of;
|
|
1409
|
+
if (typeof value == "object" && value !== null && !Array.isArray(value)) {
|
|
1410
|
+
const valueType = value._type;
|
|
1411
|
+
if (valueType) {
|
|
1412
|
+
let matchingSchema = itemSchemas.find(
|
|
1413
|
+
(schema) => schema.name === valueType || schema.type === valueType
|
|
1414
|
+
);
|
|
1415
|
+
if (matchingSchema || (matchingSchema = allSchemas.find((s) => s.name === valueType)), matchingSchema)
|
|
1416
|
+
return matchingSchema;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return itemSchemas[0];
|
|
1420
|
+
}
|
|
1421
|
+
function hasUniqueConstraint(fieldSchema) {
|
|
1422
|
+
return !!fieldSchema.validation?.some(
|
|
1423
|
+
(group) => group.rules.some((rule) => {
|
|
1424
|
+
const r = rule, ruleType = r.type || r.flag;
|
|
1425
|
+
return ruleType === "unique" || ruleType === "uniqueItems";
|
|
1426
|
+
})
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
async function validatePatchOperations(operations, documentType, schema, context) {
|
|
1430
|
+
const errors = [], warnings = [], typeSchema = schema.find((s) => s.name === documentType);
|
|
1431
|
+
return typeSchema ? (await validateSetOperations(operations.set, typeSchema, schema, context, errors, warnings), validateUnsetOperations(operations.unset, typeSchema, schema, context, errors), await validateAppendOperations(operations.append, typeSchema, schema, context, errors, warnings), await validateInsertOperations(operations, typeSchema, schema, context, errors, warnings), {
|
|
1432
|
+
valid: errors.length === 0,
|
|
1433
|
+
errors,
|
|
1434
|
+
warnings
|
|
1435
|
+
}) : (errors.push(
|
|
1436
|
+
createError(
|
|
1437
|
+
"_type",
|
|
1438
|
+
"FIELD_NOT_IN_SCHEMA",
|
|
1439
|
+
`Document type "${documentType}" not found in schema`,
|
|
1440
|
+
documentType,
|
|
1441
|
+
"Valid document type",
|
|
1442
|
+
"Check the schema for valid document type names"
|
|
1443
|
+
)
|
|
1444
|
+
), { valid: !1, errors, warnings });
|
|
1445
|
+
}
|
|
1446
|
+
async function validateSetOperations(ops, typeSchema, schema, context, errors, warnings) {
|
|
1447
|
+
if (ops)
|
|
1448
|
+
for (const op of ops) {
|
|
1449
|
+
const fieldSchema = resolveFieldSchemaAtPath(
|
|
1450
|
+
op.path,
|
|
1451
|
+
typeSchema,
|
|
1452
|
+
schema,
|
|
1453
|
+
context.existingDocument
|
|
1454
|
+
).schema;
|
|
1455
|
+
if (!fieldSchema) {
|
|
1456
|
+
errors.push(
|
|
1457
|
+
createError(
|
|
1458
|
+
op.path,
|
|
1459
|
+
"FIELD_NOT_IN_SCHEMA",
|
|
1460
|
+
"Path does not exist in schema",
|
|
1461
|
+
op.path,
|
|
1462
|
+
"Valid field path",
|
|
1463
|
+
"Check the schema for valid field paths"
|
|
1464
|
+
)
|
|
1465
|
+
);
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1468
|
+
if (op.value !== void 0) {
|
|
1469
|
+
const valueSchema = getSchemaForSetValue(fieldSchema, op.path, op.value, schema);
|
|
1470
|
+
await validateValue(op.value, valueSchema, op.path, errors, context, schema, warnings);
|
|
1471
|
+
}
|
|
1472
|
+
if (validateInternationalizedArraySet(op.path, op.value, fieldSchema, typeSchema, schema, errors), context.localizationConfig?.documentLevel) {
|
|
1473
|
+
const { languageField, availableLanguages } = context.localizationConfig.documentLevel;
|
|
1474
|
+
languageField && op.path === languageField && availableLanguages && validateDocumentLanguageField(op.value, languageField, availableLanguages, op.path, errors);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
function validateUnsetOperations(ops, typeSchema, schema, context, errors) {
|
|
1479
|
+
if (ops)
|
|
1480
|
+
for (const path of ops)
|
|
1481
|
+
resolveFieldSchemaAtPath(path, typeSchema, schema, context.existingDocument).schema || errors.push(
|
|
1482
|
+
createError(
|
|
1483
|
+
path,
|
|
1484
|
+
"FIELD_NOT_IN_SCHEMA",
|
|
1485
|
+
"Path does not exist in schema",
|
|
1486
|
+
path,
|
|
1487
|
+
"Valid field path",
|
|
1488
|
+
"Check the schema for valid field paths"
|
|
1489
|
+
)
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
async function validateAppendOperations(ops, typeSchema, schema, context, errors, warnings) {
|
|
1493
|
+
if (ops)
|
|
1494
|
+
for (const op of ops) {
|
|
1495
|
+
const fieldSchema = resolveFieldSchema(op.path, typeSchema, schema);
|
|
1496
|
+
if (!fieldSchema) {
|
|
1497
|
+
errors.push(
|
|
1498
|
+
createError(
|
|
1499
|
+
op.path,
|
|
1500
|
+
"FIELD_NOT_IN_SCHEMA",
|
|
1501
|
+
"Path does not exist in schema",
|
|
1502
|
+
op.path,
|
|
1503
|
+
"Valid array field path",
|
|
1504
|
+
"Check the schema for valid array field paths"
|
|
1505
|
+
)
|
|
1506
|
+
);
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
if (context.existingDocument && op.path.includes("[_key==") && getValueAtPath(context.existingDocument, op.path) === void 0) {
|
|
1510
|
+
errors.push(
|
|
1511
|
+
createError(
|
|
1512
|
+
op.path,
|
|
1513
|
+
"FIELD_NOT_IN_SCHEMA",
|
|
1514
|
+
"Path does not exist in document",
|
|
1515
|
+
op.path,
|
|
1516
|
+
"Valid array path that exists in document",
|
|
1517
|
+
"Check that the path with _key selectors points to an existing location"
|
|
1518
|
+
)
|
|
1519
|
+
);
|
|
1520
|
+
continue;
|
|
1521
|
+
}
|
|
1522
|
+
const arraySchema = resolveToArraySchema(fieldSchema, schema);
|
|
1523
|
+
if (arraySchema.of && arraySchema.of.length > 0) {
|
|
1524
|
+
const errorCountBefore = errors.length;
|
|
1525
|
+
await validateValue(op.items, arraySchema, op.path, errors, context, schema, warnings);
|
|
1526
|
+
for (let e = errorCountBefore; e < errors.length; e++)
|
|
1527
|
+
errors[e].path = errors[e].path.replace(
|
|
1528
|
+
new RegExp(`^${op.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\[(\\d+)\\]`),
|
|
1529
|
+
`${op.path}[append:$1]`
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
if (isInternationalizedArrayField(fieldSchema) && validateInternationalizedArrayItems(op.items, fieldSchema, schema, op.path, errors), hasUniqueConstraint(fieldSchema) && context.existingDocument) {
|
|
1533
|
+
const existingArray = getValueAtPath(context.existingDocument, op.path);
|
|
1534
|
+
Array.isArray(existingArray) && validateUniqueItems(op.items, existingArray, `${op.path}[append:`, errors);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
async function validateInsertOperations(operations, typeSchema, schema, context, errors, warnings) {
|
|
1539
|
+
for (const opType of ["insertBefore", "insertAfter"]) {
|
|
1540
|
+
const insertOps = operations[opType];
|
|
1541
|
+
if (insertOps)
|
|
1542
|
+
for (const op of insertOps) {
|
|
1543
|
+
const fieldSchema = resolveFieldSchema(op.arrayPath, typeSchema, schema);
|
|
1544
|
+
if (!fieldSchema) {
|
|
1545
|
+
errors.push(
|
|
1546
|
+
createError(
|
|
1547
|
+
op.arrayPath,
|
|
1548
|
+
"FIELD_NOT_IN_SCHEMA",
|
|
1549
|
+
"Array path does not exist in schema",
|
|
1550
|
+
op.arrayPath,
|
|
1551
|
+
"Valid array field path",
|
|
1552
|
+
"Check the schema for valid array field paths"
|
|
1553
|
+
)
|
|
1554
|
+
);
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
const resolvedSchema = resolveToArraySchema(fieldSchema, schema);
|
|
1558
|
+
if (resolvedSchema.type !== "array" && !resolvedSchema.of) {
|
|
1559
|
+
errors.push(
|
|
1560
|
+
createError(
|
|
1561
|
+
op.arrayPath,
|
|
1562
|
+
"TYPE_MISMATCH",
|
|
1563
|
+
"Path must point to an array field",
|
|
1564
|
+
fieldSchema.type || "unknown",
|
|
1565
|
+
"Array field",
|
|
1566
|
+
"Use an array field path for insert operations"
|
|
1567
|
+
)
|
|
1568
|
+
);
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
const arraySchema = resolvedSchema;
|
|
1572
|
+
if (context.existingDocument) {
|
|
1573
|
+
const existingArray = getValueAtPath(context.existingDocument, op.arrayPath);
|
|
1574
|
+
Array.isArray(existingArray) && (existingArray.some(
|
|
1575
|
+
(item) => typeof item == "object" && item !== null && item._key === op.key
|
|
1576
|
+
) || errors.push(
|
|
1577
|
+
createError(
|
|
1578
|
+
`${op.arrayPath}[_key=="${op.key}"]`,
|
|
1579
|
+
"INVALID_PATH",
|
|
1580
|
+
`Anchor key "${op.key}" not found in array`,
|
|
1581
|
+
op.key,
|
|
1582
|
+
"Existing _key in the array",
|
|
1583
|
+
"Use a _key that exists in the target array"
|
|
1584
|
+
)
|
|
1585
|
+
));
|
|
1586
|
+
}
|
|
1587
|
+
if (isInternationalizedArrayField(fieldSchema) && validateInternationalizedArrayItems(op.items, fieldSchema, schema, op.arrayPath, errors), await validateInsertItems(op, opType, arraySchema, schema, context, errors, warnings), hasUniqueConstraint(fieldSchema) && context.existingDocument) {
|
|
1588
|
+
const existingArray = getValueAtPath(context.existingDocument, op.arrayPath);
|
|
1589
|
+
Array.isArray(existingArray) && validateUniqueItems(op.items, existingArray, `${op.arrayPath}[${opType}:`, errors);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
async function validateInsertItems(op, opType, arraySchema, schema, context, errors, warnings) {
|
|
1595
|
+
const itemSchemas = arraySchema.of || [];
|
|
1596
|
+
if (itemSchemas.length !== 0)
|
|
1597
|
+
for (let i = 0; i < op.items.length; i++) {
|
|
1598
|
+
const item = op.items[i], itemPath = `${op.arrayPath}[${opType}:${i}]`, errorCountBefore = errors.length, itemType = (typeof item == "object" && item !== null ? item : null)?._type;
|
|
1599
|
+
let matchingSchema;
|
|
1600
|
+
for (const ofSchema of itemSchemas) {
|
|
1601
|
+
if (ofSchema.name === itemType || ofSchema.type === itemType) {
|
|
1602
|
+
matchingSchema = ofSchema;
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
if (!itemType && isPrimitiveType(ofSchema.type)) {
|
|
1606
|
+
matchingSchema = ofSchema;
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
matchingSchema && await validateValue(item, matchingSchema, itemPath, errors, context, schema, warnings);
|
|
1611
|
+
for (let e = errorCountBefore; e < errors.length; e++)
|
|
1612
|
+
errors[e].itemIndex = i, errors[e].arrayPath = op.arrayPath;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
export {
|
|
1616
|
+
acceptsFlatPortableText,
|
|
1617
|
+
acceptsOnlyFlatPortableText,
|
|
1618
|
+
addMissingKeys,
|
|
1619
|
+
buildPatchTransaction,
|
|
1620
|
+
clearDocumentCache,
|
|
1621
|
+
convertArrayIndexOperations,
|
|
1622
|
+
filterInvalidArrayItems,
|
|
1623
|
+
formatPartialWriteError,
|
|
1624
|
+
generateKey,
|
|
1625
|
+
getArrayAtPath,
|
|
1626
|
+
getArrayItemSchema,
|
|
1627
|
+
getKeyAtIndex,
|
|
1628
|
+
getReferenceFieldDefinition,
|
|
1629
|
+
hasAnyFlatPortableTextField,
|
|
1630
|
+
hasNumericIndex,
|
|
1631
|
+
isPrimitiveArray,
|
|
1632
|
+
mapKeyThroughKeyMapping,
|
|
1633
|
+
normalizeDocumentReferences,
|
|
1634
|
+
normalizeInternationalizedArrays,
|
|
1635
|
+
normalizeKeyQuotes,
|
|
1636
|
+
normalizePatchOperations,
|
|
1637
|
+
parseArrayIndex,
|
|
1638
|
+
prepareOperations,
|
|
1639
|
+
regenerateDuplicateKeys,
|
|
1640
|
+
transformPathWithKeyMapping,
|
|
1641
|
+
traverseAndNormalize,
|
|
1642
|
+
validatePatchOperations
|
|
1643
|
+
};
|
|
1644
|
+
//# sourceMappingURL=index.js.map
|