@sanity/agent-schema 0.0.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.
- package/README.md +277 -0
- package/dist/index.d.ts +356 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1051 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
async function fetchDatasetStats(client, options) {
|
|
3
|
+
const { organizationId, projectId, dataset } = options, response = await client.request({
|
|
4
|
+
uri: `/agent/${organizationId}/dataset-stats/${projectId}/${dataset}`
|
|
5
|
+
});
|
|
6
|
+
return "status" in response && response.status === "processing" ? { status: "processing" } : { status: "ready", stats: response };
|
|
7
|
+
}
|
|
8
|
+
function isHoistedMarker(x) {
|
|
9
|
+
if (x === null || typeof x != "object") return !1;
|
|
10
|
+
const obj = x;
|
|
11
|
+
return obj.__type === "hoisted" && typeof obj.key == "string";
|
|
12
|
+
}
|
|
13
|
+
function mapperWithHoisting(hoisted, map) {
|
|
14
|
+
const cache = /* @__PURE__ */ new Map();
|
|
15
|
+
return (entry) => {
|
|
16
|
+
if (isHoistedMarker(entry)) {
|
|
17
|
+
let result = cache.get(entry.key);
|
|
18
|
+
if (!result) {
|
|
19
|
+
const value = hoisted[entry.key];
|
|
20
|
+
if (!value) throw new Error(`Unknown hoisted key: ${entry.key}`);
|
|
21
|
+
result = map(value), cache.set(entry.key, result);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
return map(entry);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const STOP = /* @__PURE__ */ Symbol("STOP");
|
|
29
|
+
function decodeWithMarkers(x) {
|
|
30
|
+
const result = decodeWithMarkersOrStop(x);
|
|
31
|
+
if (result !== STOP)
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function decodeWithMarkersOrStop(x) {
|
|
35
|
+
if (x == null || typeof x != "object") return x;
|
|
36
|
+
if (Array.isArray(x)) {
|
|
37
|
+
const result2 = [];
|
|
38
|
+
for (const item of x) {
|
|
39
|
+
const decoded = decodeWithMarkers(item);
|
|
40
|
+
if (decoded === STOP) return STOP;
|
|
41
|
+
result2.push(decoded);
|
|
42
|
+
}
|
|
43
|
+
return result2;
|
|
44
|
+
}
|
|
45
|
+
if ("__type" in x) {
|
|
46
|
+
const typed = x;
|
|
47
|
+
switch (typed.__type) {
|
|
48
|
+
case "function":
|
|
49
|
+
case "jsx":
|
|
50
|
+
case "unknown":
|
|
51
|
+
case "cyclic":
|
|
52
|
+
case "maxDepth":
|
|
53
|
+
return STOP;
|
|
54
|
+
case "number":
|
|
55
|
+
return Number(typed.value);
|
|
56
|
+
case "undefined":
|
|
57
|
+
return;
|
|
58
|
+
case "object":
|
|
59
|
+
return decodeWithMarkersOrStop(typed.value);
|
|
60
|
+
default:
|
|
61
|
+
throw new Error(`unknown type: ${typed.__type}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const result = {};
|
|
65
|
+
for (const [key, value] of Object.entries(x)) {
|
|
66
|
+
const decoded = decodeWithMarkers(value);
|
|
67
|
+
result[key] = decoded;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
function mutableSqueeze(obj) {
|
|
72
|
+
for (const [key, value] of Object.entries(obj))
|
|
73
|
+
value === void 0 && delete obj[key];
|
|
74
|
+
return obj;
|
|
75
|
+
}
|
|
76
|
+
const STRIP_FIELDS_TYPES = /* @__PURE__ */ new Set([
|
|
77
|
+
"block",
|
|
78
|
+
"reference",
|
|
79
|
+
"globalDocumentReference",
|
|
80
|
+
"crossDatasetReference"
|
|
81
|
+
]);
|
|
82
|
+
function convertToDefinition(input) {
|
|
83
|
+
if (!input?.types || Object.keys(input.types).length === 0)
|
|
84
|
+
return [];
|
|
85
|
+
const hoisted = input.hoisted ?? {}, convertField = mapperWithHoisting(hoisted, (field) => ({
|
|
86
|
+
groups: field.groups,
|
|
87
|
+
fieldset: field.fieldset,
|
|
88
|
+
...convertTypeDef(field.name, field.typeDef)
|
|
89
|
+
})), convertArrayElement = mapperWithHoisting(
|
|
90
|
+
hoisted,
|
|
91
|
+
(element) => convertTypeDef(element.name, element.typeDef)
|
|
92
|
+
);
|
|
93
|
+
function convertTypeDef(name, typeDef) {
|
|
94
|
+
const { extends: type, fields, fieldsets, to, of, validation, ...rest } = typeDef;
|
|
95
|
+
return mutableSqueeze({
|
|
96
|
+
name,
|
|
97
|
+
type,
|
|
98
|
+
fields: fields && !STRIP_FIELDS_TYPES.has(type) ? fields.map(convertField) : void 0,
|
|
99
|
+
fieldsets: (fieldsets?.length || 0) > 0 ? fieldsets : void 0,
|
|
100
|
+
of: of ? of.map(convertArrayElement) : void 0,
|
|
101
|
+
to: to?.map(({ name: toName }) => mutableSqueeze({ type: toName })),
|
|
102
|
+
validation,
|
|
103
|
+
...decodeWithMarkers(rest)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const types = [];
|
|
107
|
+
for (const [name, typeDef] of Object.entries(input.types))
|
|
108
|
+
types.push(convertTypeDef(name, typeDef));
|
|
109
|
+
return types;
|
|
110
|
+
}
|
|
111
|
+
const DEFAULT_TTL_MS = 300 * 1e3, memoryCache = /* @__PURE__ */ new Map();
|
|
112
|
+
function getFromMemoryCache(descriptorId) {
|
|
113
|
+
const entry = memoryCache.get(descriptorId);
|
|
114
|
+
if (entry) {
|
|
115
|
+
if (Date.now() > entry.expiresAt) {
|
|
116
|
+
memoryCache.delete(descriptorId);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
return entry.response;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function setInMemoryCache(descriptorId, response, ttl) {
|
|
123
|
+
memoryCache.set(descriptorId, { response, expiresAt: Date.now() + ttl });
|
|
124
|
+
}
|
|
125
|
+
async function fetchSchemaByDescriptorId(client, descriptorId, options) {
|
|
126
|
+
const { cache, ttlMs = DEFAULT_TTL_MS } = options ?? {};
|
|
127
|
+
if (cache === !0) {
|
|
128
|
+
const hit = getFromMemoryCache(descriptorId);
|
|
129
|
+
if (hit) return convertToDefinition(hit);
|
|
130
|
+
} else if (cache) {
|
|
131
|
+
const hit = await cache.get(descriptorId);
|
|
132
|
+
if (hit) return convertToDefinition(hit);
|
|
133
|
+
}
|
|
134
|
+
const response = await client.request({
|
|
135
|
+
uri: `/descriptors/schemas/${descriptorId}`
|
|
136
|
+
});
|
|
137
|
+
return cache === !0 ? setInMemoryCache(descriptorId, response, ttlMs) : cache && await cache.set(descriptorId, response).catch(() => {
|
|
138
|
+
}), convertToDefinition(response);
|
|
139
|
+
}
|
|
140
|
+
const SCHEMA_CONSTANTS = {
|
|
141
|
+
SKIP_PATTERNS: /^(system\.|sanity\.|assist\.|media\.|mux\.)/,
|
|
142
|
+
TITLE_FIELD_CANDIDATES: ["title", "name", "heading", "label"],
|
|
143
|
+
DEFAULT_OPTIONS: {
|
|
144
|
+
trackPaths: !0,
|
|
145
|
+
maxExpansionDepth: 1
|
|
146
|
+
},
|
|
147
|
+
DEFAULTS: {
|
|
148
|
+
titleField: "title",
|
|
149
|
+
fallbackTitleField: "_id",
|
|
150
|
+
maxFieldsInSummary: 10,
|
|
151
|
+
maxEnumValues: 10,
|
|
152
|
+
maxDescriptionLength: 50,
|
|
153
|
+
maxDocDescriptionLength: 512
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
function resolveFilterSync(filter) {
|
|
157
|
+
if (filter && Array.isArray(filter))
|
|
158
|
+
return filter.length > 0 ? filter : void 0;
|
|
159
|
+
}
|
|
160
|
+
const TITLE_FIELD_CANDIDATES = SCHEMA_CONSTANTS.TITLE_FIELD_CANDIDATES;
|
|
161
|
+
function isTextLike(type) {
|
|
162
|
+
return type === "string" || type === "text";
|
|
163
|
+
}
|
|
164
|
+
function findTitleField(fields) {
|
|
165
|
+
for (const candidate of TITLE_FIELD_CANDIDATES)
|
|
166
|
+
if (fields.some((f) => f.name === candidate && isTextLike(f.type))) return candidate;
|
|
167
|
+
return fields.some((f) => f.name === "title") ? "title" : fields.find((f) => isTextLike(f.type))?.name ?? SCHEMA_CONSTANTS.DEFAULTS.fallbackTitleField;
|
|
168
|
+
}
|
|
169
|
+
function expandFields(fields, objectTypes, depth) {
|
|
170
|
+
return depth <= 0 ? fields : fields.map((field) => {
|
|
171
|
+
const expanded = { ...field };
|
|
172
|
+
if (field.type && objectTypes.has(field.type)) {
|
|
173
|
+
const obj = objectTypes.get(field.type);
|
|
174
|
+
expanded.type = "object", expanded.fields = expandFields(obj.fields ?? [], objectTypes, depth - 1);
|
|
175
|
+
}
|
|
176
|
+
return Array.isArray(field.of) && (expanded.of = field.of.map((item) => {
|
|
177
|
+
const itemType = item?.type;
|
|
178
|
+
if (itemType && objectTypes.has(itemType)) {
|
|
179
|
+
const obj = objectTypes.get(itemType);
|
|
180
|
+
return {
|
|
181
|
+
...item,
|
|
182
|
+
type: "object",
|
|
183
|
+
fields: expandFields(obj.fields ?? [], objectTypes, depth - 1)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return item;
|
|
187
|
+
})), expanded;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function collectReferences(fields, path, included, trackPaths, outgoing, outgoingPaths, addIncoming, sourceType) {
|
|
191
|
+
for (const field of fields) {
|
|
192
|
+
const fieldPath = path ? `${path}.${field.name}` : field.name;
|
|
193
|
+
if (field.type === "reference" && field.to)
|
|
194
|
+
for (const target of field.to) {
|
|
195
|
+
const t = target.type;
|
|
196
|
+
!t || !included(t) || (outgoing.add(t), trackPaths && (outgoingPaths.push({ type: t, path: fieldPath }), addIncoming(t, sourceType, fieldPath)));
|
|
197
|
+
}
|
|
198
|
+
if (field.fields?.length && collectReferences(
|
|
199
|
+
field.fields,
|
|
200
|
+
fieldPath,
|
|
201
|
+
included,
|
|
202
|
+
trackPaths,
|
|
203
|
+
outgoing,
|
|
204
|
+
outgoingPaths,
|
|
205
|
+
addIncoming,
|
|
206
|
+
sourceType
|
|
207
|
+
), Array.isArray(field.of))
|
|
208
|
+
for (const item of field.of)
|
|
209
|
+
if (item?.type === "reference" && Array.isArray(item.to))
|
|
210
|
+
for (const target of item.to) {
|
|
211
|
+
const t = target.type;
|
|
212
|
+
if (!t || !included(t)) continue;
|
|
213
|
+
outgoing.add(t);
|
|
214
|
+
const arrayPath = `${fieldPath}[]`;
|
|
215
|
+
trackPaths && (outgoingPaths.push({ type: t, path: arrayPath }), addIncoming(t, sourceType, arrayPath));
|
|
216
|
+
}
|
|
217
|
+
else Array.isArray(item?.fields) && collectReferences(
|
|
218
|
+
item.fields,
|
|
219
|
+
`${fieldPath}[]`,
|
|
220
|
+
included,
|
|
221
|
+
trackPaths,
|
|
222
|
+
outgoing,
|
|
223
|
+
outgoingPaths,
|
|
224
|
+
addIncoming,
|
|
225
|
+
sourceType
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function processSchema(schema, options) {
|
|
230
|
+
const trackPaths = SCHEMA_CONSTANTS.DEFAULT_OPTIONS.trackPaths, includeList = resolveFilterSync(options?.filter), includeSet = includeList?.length ? new Set(includeList) : null, included = (name) => !includeSet || includeSet.has(name), maxDepth = SCHEMA_CONSTANTS.DEFAULT_OPTIONS.maxExpansionDepth, documentTypes = /* @__PURE__ */ new Map(), objectTypes = /* @__PURE__ */ new Map();
|
|
231
|
+
for (const type of schema)
|
|
232
|
+
if (type.type === "document") {
|
|
233
|
+
if (!included(type.name)) continue;
|
|
234
|
+
documentTypes.set(type.name, {
|
|
235
|
+
name: type.name,
|
|
236
|
+
title: type.title,
|
|
237
|
+
description: type.description,
|
|
238
|
+
deprecated: type.deprecated,
|
|
239
|
+
titleField: SCHEMA_CONSTANTS.DEFAULTS.titleField,
|
|
240
|
+
fields: type.fields ?? [],
|
|
241
|
+
outgoingReferences: [],
|
|
242
|
+
incomingReferences: []
|
|
243
|
+
});
|
|
244
|
+
} else type.type === "object" && objectTypes.set(type.name, type);
|
|
245
|
+
for (const doc of documentTypes.values())
|
|
246
|
+
doc.fields = expandFields(doc.fields, objectTypes, maxDepth);
|
|
247
|
+
const incomingMap = /* @__PURE__ */ new Map(), incomingSimple = /* @__PURE__ */ new Map(), addIncoming = (targetType, sourceType, fieldPath) => {
|
|
248
|
+
incomingSimple.has(targetType) || incomingSimple.set(targetType, /* @__PURE__ */ new Set()), incomingSimple.get(targetType).add(sourceType), incomingMap.has(targetType) || incomingMap.set(targetType, /* @__PURE__ */ new Map());
|
|
249
|
+
const sourceMap = incomingMap.get(targetType);
|
|
250
|
+
sourceMap.has(sourceType) || sourceMap.set(sourceType, /* @__PURE__ */ new Set()), sourceMap.get(sourceType).add(fieldPath);
|
|
251
|
+
};
|
|
252
|
+
for (const [docName, doc] of documentTypes) {
|
|
253
|
+
const outgoing = /* @__PURE__ */ new Set(), outgoingPaths = [];
|
|
254
|
+
if (collectReferences(
|
|
255
|
+
doc.fields,
|
|
256
|
+
"",
|
|
257
|
+
included,
|
|
258
|
+
trackPaths,
|
|
259
|
+
outgoing,
|
|
260
|
+
outgoingPaths,
|
|
261
|
+
addIncoming,
|
|
262
|
+
docName
|
|
263
|
+
), !trackPaths)
|
|
264
|
+
for (const targetType of outgoing)
|
|
265
|
+
incomingSimple.has(targetType) || incomingSimple.set(targetType, /* @__PURE__ */ new Set()), incomingSimple.get(targetType).add(docName);
|
|
266
|
+
doc.outgoingReferences = Array.from(outgoing), trackPaths && (doc.outgoingReferencePaths = outgoingPaths);
|
|
267
|
+
}
|
|
268
|
+
for (const [targetType, sources] of incomingSimple) {
|
|
269
|
+
const target = documentTypes.get(targetType);
|
|
270
|
+
if (target && (target.incomingReferences = Array.from(sources), trackPaths && incomingMap.has(targetType))) {
|
|
271
|
+
const paths = [];
|
|
272
|
+
for (const [sourceType, fieldPaths] of incomingMap.get(targetType))
|
|
273
|
+
for (const p of fieldPaths)
|
|
274
|
+
paths.push({ type: sourceType, path: p });
|
|
275
|
+
target.incomingReferencePaths = paths;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
for (const doc of documentTypes.values())
|
|
279
|
+
doc.titleField = findTitleField(doc.fields);
|
|
280
|
+
return { documentTypes, objectTypes };
|
|
281
|
+
}
|
|
282
|
+
const BUILT_IN_TYPE_FIELDS = {
|
|
283
|
+
slug: {
|
|
284
|
+
current: { name: "current", type: "string" },
|
|
285
|
+
_type: { name: "_type", type: "string" }
|
|
286
|
+
},
|
|
287
|
+
geopoint: {
|
|
288
|
+
lat: { name: "lat", type: "number" },
|
|
289
|
+
lng: { name: "lng", type: "number" },
|
|
290
|
+
alt: { name: "alt", type: "number" },
|
|
291
|
+
_type: { name: "_type", type: "string" }
|
|
292
|
+
},
|
|
293
|
+
reference: {
|
|
294
|
+
_ref: { name: "_ref", type: "string" },
|
|
295
|
+
_type: { name: "_type", type: "string" },
|
|
296
|
+
_weak: { name: "_weak", type: "boolean" }
|
|
297
|
+
},
|
|
298
|
+
image: {
|
|
299
|
+
asset: { name: "asset", type: "reference" },
|
|
300
|
+
hotspot: {
|
|
301
|
+
name: "hotspot",
|
|
302
|
+
type: "object",
|
|
303
|
+
fields: [
|
|
304
|
+
{ name: "x", type: "number" },
|
|
305
|
+
{ name: "y", type: "number" },
|
|
306
|
+
{ name: "height", type: "number" },
|
|
307
|
+
{ name: "width", type: "number" }
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
crop: {
|
|
311
|
+
name: "crop",
|
|
312
|
+
type: "object",
|
|
313
|
+
fields: [
|
|
314
|
+
{ name: "top", type: "number" },
|
|
315
|
+
{ name: "bottom", type: "number" },
|
|
316
|
+
{ name: "left", type: "number" },
|
|
317
|
+
{ name: "right", type: "number" }
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
_type: { name: "_type", type: "string" }
|
|
321
|
+
},
|
|
322
|
+
file: {
|
|
323
|
+
asset: { name: "asset", type: "reference" },
|
|
324
|
+
_type: { name: "_type", type: "string" }
|
|
325
|
+
},
|
|
326
|
+
block: {
|
|
327
|
+
style: { name: "style", type: "string" },
|
|
328
|
+
listItem: { name: "listItem", type: "string" },
|
|
329
|
+
level: { name: "level", type: "number" },
|
|
330
|
+
children: {
|
|
331
|
+
name: "children",
|
|
332
|
+
type: "array",
|
|
333
|
+
of: [{ name: "span", type: "span" }]
|
|
334
|
+
},
|
|
335
|
+
markDefs: {
|
|
336
|
+
name: "markDefs",
|
|
337
|
+
type: "array",
|
|
338
|
+
of: [{ name: "object", type: "object" }]
|
|
339
|
+
},
|
|
340
|
+
_type: { name: "_type", type: "string" }
|
|
341
|
+
},
|
|
342
|
+
span: {
|
|
343
|
+
text: { name: "text", type: "string" },
|
|
344
|
+
marks: {
|
|
345
|
+
name: "marks",
|
|
346
|
+
type: "array",
|
|
347
|
+
of: [{ name: "string", type: "string" }]
|
|
348
|
+
},
|
|
349
|
+
_type: { name: "_type", type: "string" }
|
|
350
|
+
}
|
|
351
|
+
}, DEFAULT_LINK_ANNOTATION = {
|
|
352
|
+
name: "link",
|
|
353
|
+
type: "object",
|
|
354
|
+
fields: [
|
|
355
|
+
{ name: "href", type: "string" },
|
|
356
|
+
{ name: "blank", type: "boolean" }
|
|
357
|
+
]
|
|
358
|
+
}, ARRAY_ITEM_SYSTEM_FIELDS = {
|
|
359
|
+
_key: { name: "_key", type: "string" },
|
|
360
|
+
_type: { name: "_type", type: "string" }
|
|
361
|
+
};
|
|
362
|
+
function parseOfIndexSyntax(segment) {
|
|
363
|
+
const match = segment.match(/^of\[(\d+)\]$/);
|
|
364
|
+
return match ? parseInt(match[1], 10) : null;
|
|
365
|
+
}
|
|
366
|
+
function parseArrayNotation(segment) {
|
|
367
|
+
const match = segment.match(/^([^[]+)\[.*\]$/);
|
|
368
|
+
return match ? { fieldName: match[1], isArray: !0 } : null;
|
|
369
|
+
}
|
|
370
|
+
function createNotFoundMarker() {
|
|
371
|
+
return { name: "", type: "notfound", fields: [] };
|
|
372
|
+
}
|
|
373
|
+
function navigateToOfIndex(node, index) {
|
|
374
|
+
return !node.of || !Array.isArray(node.of) || index < 0 || index >= node.of.length ? null : node.of[index];
|
|
375
|
+
}
|
|
376
|
+
function navigateToSchemaProperty(node, propertyName, isLastSegment) {
|
|
377
|
+
if (!node || typeof node != "object" || !(propertyName in node))
|
|
378
|
+
return { result: null };
|
|
379
|
+
const propertyValue = node[propertyName];
|
|
380
|
+
return isLastSegment ? Array.isArray(propertyValue) ? {
|
|
381
|
+
result: {
|
|
382
|
+
name: propertyName,
|
|
383
|
+
type: "array",
|
|
384
|
+
of: propertyValue
|
|
385
|
+
}
|
|
386
|
+
} : {
|
|
387
|
+
result: {
|
|
388
|
+
name: propertyName,
|
|
389
|
+
type: typeof propertyValue == "object" && propertyValue !== null ? "object" : typeof propertyValue,
|
|
390
|
+
...propertyValue
|
|
391
|
+
}
|
|
392
|
+
} : typeof propertyValue == "object" && propertyValue !== null ? Array.isArray(propertyValue) ? {
|
|
393
|
+
result: null,
|
|
394
|
+
continueWith: {
|
|
395
|
+
type: "array",
|
|
396
|
+
of: propertyValue
|
|
397
|
+
}
|
|
398
|
+
} : {
|
|
399
|
+
result: null,
|
|
400
|
+
continueWith: propertyValue
|
|
401
|
+
} : { result: null };
|
|
402
|
+
}
|
|
403
|
+
function resolveArrayFieldOf(field, schema) {
|
|
404
|
+
if (field.of) return field.of;
|
|
405
|
+
if (field.type) {
|
|
406
|
+
const referencedType = schema.find((t) => t.name === field.type);
|
|
407
|
+
if (referencedType?.of)
|
|
408
|
+
return referencedType.of;
|
|
409
|
+
}
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
function createArrayTypeFromField(field, schema) {
|
|
413
|
+
if (field.type) {
|
|
414
|
+
const arrayType = schema.find((t) => t.name === field.type);
|
|
415
|
+
if (arrayType) return arrayType;
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
...field,
|
|
419
|
+
name: field.name,
|
|
420
|
+
type: "array"
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function filterToPath(type, path, schema) {
|
|
424
|
+
const segments = path.split(/\.(?![^[]*\])/);
|
|
425
|
+
if (segments.length === 1 && segments[0].startsWith("of[")) {
|
|
426
|
+
const index = parseOfIndexSyntax(segments[0]);
|
|
427
|
+
if (index !== null)
|
|
428
|
+
return navigateToOfIndex(type, index) || { ...type, fields: [], of: void 0 };
|
|
429
|
+
}
|
|
430
|
+
let currentNode = type, currentFields = type.fields;
|
|
431
|
+
for (let i = 0; i < segments.length; i++) {
|
|
432
|
+
const segment = segments[i], ofIndex = parseOfIndexSyntax(segment);
|
|
433
|
+
if (ofIndex !== null) {
|
|
434
|
+
const item = navigateToOfIndex(currentNode, ofIndex);
|
|
435
|
+
if (item) {
|
|
436
|
+
if (i === segments.length - 1)
|
|
437
|
+
return item;
|
|
438
|
+
currentNode = item, currentFields = currentNode.fields;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
return { ...type, fields: [], of: void 0 };
|
|
442
|
+
}
|
|
443
|
+
const arrayNotation = parseArrayNotation(segment), fieldName = arrayNotation ? arrayNotation.fieldName : segment, field = currentFields?.find((f) => f.name === fieldName);
|
|
444
|
+
if (!field) {
|
|
445
|
+
const isLastSegment = i === segments.length - 1, propertyNav = navigateToSchemaProperty(currentNode, fieldName, isLastSegment);
|
|
446
|
+
if (propertyNav.result)
|
|
447
|
+
return propertyNav.result;
|
|
448
|
+
if (propertyNav.continueWith) {
|
|
449
|
+
currentNode = propertyNav.continueWith, currentFields = currentNode.fields;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
return createNotFoundMarker();
|
|
453
|
+
}
|
|
454
|
+
if (i === segments.length - 1)
|
|
455
|
+
return arrayNotation ? (segment.match(/\[(\d+)\]$/), createArrayTypeFromField(field, schema)) : { ...type, fields: [field] };
|
|
456
|
+
if (arrayNotation) {
|
|
457
|
+
const fieldOf = resolveArrayFieldOf(field, schema);
|
|
458
|
+
if (!fieldOf)
|
|
459
|
+
return { ...type, fields: [] };
|
|
460
|
+
currentNode = createArrayTypeFromField(field, schema);
|
|
461
|
+
const nextSegment = segments[i + 1];
|
|
462
|
+
if (!nextSegment)
|
|
463
|
+
continue;
|
|
464
|
+
const nextOfIndex = parseOfIndexSyntax(nextSegment);
|
|
465
|
+
if (nextOfIndex !== null) {
|
|
466
|
+
const item = navigateToOfIndex(currentNode, nextOfIndex);
|
|
467
|
+
if (item) {
|
|
468
|
+
if (i + 1 === segments.length - 1)
|
|
469
|
+
return item;
|
|
470
|
+
currentNode = item, currentFields = item.fields, i++;
|
|
471
|
+
continue;
|
|
472
|
+
} else
|
|
473
|
+
return { ...type, fields: [], of: void 0 };
|
|
474
|
+
}
|
|
475
|
+
const arrayItemByType = fieldOf.find(
|
|
476
|
+
(item) => item.name === nextSegment || item.type === nextSegment
|
|
477
|
+
);
|
|
478
|
+
if (arrayItemByType) {
|
|
479
|
+
if (i + 1 === segments.length - 1)
|
|
480
|
+
return { ...type, fields: [{ ...field, of: [arrayItemByType] }] };
|
|
481
|
+
if (arrayItemByType.fields) {
|
|
482
|
+
currentFields = arrayItemByType.fields, i++;
|
|
483
|
+
continue;
|
|
484
|
+
} else
|
|
485
|
+
return { ...type, fields: [] };
|
|
486
|
+
}
|
|
487
|
+
const matchingFields = [];
|
|
488
|
+
for (const item of fieldOf) {
|
|
489
|
+
const itemAsType = item;
|
|
490
|
+
if (itemAsType.fields && Array.isArray(itemAsType.fields)) {
|
|
491
|
+
const matchingField = itemAsType.fields.find((f) => f.name === nextSegment);
|
|
492
|
+
matchingField && matchingFields.push({
|
|
493
|
+
typeName: itemAsType.name || itemAsType.type,
|
|
494
|
+
field: matchingField
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (matchingFields.length === 0)
|
|
499
|
+
return { ...type, fields: [] };
|
|
500
|
+
if (i + 1 === segments.length - 1)
|
|
501
|
+
if (matchingFields.length === 1) {
|
|
502
|
+
const match = matchingFields[0], syntheticArrayField = {
|
|
503
|
+
...field,
|
|
504
|
+
of: [
|
|
505
|
+
{
|
|
506
|
+
name: match.typeName,
|
|
507
|
+
type: "object",
|
|
508
|
+
fields: [match.field]
|
|
509
|
+
// The matched field
|
|
510
|
+
}
|
|
511
|
+
]
|
|
512
|
+
};
|
|
513
|
+
return { ...type, fields: [syntheticArrayField] };
|
|
514
|
+
} else {
|
|
515
|
+
const syntheticArrayField = {
|
|
516
|
+
...field,
|
|
517
|
+
of: matchingFields.map((m) => ({
|
|
518
|
+
name: m.typeName,
|
|
519
|
+
type: "object",
|
|
520
|
+
fields: [m.field]
|
|
521
|
+
}))
|
|
522
|
+
};
|
|
523
|
+
return { ...type, fields: [syntheticArrayField] };
|
|
524
|
+
}
|
|
525
|
+
currentFields = matchingFields[0].field.fields, i++;
|
|
526
|
+
} else if (field.fields)
|
|
527
|
+
currentFields = field.fields;
|
|
528
|
+
else
|
|
529
|
+
return { ...type, fields: [] };
|
|
530
|
+
}
|
|
531
|
+
return { ...type, fields: [] };
|
|
532
|
+
}
|
|
533
|
+
function toSchemaPath(path) {
|
|
534
|
+
return path ? path.replace(/\[append:\d+\]/g, "").replace(/\[_key\s*==\s*["'][^"']*["']\]/g, "").replace(/\[\d+\]/g, "").replace(/\[\]/g, "").replace(/\.{2,}/g, ".").replace(/^\.+|\.+$/g, "") : "";
|
|
535
|
+
}
|
|
536
|
+
const BASE_PRIMITIVES = ["string", "number", "boolean", "date", "datetime", "text", "url", "email"], BLOCK_TYPES = ["block", "reference"];
|
|
537
|
+
function isPrimitiveType(typeName, options = {}) {
|
|
538
|
+
return typeName ? options.includeBlockTypes ? BASE_PRIMITIVES.includes(typeName) || BLOCK_TYPES.includes(typeName) : BASE_PRIMITIVES.includes(typeName) : !1;
|
|
539
|
+
}
|
|
540
|
+
function isBuiltInType(type) {
|
|
541
|
+
return BUILT_IN_TYPES.has(type);
|
|
542
|
+
}
|
|
543
|
+
const BUILT_IN_TYPES = /* @__PURE__ */ new Set([
|
|
544
|
+
"string",
|
|
545
|
+
"text",
|
|
546
|
+
"number",
|
|
547
|
+
"boolean",
|
|
548
|
+
"array",
|
|
549
|
+
"object",
|
|
550
|
+
"reference",
|
|
551
|
+
"image",
|
|
552
|
+
"file",
|
|
553
|
+
"date",
|
|
554
|
+
"datetime",
|
|
555
|
+
"url",
|
|
556
|
+
"slug",
|
|
557
|
+
"block",
|
|
558
|
+
"geopoint"
|
|
559
|
+
]);
|
|
560
|
+
function isGenericObject(node) {
|
|
561
|
+
if (typeof node != "object" || node === null || Array.isArray(node)) return !1;
|
|
562
|
+
const obj = node;
|
|
563
|
+
return !obj.fields && !(obj.type === "array" && Array.isArray(obj.of));
|
|
564
|
+
}
|
|
565
|
+
function resolveFieldSchemaAllUnions(path, typeSchema, schema) {
|
|
566
|
+
const pathParts = toSchemaPath(path).split(".").filter(Boolean);
|
|
567
|
+
let currentSchemas = [typeSchema];
|
|
568
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
569
|
+
const part = pathParts[i], isLastPart = i === pathParts.length - 1, nextSchemas = [];
|
|
570
|
+
for (const currentSchema of currentSchemas) {
|
|
571
|
+
if (!currentSchema?.fields || !Array.isArray(currentSchema.fields))
|
|
572
|
+
continue;
|
|
573
|
+
const field = currentSchema.fields.find((f) => f.name === part);
|
|
574
|
+
if (field)
|
|
575
|
+
if (field.type === "array" && field.of && field.of.length > 0)
|
|
576
|
+
for (const itemSchema of field.of) {
|
|
577
|
+
const itemType = typeof itemSchema.type == "string" ? itemSchema.type : void 0;
|
|
578
|
+
if (isLastPart && (itemType === "image" || itemType === "file"))
|
|
579
|
+
nextSchemas.push(itemSchema);
|
|
580
|
+
else if (itemType && !isBuiltInType(itemType)) {
|
|
581
|
+
const foundSchema = schema.find((s) => s.name === itemType);
|
|
582
|
+
foundSchema && nextSchemas.push(foundSchema);
|
|
583
|
+
} else itemSchema.fields && nextSchemas.push(itemSchema);
|
|
584
|
+
}
|
|
585
|
+
else if (field.type && !isBuiltInType(field.type)) {
|
|
586
|
+
const foundSchema = schema.find((s) => s.name === field.type);
|
|
587
|
+
if (foundSchema)
|
|
588
|
+
if (foundSchema.type === "array" && foundSchema.of && foundSchema.of.length > 0)
|
|
589
|
+
for (const itemSchema of foundSchema.of) {
|
|
590
|
+
const itemType = typeof itemSchema.type == "string" ? itemSchema.type : void 0;
|
|
591
|
+
if (isLastPart && (itemType === "image" || itemType === "file"))
|
|
592
|
+
nextSchemas.push(itemSchema);
|
|
593
|
+
else if (itemType && !isBuiltInType(itemType)) {
|
|
594
|
+
const itemFoundSchema = schema.find((s) => s.name === itemType);
|
|
595
|
+
itemFoundSchema && nextSchemas.push(itemFoundSchema);
|
|
596
|
+
} else itemSchema.fields && nextSchemas.push(itemSchema);
|
|
597
|
+
}
|
|
598
|
+
else
|
|
599
|
+
nextSchemas.push(foundSchema);
|
|
600
|
+
} else
|
|
601
|
+
nextSchemas.push(field);
|
|
602
|
+
}
|
|
603
|
+
currentSchemas = nextSchemas;
|
|
604
|
+
}
|
|
605
|
+
return currentSchemas;
|
|
606
|
+
}
|
|
607
|
+
function getBuiltInFieldSchema(segment, currentSchema) {
|
|
608
|
+
if (!currentSchema?.type || !BUILT_IN_TYPE_FIELDS[currentSchema.type]?.[segment])
|
|
609
|
+
return null;
|
|
610
|
+
let builtInField = BUILT_IN_TYPE_FIELDS[currentSchema.type][segment];
|
|
611
|
+
if (currentSchema.type === "block" && segment === "children" && currentSchema.of && (builtInField = {
|
|
612
|
+
...builtInField,
|
|
613
|
+
of: [...builtInField.of || [], ...currentSchema.of]
|
|
614
|
+
}), currentSchema.type === "block" && segment === "markDefs") {
|
|
615
|
+
const annotations = currentSchema.marks?.annotations;
|
|
616
|
+
annotations ? builtInField = {
|
|
617
|
+
...builtInField,
|
|
618
|
+
of: [...builtInField.of || [], ...annotations]
|
|
619
|
+
} : builtInField = {
|
|
620
|
+
...builtInField,
|
|
621
|
+
of: [...builtInField.of || [], DEFAULT_LINK_ANNOTATION]
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
return builtInField;
|
|
625
|
+
}
|
|
626
|
+
function findSchemaInUnionArray(segment, arraySchema, allSchemas, isLastSegment) {
|
|
627
|
+
if (!arraySchema.of || arraySchema.of.length === 0)
|
|
628
|
+
return { schema: null, earlyReturn: null };
|
|
629
|
+
for (const itemSchema of arraySchema.of) {
|
|
630
|
+
if (BUILT_IN_TYPE_FIELDS[itemSchema.type]?.[segment]) {
|
|
631
|
+
const builtInField = BUILT_IN_TYPE_FIELDS[itemSchema.type][segment];
|
|
632
|
+
return isLastSegment ? { schema: null, earlyReturn: builtInField } : { schema: builtInField, earlyReturn: null };
|
|
633
|
+
}
|
|
634
|
+
if (itemSchema.fields?.some((f) => f.name === segment))
|
|
635
|
+
return { schema: itemSchema, earlyReturn: null };
|
|
636
|
+
if (itemSchema.type && !isPrimitiveType(itemSchema.type)) {
|
|
637
|
+
const referencedSchema2 = allSchemas.find((s) => s.name === itemSchema.type);
|
|
638
|
+
if (referencedSchema2?.fields?.some((f) => f.name === segment))
|
|
639
|
+
return { schema: referencedSchema2, earlyReturn: null };
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const firstItem = arraySchema.of[0];
|
|
643
|
+
if (firstItem.fields)
|
|
644
|
+
return { schema: firstItem, earlyReturn: null };
|
|
645
|
+
const referencedSchema = allSchemas.find((s) => s.name === firstItem.type);
|
|
646
|
+
return referencedSchema?.fields ? { schema: referencedSchema, earlyReturn: null } : { schema: null, earlyReturn: null };
|
|
647
|
+
}
|
|
648
|
+
function resolveCurrentSchema(segment, currentSchema, allSchemas, isLastSegment) {
|
|
649
|
+
if (!currentSchema?.type)
|
|
650
|
+
return { schema: null, earlyReturn: null };
|
|
651
|
+
if (currentSchema.type === "array" && currentSchema.of && currentSchema.of.length > 0)
|
|
652
|
+
return findSchemaInUnionArray(segment, currentSchema, allSchemas, isLastSegment);
|
|
653
|
+
const referencedSchema = allSchemas.find((s) => s.name === currentSchema.type);
|
|
654
|
+
if (referencedSchema?.fields)
|
|
655
|
+
return { schema: referencedSchema, earlyReturn: null };
|
|
656
|
+
if (BUILT_IN_TYPE_FIELDS[currentSchema.type]?.[segment]) {
|
|
657
|
+
const builtInField = BUILT_IN_TYPE_FIELDS[currentSchema.type][segment];
|
|
658
|
+
return isLastSegment ? { schema: null, earlyReturn: builtInField } : { schema: builtInField, earlyReturn: null };
|
|
659
|
+
}
|
|
660
|
+
return { schema: null, earlyReturn: null };
|
|
661
|
+
}
|
|
662
|
+
function getNextSchemaForField(field, nextSegment, allSchemas) {
|
|
663
|
+
let resolvedField = field;
|
|
664
|
+
if (field.type && !isPrimitiveType(field.type) && field.type !== "array") {
|
|
665
|
+
const referencedSchema = allSchemas.find((s) => s.name === field.type);
|
|
666
|
+
referencedSchema && (resolvedField = referencedSchema);
|
|
667
|
+
}
|
|
668
|
+
if (resolvedField.type === "array" && resolvedField.of && resolvedField.of.length > 0) {
|
|
669
|
+
for (const itemSchema of resolvedField.of) {
|
|
670
|
+
if (BUILT_IN_TYPE_FIELDS[itemSchema.type]?.[nextSegment] || itemSchema.fields?.some((f) => f.name === nextSegment))
|
|
671
|
+
return itemSchema;
|
|
672
|
+
if (itemSchema.type && !isPrimitiveType(itemSchema.type)) {
|
|
673
|
+
const refSchema = allSchemas.find((s) => s.name === itemSchema.type);
|
|
674
|
+
if (refSchema?.fields?.some((f) => f.name === nextSegment))
|
|
675
|
+
return refSchema;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return resolvedField.of[0];
|
|
679
|
+
}
|
|
680
|
+
return resolvedField;
|
|
681
|
+
}
|
|
682
|
+
function resolveFieldSchema(path, typeSchema, schema) {
|
|
683
|
+
const segments = toSchemaPath(path).split(".").filter(Boolean);
|
|
684
|
+
let currentSchema = typeSchema;
|
|
685
|
+
for (let i = 0; i < segments.length; i++) {
|
|
686
|
+
const segment = segments[i], isLastSegment = i === segments.length - 1;
|
|
687
|
+
if (ARRAY_ITEM_SYSTEM_FIELDS[segment])
|
|
688
|
+
return ARRAY_ITEM_SYSTEM_FIELDS[segment];
|
|
689
|
+
const builtInField = getBuiltInFieldSchema(segment, currentSchema);
|
|
690
|
+
if (builtInField) {
|
|
691
|
+
if (isLastSegment) return builtInField;
|
|
692
|
+
currentSchema = builtInField;
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (!currentSchema?.fields) {
|
|
696
|
+
const resolved = resolveCurrentSchema(segment, currentSchema, schema, isLastSegment);
|
|
697
|
+
if (resolved.earlyReturn) return resolved.earlyReturn;
|
|
698
|
+
if (!resolved.schema) return null;
|
|
699
|
+
currentSchema = resolved.schema;
|
|
700
|
+
const resolvedBuiltInField = getBuiltInFieldSchema(segment, currentSchema);
|
|
701
|
+
if (resolvedBuiltInField) {
|
|
702
|
+
if (isLastSegment) return resolvedBuiltInField;
|
|
703
|
+
currentSchema = resolvedBuiltInField;
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (!currentSchema?.fields) return null;
|
|
708
|
+
const field = currentSchema.fields.find((f) => f.name === segment);
|
|
709
|
+
if (!field) return null;
|
|
710
|
+
if (isLastSegment) return field;
|
|
711
|
+
currentSchema = getNextSchemaForField(field, segments[i + 1], schema);
|
|
712
|
+
}
|
|
713
|
+
return currentSchema || null;
|
|
714
|
+
}
|
|
715
|
+
function shouldSkipType(typeName) {
|
|
716
|
+
return SCHEMA_CONSTANTS.SKIP_PATTERNS.test(typeName);
|
|
717
|
+
}
|
|
718
|
+
function fullSchema(schema, options) {
|
|
719
|
+
const { type: typeName, path, sizeThreshold = 200 * 1024 } = options, type = schema.find((t) => t.name === typeName);
|
|
720
|
+
if (!type) {
|
|
721
|
+
const documentTypes = schema.filter((t) => t.type === "document" && !shouldSkipType(t.name)).map((t) => t.name).sort().slice(0, 10);
|
|
722
|
+
return {
|
|
723
|
+
_error: !0,
|
|
724
|
+
message: `Type "${typeName}" not found`,
|
|
725
|
+
availableTypes: documentTypes
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
const targetNode = path ? filterToPath(type, path, schema) : type;
|
|
729
|
+
if (path) {
|
|
730
|
+
if (targetNode.type === "notfound")
|
|
731
|
+
return {
|
|
732
|
+
_error: !0,
|
|
733
|
+
message: `Path "${path}" not found in type "${typeName}"`,
|
|
734
|
+
type: typeName,
|
|
735
|
+
path
|
|
736
|
+
};
|
|
737
|
+
const originalHadFields = type.fields && type.fields.length > 0, resultHasFields = targetNode.fields && targetNode.fields.length > 0, resultHasOf = targetNode.of && targetNode.of.length > 0, resultIsDifferent = targetNode !== type, hasJustTypeProperty = targetNode.type && Object.keys(targetNode).length <= 2, hasPropertyContent = resultIsDifferent && Object.keys(targetNode).length > 2, resultHasContent = resultHasFields || resultHasOf || targetNode.validation || targetNode.options || targetNode.to || resultIsDifferent && hasJustTypeProperty || hasPropertyContent;
|
|
738
|
+
if (resultIsDifferent && !resultHasContent || !resultIsDifferent && originalHadFields && !resultHasFields && !resultHasOf)
|
|
739
|
+
return {
|
|
740
|
+
_error: !0,
|
|
741
|
+
message: `Path "${path}" not found in type "${typeName}"`,
|
|
742
|
+
type: typeName,
|
|
743
|
+
path
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
const rawSubtree = extractRawSubtree(targetNode), jsonString = JSON.stringify(rawSubtree, null, 2);
|
|
747
|
+
return new TextEncoder().encode(jsonString).length <= sizeThreshold ? rawSubtree : generateLargeNodeNavigator(targetNode, path, typeName);
|
|
748
|
+
}
|
|
749
|
+
function extractRawSubtree(node) {
|
|
750
|
+
const cleaned = JSON.parse(JSON.stringify(node));
|
|
751
|
+
return cleaned.fields && Array.isArray(cleaned.fields) && (cleaned.fields = cleaned.fields.filter((field) => !shouldSkipType(field.name))), cleaned;
|
|
752
|
+
}
|
|
753
|
+
function generateLargeNodeNavigator(node, currentPath, rootTypeName) {
|
|
754
|
+
const children = [];
|
|
755
|
+
if ((node.type === "array" || node.of) && Array.isArray(node.of) && node.of.length > 0)
|
|
756
|
+
node.of.forEach((item, index) => {
|
|
757
|
+
const itemType = item.name || item.title || (item.type && item.type !== "object" ? item.type : null) || item.type || "unknown";
|
|
758
|
+
children.push({
|
|
759
|
+
type: String(itemType),
|
|
760
|
+
path: currentPath ? `${currentPath}.of[${index}]` : `of[${index}]`
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
else if (node.fields && Array.isArray(node.fields))
|
|
764
|
+
node.fields.forEach((field) => {
|
|
765
|
+
if (shouldSkipType(field.name)) return;
|
|
766
|
+
const childPath = currentPath ? `${currentPath}.${field.name}` : field.name;
|
|
767
|
+
if (field.type === "array" && field.of && Array.isArray(field.of)) {
|
|
768
|
+
const arrayPath = `${childPath}[]`;
|
|
769
|
+
field.of.forEach((item) => {
|
|
770
|
+
if (item.name || item.type) {
|
|
771
|
+
const itemType = String(item.name || item.type);
|
|
772
|
+
isPrimitiveType(itemType, { includeBlockTypes: !0 }) || children.push({ type: itemType, path: `${arrayPath}.${itemType}` });
|
|
773
|
+
}
|
|
774
|
+
}), field.of.length > 0 && children.push({ type: "array", path: arrayPath });
|
|
775
|
+
} else field.type === "object" || field.fields ? children.push({ type: field.type || "object", path: childPath }) : field.type && !isPrimitiveType(field.type, { includeBlockTypes: !0 }) && children.push({ type: field.type, path: childPath });
|
|
776
|
+
});
|
|
777
|
+
else if (isGenericObject(node)) {
|
|
778
|
+
const nodeObj = node, skipKeys = /* @__PURE__ */ new Set(["type", "name", "_type", "_key"]);
|
|
779
|
+
for (const key of Object.keys(nodeObj)) {
|
|
780
|
+
if (skipKeys.has(key)) continue;
|
|
781
|
+
const value = nodeObj[key], childPath = currentPath ? `${currentPath}.${key}` : key;
|
|
782
|
+
let propertyType = "unknown";
|
|
783
|
+
Array.isArray(value) ? propertyType = "array" : typeof value == "object" && value !== null ? propertyType = "object" : propertyType = typeof value, children.push({ type: propertyType, path: childPath });
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return {
|
|
787
|
+
_rawLargeNode: !0,
|
|
788
|
+
name: currentPath || rootTypeName,
|
|
789
|
+
type: node.type || "object",
|
|
790
|
+
children,
|
|
791
|
+
hint: "This node is large. Fetch a specific child path to drill in."
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
const InOutCountsSchema = z.object({
|
|
795
|
+
incoming: z.record(z.union([z.number(), z.literal("unknown")])),
|
|
796
|
+
outgoing: z.record(z.union([z.number(), z.literal("unknown")]))
|
|
797
|
+
}), DatasetStatsSchema = z.object({
|
|
798
|
+
documentCount: z.record(z.number()).optional(),
|
|
799
|
+
referenceCount: z.record(InOutCountsSchema).optional()
|
|
800
|
+
});
|
|
801
|
+
async function resolveFilter(schema, filter) {
|
|
802
|
+
if (!filter) return;
|
|
803
|
+
if (Array.isArray(filter)) return filter.length > 0 ? filter : void 0;
|
|
804
|
+
const result = await getFilteredDocumentTypes(schema, filter);
|
|
805
|
+
return result.length > 0 ? result : void 0;
|
|
806
|
+
}
|
|
807
|
+
async function getFilteredDocumentTypes(schema, filter) {
|
|
808
|
+
if (!filter || !filter.read && !filter.write) return [];
|
|
809
|
+
const allDocTypes = schema.filter((s) => s.type === "document").map((s) => s.name);
|
|
810
|
+
if (allDocTypes.length === 0) return [];
|
|
811
|
+
const combined = /* @__PURE__ */ new Set();
|
|
812
|
+
if (filter.read)
|
|
813
|
+
for (const type of await filterTypesByExpression(filter.read, allDocTypes))
|
|
814
|
+
combined.add(type);
|
|
815
|
+
if (filter.write)
|
|
816
|
+
for (const type of await filterTypesByExpression(filter.write, allDocTypes))
|
|
817
|
+
combined.add(type);
|
|
818
|
+
return combined.size === allDocTypes.length ? [] : Array.from(combined);
|
|
819
|
+
}
|
|
820
|
+
async function filterTypesByExpression(filterExpr, allTypes) {
|
|
821
|
+
if (!filterExpr?.trim() || allTypes.length === 0)
|
|
822
|
+
return new Set(allTypes);
|
|
823
|
+
try {
|
|
824
|
+
const { parse } = await import("groq-js"), ast = parse(filterExpr), matching = allTypes.filter((type) => evaluateForType(ast, type) !== !1);
|
|
825
|
+
return matching.length > 0 ? new Set(matching) : new Set(allTypes);
|
|
826
|
+
} catch {
|
|
827
|
+
return new Set(allTypes);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function prop(node, key) {
|
|
831
|
+
return node[key];
|
|
832
|
+
}
|
|
833
|
+
function child(node, key) {
|
|
834
|
+
return prop(node, key);
|
|
835
|
+
}
|
|
836
|
+
function evaluateForType(node, type) {
|
|
837
|
+
if (!node || typeof node != "object") return null;
|
|
838
|
+
switch (node.type) {
|
|
839
|
+
case "OpCall":
|
|
840
|
+
return evaluateOpCall(node, type);
|
|
841
|
+
case "And": {
|
|
842
|
+
const left = evaluateForType(child(node, "left"), type), right = evaluateForType(child(node, "right"), type);
|
|
843
|
+
return left === !1 || right === !1 ? !1 : left === !0 && right === !0 ? !0 : null;
|
|
844
|
+
}
|
|
845
|
+
case "Or": {
|
|
846
|
+
const left = evaluateForType(child(node, "left"), type), right = evaluateForType(child(node, "right"), type);
|
|
847
|
+
return left === !0 || right === !0 ? !0 : left === !1 && right === !1 ? !1 : null;
|
|
848
|
+
}
|
|
849
|
+
case "Not": {
|
|
850
|
+
const inner = evaluateForType(child(node, "base"), type);
|
|
851
|
+
return inner === null ? null : !inner;
|
|
852
|
+
}
|
|
853
|
+
case "Group":
|
|
854
|
+
return evaluateForType(child(node, "base"), type);
|
|
855
|
+
default:
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function evaluateOpCall(node, type) {
|
|
860
|
+
const op = prop(node, "op"), left = child(node, "left"), right = child(node, "right");
|
|
861
|
+
if (!isTypeAttribute(left)) return null;
|
|
862
|
+
const rightType = right.type;
|
|
863
|
+
if (op === "==" && rightType === "Value") {
|
|
864
|
+
const val = prop(right, "value");
|
|
865
|
+
return typeof val == "string" ? type === val : null;
|
|
866
|
+
}
|
|
867
|
+
if (op === "!=" && rightType === "Value") {
|
|
868
|
+
const val = prop(right, "value");
|
|
869
|
+
return typeof val == "string" ? type !== val : null;
|
|
870
|
+
}
|
|
871
|
+
if (op === "in" && rightType === "Array") {
|
|
872
|
+
const values = extractStringValues(right);
|
|
873
|
+
return values ? values.includes(type) : null;
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
function isTypeAttribute(node) {
|
|
878
|
+
return node?.type === "AccessAttribute" && prop(node, "name") === "_type";
|
|
879
|
+
}
|
|
880
|
+
function extractStringValues(arrayNode) {
|
|
881
|
+
const elements = prop(arrayNode, "elements");
|
|
882
|
+
if (!elements) return null;
|
|
883
|
+
const values = [];
|
|
884
|
+
for (const el of elements) {
|
|
885
|
+
if (el.type !== "ArrayElement" || el.isSplat) return null;
|
|
886
|
+
const val = el.value;
|
|
887
|
+
if (val?.type !== "Value") return null;
|
|
888
|
+
const v = prop(val, "value");
|
|
889
|
+
if (typeof v != "string") return null;
|
|
890
|
+
values.push(v);
|
|
891
|
+
}
|
|
892
|
+
return values;
|
|
893
|
+
}
|
|
894
|
+
const SIMPLE_TYPE_MAP = {
|
|
895
|
+
document: "object",
|
|
896
|
+
crossDatasetReference: "reference"
|
|
897
|
+
}, KNOWN_TYPES = /* @__PURE__ */ new Set([
|
|
898
|
+
"string",
|
|
899
|
+
"text",
|
|
900
|
+
"number",
|
|
901
|
+
"boolean",
|
|
902
|
+
"date",
|
|
903
|
+
"datetime",
|
|
904
|
+
"image",
|
|
905
|
+
"file",
|
|
906
|
+
"array",
|
|
907
|
+
"object",
|
|
908
|
+
"reference",
|
|
909
|
+
"block",
|
|
910
|
+
"slug",
|
|
911
|
+
"geopoint",
|
|
912
|
+
"span",
|
|
913
|
+
"url",
|
|
914
|
+
"email",
|
|
915
|
+
"color"
|
|
916
|
+
]);
|
|
917
|
+
function getSimpleFieldType(field) {
|
|
918
|
+
return SIMPLE_TYPE_MAP[field.type] ?? (KNOWN_TYPES.has(field.type) ? field.type : "object");
|
|
919
|
+
}
|
|
920
|
+
function normalize(s) {
|
|
921
|
+
return s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
922
|
+
}
|
|
923
|
+
function getEnumValues(field) {
|
|
924
|
+
const list = field.options?.list;
|
|
925
|
+
return !Array.isArray(list) || list.length === 0 ? null : list.map(
|
|
926
|
+
(item) => typeof item == "string" ? item : item.value
|
|
927
|
+
).filter(Boolean);
|
|
928
|
+
}
|
|
929
|
+
function formatTypeString(field, maxEnumValues) {
|
|
930
|
+
const enumValues = getEnumValues(field);
|
|
931
|
+
if (!enumValues) return getSimpleFieldType(field);
|
|
932
|
+
const shown = enumValues.slice(0, maxEnumValues), overflow = enumValues.length - maxEnumValues;
|
|
933
|
+
return shown.join("|") + (overflow > 0 ? `|...${overflow} more` : "");
|
|
934
|
+
}
|
|
935
|
+
function formatDescription(field, includeDescription, maxLen) {
|
|
936
|
+
return !includeDescription || !field.title || normalize(field.title) === normalize(field.name) ? "" : `: ${field.title.length > maxLen ? `${field.title.slice(0, maxLen)}...` : field.title}`;
|
|
937
|
+
}
|
|
938
|
+
function formatFieldNamesWithTypes(fields, options) {
|
|
939
|
+
const {
|
|
940
|
+
showAll,
|
|
941
|
+
maxCount,
|
|
942
|
+
includeDescription = !1,
|
|
943
|
+
includeHidden = !1,
|
|
944
|
+
includeDeprecated = !1,
|
|
945
|
+
maxEnumValues = SCHEMA_CONSTANTS.DEFAULTS.maxEnumValues,
|
|
946
|
+
maxDescriptionLength = SCHEMA_CONSTANTS.DEFAULTS.maxDescriptionLength
|
|
947
|
+
} = options ?? {}, filtered = fields.filter((f) => !(!includeDeprecated && f.deprecated || !includeHidden && f.hidden)), limit = showAll ? filtered.length : maxCount ?? SCHEMA_CONSTANTS.DEFAULTS.maxFieldsInSummary;
|
|
948
|
+
return filtered.slice(0, limit).map((f) => {
|
|
949
|
+
const typeStr = formatTypeString(f, maxEnumValues), desc = formatDescription(f, includeDescription, maxDescriptionLength);
|
|
950
|
+
return `${f.name}<${typeStr}>${desc}`;
|
|
951
|
+
}).join(", ");
|
|
952
|
+
}
|
|
953
|
+
function formatReferenceCount(count) {
|
|
954
|
+
return typeof count == "number" && count > 0 ? `\xD7${count}` : "";
|
|
955
|
+
}
|
|
956
|
+
function formatDocumentCount(count, hasDocumentCounts) {
|
|
957
|
+
return hasDocumentCounts ? String(count ?? 0) : "unknown";
|
|
958
|
+
}
|
|
959
|
+
function calculateMaxDepth(fields, objectTypes, currentDepth = 1, visited = /* @__PURE__ */ new Set()) {
|
|
960
|
+
let max = currentDepth;
|
|
961
|
+
for (const field of fields) {
|
|
962
|
+
field.fields?.length && (max = Math.max(max, calculateMaxDepth(field.fields, objectTypes, currentDepth + 1, visited)));
|
|
963
|
+
const checkObjectType = (typeName) => {
|
|
964
|
+
if (!typeName || visited.has(typeName)) return;
|
|
965
|
+
const obj = objectTypes.get(typeName);
|
|
966
|
+
obj?.fields?.length && (visited.add(typeName), max = Math.max(max, calculateMaxDepth(obj.fields, objectTypes, currentDepth + 1, visited)));
|
|
967
|
+
};
|
|
968
|
+
if (checkObjectType(field.type), Array.isArray(field.of))
|
|
969
|
+
for (const item of field.of)
|
|
970
|
+
checkObjectType(item?.type);
|
|
971
|
+
}
|
|
972
|
+
return max;
|
|
973
|
+
}
|
|
974
|
+
function formatRefsWithCounts(doc, referenceCount) {
|
|
975
|
+
const counts = referenceCount ?? {}, outgoing = [...doc.outgoingReferencePaths ?? []].sort((a, b) => `${a.path}->${a.type}`.localeCompare(`${b.path}->${b.type}`)).map((r) => {
|
|
976
|
+
const pathKey = `${r.path}->${r.type}`, refCount = counts[doc.name]?.outgoing?.[pathKey];
|
|
977
|
+
return typeof refCount != "number" || refCount <= 0 ? null : `${pathKey}${formatReferenceCount(refCount)}`;
|
|
978
|
+
}).filter(Boolean).join(", ") || "";
|
|
979
|
+
if (doc.incomingReferences.length === 0) return { outgoing, incoming: "" };
|
|
980
|
+
const incomingParts = [];
|
|
981
|
+
for (const sourceType of [...doc.incomingReferences].sort()) {
|
|
982
|
+
const sourceCounts = counts[sourceType];
|
|
983
|
+
let total = 0;
|
|
984
|
+
if (sourceCounts?.outgoing) {
|
|
985
|
+
const suffix = `->${doc.name}`;
|
|
986
|
+
for (const [pathKey, pathCount] of Object.entries(sourceCounts.outgoing))
|
|
987
|
+
pathKey.endsWith(suffix) && typeof pathCount == "number" && pathCount > 0 && (total += pathCount);
|
|
988
|
+
}
|
|
989
|
+
total > 0 && incomingParts.push(`${sourceType}${formatReferenceCount(total)}`);
|
|
990
|
+
}
|
|
991
|
+
return { outgoing, incoming: incomingParts.join(", ") };
|
|
992
|
+
}
|
|
993
|
+
function formatRefsSimple(doc) {
|
|
994
|
+
return {
|
|
995
|
+
outgoing: doc.outgoingReferences.length > 0 ? [...doc.outgoingReferences].sort().join(", ") : "",
|
|
996
|
+
incoming: doc.incomingReferences.length > 0 ? [...doc.incomingReferences].sort().join(", ") : ""
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function formatDocEntry(name, doc, objectTypes, fieldOptions, datasetStats) {
|
|
1000
|
+
if (shouldSkipType(name)) return null;
|
|
1001
|
+
const { documentCount, referenceCount } = datasetStats ?? {}, hasDocumentCounts = !!documentCount, count = documentCount?.[name] ?? 0;
|
|
1002
|
+
if (documentCount && documentCount[name] === 0) return null;
|
|
1003
|
+
const useCount = formatDocumentCount(count, hasDocumentCounts);
|
|
1004
|
+
if (doc.deprecated)
|
|
1005
|
+
return `- ${name} (DEPRECATED: "${doc.deprecated.reason}", count: ${useCount})`;
|
|
1006
|
+
const refs = referenceCount ? formatRefsWithCounts(doc, referenceCount) : formatRefsSimple(doc), depth = calculateMaxDepth(doc.fields, objectTypes), fieldNames = formatFieldNamesWithTypes(doc.fields, fieldOptions), indent = " ", parts = [`- ${name} (title: ${doc.titleField}, depth: ${depth}, count: ${useCount})`];
|
|
1007
|
+
if (doc.description) {
|
|
1008
|
+
const maxLen = SCHEMA_CONSTANTS.DEFAULTS.maxDocDescriptionLength, desc = doc.description.length > maxLen ? `${doc.description.slice(0, maxLen)}...` : doc.description;
|
|
1009
|
+
parts.push(`${indent}- description: ${desc}`);
|
|
1010
|
+
}
|
|
1011
|
+
return parts.push(`${indent}- fields: ${fieldNames}`), refs.outgoing && parts.push(`${indent}- references: ${refs.outgoing}`), refs.incoming && parts.push(`${indent}- referenced by: ${refs.incoming}`), parts.join(`
|
|
1012
|
+
`);
|
|
1013
|
+
}
|
|
1014
|
+
function formatCompressedSchema(documentTypes, objectTypes, fieldOptions, datasetStats) {
|
|
1015
|
+
return [...documentTypes.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([name, doc]) => formatDocEntry(name, doc, objectTypes, fieldOptions, datasetStats)).filter(Boolean).join(`
|
|
1016
|
+
`);
|
|
1017
|
+
}
|
|
1018
|
+
async function compressedSchema(types, options = {}) {
|
|
1019
|
+
const includeTypes = await resolveFilter(types, options.filter), { documentTypes, objectTypes } = processSchema(types, {
|
|
1020
|
+
filter: includeTypes
|
|
1021
|
+
});
|
|
1022
|
+
return formatCompressedSchema(
|
|
1023
|
+
documentTypes,
|
|
1024
|
+
objectTypes,
|
|
1025
|
+
options.fields ?? {},
|
|
1026
|
+
options.datasetStats
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
export {
|
|
1030
|
+
ARRAY_ITEM_SYSTEM_FIELDS,
|
|
1031
|
+
BUILT_IN_TYPE_FIELDS,
|
|
1032
|
+
DEFAULT_LINK_ANNOTATION,
|
|
1033
|
+
DatasetStatsSchema,
|
|
1034
|
+
SCHEMA_CONSTANTS,
|
|
1035
|
+
compressedSchema,
|
|
1036
|
+
fetchDatasetStats,
|
|
1037
|
+
fetchSchemaByDescriptorId,
|
|
1038
|
+
filterToPath,
|
|
1039
|
+
fullSchema,
|
|
1040
|
+
isBuiltInType,
|
|
1041
|
+
isGenericObject,
|
|
1042
|
+
isPrimitiveType,
|
|
1043
|
+
resolveFieldSchema as navigateToFieldSchema,
|
|
1044
|
+
resolveFieldSchemaAllUnions as navigateToFieldSchemaAllUnions,
|
|
1045
|
+
processSchema,
|
|
1046
|
+
resolveFieldSchema,
|
|
1047
|
+
resolveFieldSchemaAllUnions,
|
|
1048
|
+
shouldSkipType,
|
|
1049
|
+
toSchemaPath
|
|
1050
|
+
};
|
|
1051
|
+
//# sourceMappingURL=index.js.map
|