@sanity/schema 5.0.0-next-major.20251216093854 → 5.0.0-next-major.20251216101132

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.
@@ -233,6 +233,19 @@ export declare class DescriptorConverter {
233
233
  ): Promise<SetSynchronization<RegistryType>>
234
234
  }
235
235
 
236
+ /**
237
+ * Extracts a GROQ-compatible schema from a Sanity schema definition. The extraction happens in three passes:
238
+ *
239
+ * 1. **Dependency analysis & hoisting detection** (`sortByDependencies`): Walks the entire schema to sort
240
+ * types topologically and identifies inline object fields that are used multiple times (candidates
241
+ * for "hoisting").
242
+ *
243
+ * 2. **Hoisted type creation**: For any repeated inline fields, we create top-level named type definitions
244
+ * first, so they exist before being referenced.
245
+ *
246
+ * 3. **Main type conversion**: Processes each schema type in dependency order. When a field was marked for
247
+ * hoisting, we emit an `inline` reference to the hoisted type instead of duplicating the structure.
248
+ */
236
249
  export declare function extractSchema(
237
250
  schemaDef: Schema,
238
251
  extractOptions?: ExtractSchemaOptions,
package/lib/_internal.js CHANGED
@@ -2314,15 +2314,32 @@ const documentDefaultFields = (typeName) => ({
2314
2314
  ["email", { type: "string" }]
2315
2315
  ]);
2316
2316
  function extractSchema(schemaDef, extractOptions = {}) {
2317
- const inlineFields = /* @__PURE__ */ new Set(), documentTypes = /* @__PURE__ */ new Map(), schema = [], generatedTypes = /* @__PURE__ */ new Map(), sortedSchemaTypeNames = sortByDependencies(schemaDef);
2318
- sortedSchemaTypeNames.forEach((typeName) => {
2317
+ const inlineFields = /* @__PURE__ */ new Set(), documentTypes = /* @__PURE__ */ new Map(), schema = [], generatedTypes = /* @__PURE__ */ new Map(), { sortedSchemaTypeNames, repeated } = sortByDependencies(schemaDef);
2318
+ repeated.forEach((key, objectField) => {
2319
+ const base = convertSchemaType(objectField.type);
2320
+ if (base !== null) {
2321
+ if (base.type === "inline") {
2322
+ repeated.delete(objectField);
2323
+ return;
2324
+ }
2325
+ if (base.type === "unknown") {
2326
+ repeated.delete(objectField);
2327
+ return;
2328
+ }
2329
+ schema.push({
2330
+ type: "type",
2331
+ name: getGeneratedTypeName(key),
2332
+ value: base
2333
+ });
2334
+ }
2335
+ }), sortedSchemaTypeNames.forEach((typeName) => {
2319
2336
  const schemaType = schemaDef.get(typeName);
2320
2337
  if (schemaType === void 0)
2321
2338
  return;
2322
2339
  const base = convertBaseType(schemaType);
2323
2340
  base !== null && (base.type === "type" && inlineFields.add(schemaType), base.type === "document" && documentTypes.set(typeName, base), schema.push(base));
2324
2341
  });
2325
- function getGeneratedTypeName(typeName, suffix) {
2342
+ function getGeneratedTypeName(typeName, suffix = "") {
2326
2343
  const name = generatedTypes.get(typeName);
2327
2344
  if (name) return name;
2328
2345
  for (let i = 0; i < 5; i++) {
@@ -2395,10 +2412,19 @@ function extractSchema(schemaDef, extractOptions = {}) {
2395
2412
  function createObject(schemaType) {
2396
2413
  const attributes = {}, fields = gatherFields(schemaType);
2397
2414
  for (const field of fields) {
2398
- const fieldIsRequired = isFieldRequired(field?.type?.validation), value = convertSchemaType(field.type);
2399
- if (value === null)
2400
- continue;
2401
- hasAssetRequired(field?.type?.validation) && value.type === "object" && (value.attributes.asset.optional = !1);
2415
+ const fieldIsRequired = isFieldRequired(field?.type?.validation);
2416
+ let value;
2417
+ const hoisted = repeated.get(field), isTopLevelSchemaType = sortedSchemaTypeNames.includes(field.type.name);
2418
+ if (hoisted && !isTopLevelSchemaType)
2419
+ value = {
2420
+ type: "inline",
2421
+ name: getGeneratedTypeName(hoisted)
2422
+ };
2423
+ else {
2424
+ if (value = convertSchemaType(field.type), value === null)
2425
+ continue;
2426
+ hasAssetRequired(field?.type?.validation) && value.type === "object" && (value.attributes.asset.optional = !1);
2427
+ }
2402
2428
  const optional = extractOptions.enforceRequiredFields ? fieldIsRequired === !1 : !0;
2403
2429
  attributes[field.name] = {
2404
2430
  type: "objectAttribute",
@@ -2576,8 +2602,16 @@ function lastType(typeDef) {
2576
2602
  }
2577
2603
  }
2578
2604
  function sortByDependencies(compiledSchema) {
2579
- const seen = /* @__PURE__ */ new Set();
2580
- function walkDependencies(schemaType, dependencies) {
2605
+ const seen = /* @__PURE__ */ new Set(), objectMap = /* @__PURE__ */ new Set(), repeated = /* @__PURE__ */ new Map(), repeatedNames = /* @__PURE__ */ new Set();
2606
+ function pickRepeatedName(path) {
2607
+ for (let idx = path.length - 1; idx >= 0; idx--) {
2608
+ const name = path.slice(idx).join(".");
2609
+ if (!repeatedNames.has(name))
2610
+ return repeatedNames.add(name), name;
2611
+ }
2612
+ throw new Error(`Unable to pick repeated name: ${path.join(".")}`);
2613
+ }
2614
+ function walkDependencies(schemaType, dependencies, path, hoistRepetitions = !0) {
2581
2615
  if (!seen.has(schemaType)) {
2582
2616
  if (seen.add(schemaType), "fields" in schemaType)
2583
2617
  for (const field of gatherFields(schemaType)) {
@@ -2587,11 +2621,19 @@ function sortByDependencies(compiledSchema) {
2587
2621
  continue;
2588
2622
  }
2589
2623
  let schemaTypeName;
2590
- schemaType.type.type ? schemaTypeName = field.type.type.name : "jsonType" in schemaType.type && (schemaTypeName = field.type.jsonType), schemaTypeName === "object" || schemaTypeName === "block" ? isReferenceType(field.type) ? field.type.to.forEach((ref) => dependencies.add(ref.type)) : dependencies.add(field.type) : field.type && dependencies.add(field.type), walkDependencies(field.type, dependencies);
2624
+ if (schemaType.type.type ? schemaTypeName = field.type.type.name : "jsonType" in schemaType.type && (schemaTypeName = field.type.jsonType), schemaTypeName === "object" || schemaTypeName === "block") {
2625
+ if (isReferenceType(field.type))
2626
+ field.type.to.forEach((ref) => dependencies.add(ref.type));
2627
+ else if (dependencies.add(field.type), hoistRepetitions && !validSchemaNames.has(field.type.name)) {
2628
+ const fieldPath = path.concat([field.name]);
2629
+ !repeated.has(field) && objectMap.has(field) && repeated.set(field, pickRepeatedName(fieldPath)), objectMap.add(field);
2630
+ }
2631
+ } else field.type && dependencies.add(field.type);
2632
+ walkDependencies(field.type, dependencies, path.concat([field.name]));
2591
2633
  }
2592
2634
  else if ("of" in schemaType)
2593
2635
  for (const item of schemaType.of)
2594
- walkDependencies(item, dependencies);
2636
+ walkDependencies(item, dependencies, path.concat(item.name), !isReferenceType(schemaType));
2595
2637
  }
2596
2638
  }
2597
2639
  const dependencyMap = /* @__PURE__ */ new Map(), schemaTypeNames = compiledSchema.getTypeNames(), validSchemaNames = /* @__PURE__ */ new Set();
@@ -2601,7 +2643,7 @@ function sortByDependencies(compiledSchema) {
2601
2643
  return;
2602
2644
  validSchemaNames.add(typeName);
2603
2645
  const dependencies = /* @__PURE__ */ new Set();
2604
- walkDependencies(schemaType, dependencies), dependencyMap.set(schemaType, dependencies), seen.clear();
2646
+ walkDependencies(schemaType, dependencies, [typeName]), dependencyMap.set(schemaType, dependencies), seen.clear();
2605
2647
  });
2606
2648
  const typeNames = [], currentlyVisiting = /* @__PURE__ */ new Set(), visited = /* @__PURE__ */ new Set();
2607
2649
  function visit(type) {
@@ -2613,7 +2655,10 @@ function sortByDependencies(compiledSchema) {
2613
2655
  }
2614
2656
  for (const [type] of dependencyMap)
2615
2657
  visit(type);
2616
- return typeNames.filter((typeName) => validSchemaNames.has(typeName));
2658
+ return {
2659
+ sortedSchemaTypeNames: typeNames.filter((typeName) => validSchemaNames.has(typeName)),
2660
+ repeated
2661
+ };
2617
2662
  }
2618
2663
  function validateNoCallbacks(typeDef) {
2619
2664
  const problems = [];