@tinacms/schema-tools 0.0.0-e0ddb8c-20241004065742 → 0.0.0-e27c017-20250619233313

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.mjs CHANGED
@@ -2,42 +2,26 @@ import * as yup from "yup";
2
2
  import UrlPattern from "url-pattern";
3
3
  import z$1, { z, ZodError } from "zod";
4
4
  function addNamespaceToSchema(maybeNode, namespace = []) {
5
- if (typeof maybeNode === "string") {
5
+ if (typeof maybeNode !== "object" || maybeNode === null) {
6
6
  return maybeNode;
7
7
  }
8
- if (typeof maybeNode === "boolean") {
9
- return maybeNode;
10
- }
11
- if (typeof maybeNode === "function") {
12
- return maybeNode;
13
- }
14
- const newNode = { ...maybeNode };
15
- const keys = Object.keys(maybeNode);
16
- Object.values(maybeNode).map((m, index) => {
17
- const key = keys[index];
18
- if (Array.isArray(m)) {
19
- newNode[key] = m.map((element) => {
20
- if (!element) {
21
- return;
22
- }
23
- if (!element.hasOwnProperty("name")) {
24
- return element;
8
+ const newNode = { ...maybeNode, namespace: [...namespace] };
9
+ Object.entries(maybeNode).forEach(([key, value]) => {
10
+ if (Array.isArray(value)) {
11
+ newNode[key] = value.map((element) => {
12
+ if (element && typeof element === "object" && "name" in element) {
13
+ const valueName = element.name || element.value;
14
+ return addNamespaceToSchema(element, [...namespace, valueName]);
25
15
  }
26
- const value = element.name || element.value;
27
- return addNamespaceToSchema(element, [...namespace, value]);
16
+ return element;
28
17
  });
18
+ } else if (value && typeof value === "object" && "name" in value) {
19
+ newNode[key] = addNamespaceToSchema(value, [...namespace, value.name]);
29
20
  } else {
30
- if (!m) {
31
- return;
32
- }
33
- if (!m.hasOwnProperty("name")) {
34
- newNode[key] = m;
35
- } else {
36
- newNode[key] = addNamespaceToSchema(m, [...namespace, m.name]);
37
- }
21
+ newNode[key] = value;
38
22
  }
39
23
  });
40
- return { ...newNode, namespace };
24
+ return newNode;
41
25
  }
42
26
  function getDefaultExportFromCjs(x) {
43
27
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -1662,6 +1646,9 @@ const parseURL = (url) => {
1662
1646
  };
1663
1647
  };
1664
1648
  const normalizePath = (filepath) => filepath.replace(/\\/g, "/");
1649
+ const canonicalPath = (filepath) => {
1650
+ return normalizePath(filepath).split("/").filter((name2) => name2 !== "").join("/");
1651
+ };
1665
1652
  class TinaSchema {
1666
1653
  /**
1667
1654
  * Create a schema class from a user defined schema object
@@ -1711,21 +1698,21 @@ class TinaSchema {
1711
1698
  };
1712
1699
  this.getCollectionByFullPath = (filepath) => {
1713
1700
  const fileExtension = filepath.split(".").pop();
1714
- const normalizedPath = filepath.replace(/\\/g, "/");
1701
+ const canonicalFilepath = canonicalPath(filepath);
1715
1702
  const possibleCollections = this.getCollections().filter((collection) => {
1716
1703
  var _a, _b;
1717
- if (!normalizedPath.endsWith(`.gitkeep.${collection.format || "md"}`) && fileExtension !== (collection.format || "md")) {
1704
+ if (!canonicalFilepath.endsWith(`.gitkeep.${collection.format || "md"}`) && fileExtension !== (collection.format || "md")) {
1718
1705
  return false;
1719
1706
  }
1720
1707
  if (((_a = collection == null ? void 0 : collection.match) == null ? void 0 : _a.include) || ((_b = collection == null ? void 0 : collection.match) == null ? void 0 : _b.exclude)) {
1721
1708
  const matches = this.getMatches({ collection });
1722
- const match = picomatch$1.isMatch(normalizedPath, matches);
1709
+ const match = picomatch$1.isMatch(canonicalFilepath, matches);
1723
1710
  if (!match) {
1724
1711
  return false;
1725
1712
  }
1726
1713
  }
1727
- const path = collection.path ? collection.path.replace(/\/?$/, "/") : "";
1728
- return normalizedPath.startsWith(path);
1714
+ const collectionPath = canonicalPath(collection.path);
1715
+ return collectionPath === "" || canonicalFilepath.startsWith(`${collectionPath}/`);
1729
1716
  });
1730
1717
  if (possibleCollections.length === 0) {
1731
1718
  throw new Error(`Unable to find collection for file at ${filepath}`);
@@ -1943,7 +1930,7 @@ class TinaSchema {
1943
1930
  }
1944
1931
  }
1945
1932
  };
1946
- this.walkFields = (cb) => {
1933
+ this.legacyWalkFields = (cb) => {
1947
1934
  const walk = (collectionOrObject, collection, path) => {
1948
1935
  if (collectionOrObject.templates) {
1949
1936
  collectionOrObject.templates.forEach((template) => {
@@ -1965,7 +1952,7 @@ class TinaSchema {
1965
1952
  collections.forEach((collection) => walk(collection, collection, []));
1966
1953
  };
1967
1954
  this.schema = config;
1968
- this.walkFields(({ field, collection, path }) => {
1955
+ this.legacyWalkFields(({ field, collection }) => {
1969
1956
  if (!("searchable" in field)) {
1970
1957
  if (field.type === "image") {
1971
1958
  field.searchable = false;
@@ -1986,6 +1973,67 @@ class TinaSchema {
1986
1973
  field.uid = field.uid || false;
1987
1974
  });
1988
1975
  }
1976
+ findReferencesFromCollection(name2) {
1977
+ const result = {};
1978
+ this.walkFields(({ field, collection: c, path }) => {
1979
+ if (c.name !== name2) {
1980
+ return;
1981
+ }
1982
+ if (field.type === "reference") {
1983
+ field.collections.forEach((name22) => {
1984
+ if (result[name22] === void 0) {
1985
+ result[name22] = [];
1986
+ }
1987
+ result[name22].push(path);
1988
+ });
1989
+ }
1990
+ });
1991
+ return result;
1992
+ }
1993
+ /**
1994
+ * Walk all fields in tina schema
1995
+ *
1996
+ * @param cb callback function invoked for each field
1997
+ */
1998
+ walkFields(cb) {
1999
+ const walk = (collectionOrObject, collection, path = "$") => {
2000
+ if (collectionOrObject.templates) {
2001
+ collectionOrObject.templates.forEach((template) => {
2002
+ const templatePath = `${path}.${template.name}`;
2003
+ template.fields.forEach((field) => {
2004
+ const fieldPath = field.list ? `${templatePath}[*].${field.name}` : `${templatePath}.${field.name}`;
2005
+ cb({ field, collection, path: fieldPath });
2006
+ if (field.type === "object") {
2007
+ walk(field, collection, fieldPath);
2008
+ }
2009
+ });
2010
+ });
2011
+ }
2012
+ if (collectionOrObject.fields) {
2013
+ collectionOrObject.fields.forEach((field) => {
2014
+ const fieldPath = field.list ? `${path}.${field.name}[*]` : `${path}.${field.name}`;
2015
+ cb({ field, collection, path: fieldPath });
2016
+ if (field.type === "object" && field.fields) {
2017
+ walk(field, collection, fieldPath);
2018
+ } else if (field.templates) {
2019
+ field.templates.forEach((template) => {
2020
+ const templatePath = `${fieldPath}.${template.name}`;
2021
+ template.fields.forEach((field2) => {
2022
+ const fieldPath2 = field2.list ? `${templatePath}[*].${field2.name}` : `${templatePath}.${field2.name}`;
2023
+ cb({ field: field2, collection, path: fieldPath2 });
2024
+ if (field2.type === "object") {
2025
+ walk(field2, collection, fieldPath2);
2026
+ }
2027
+ });
2028
+ });
2029
+ }
2030
+ });
2031
+ }
2032
+ };
2033
+ this.getCollections().forEach((collection) => {
2034
+ walk(collection, collection);
2035
+ });
2036
+ }
1989
2037
  /**
1990
2038
  * This function returns an array of glob matches for a given collection.
1991
2039
  *
@@ -1997,16 +2045,16 @@ class TinaSchema {
1997
2045
  }) {
1998
2046
  var _a, _b;
1999
2047
  const collection = typeof collectionOrString === "string" ? this.getCollection(collectionOrString) : collectionOrString;
2000
- const normalPath = normalizePath(collection.path);
2001
- const pathSuffix = normalPath ? "/" : "";
2048
+ const collectionPath = canonicalPath(collection.path);
2049
+ const pathSuffix = collectionPath ? "/" : "";
2002
2050
  const format = collection.format || "md";
2003
2051
  const matches = [];
2004
2052
  if ((_a = collection == null ? void 0 : collection.match) == null ? void 0 : _a.include) {
2005
- const match = `${normalPath}${pathSuffix}${collection.match.include}.${format}`;
2053
+ const match = `${collectionPath}${pathSuffix}${collection.match.include}.${format}`;
2006
2054
  matches.push(match);
2007
2055
  }
2008
2056
  if ((_b = collection == null ? void 0 : collection.match) == null ? void 0 : _b.exclude) {
2009
- const exclude = `!(${normalPath}${pathSuffix}${collection.match.exclude}.${format})`;
2057
+ const exclude = `!(${collectionPath}${pathSuffix}${collection.match.exclude}.${format})`;
2010
2058
  matches.push(exclude);
2011
2059
  }
2012
2060
  return matches;
@@ -2199,6 +2247,15 @@ const resolveForm = ({
2199
2247
  })
2200
2248
  };
2201
2249
  };
2250
+ const CONTENT_FORMATS = [
2251
+ "mdx",
2252
+ "md",
2253
+ "markdown",
2254
+ "json",
2255
+ "yaml",
2256
+ "yml",
2257
+ "toml"
2258
+ ];
2202
2259
  const parseZodError = ({ zodError }) => {
2203
2260
  var _a, _b, _c, _d;
2204
2261
  const errors = zodError.flatten((issue) => {
@@ -2208,9 +2265,12 @@ const parseZodError = ({ zodError }) => {
2208
2265
  moreInfo.push(parseZodError({ zodError: unionError }));
2209
2266
  });
2210
2267
  }
2211
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
2268
+ const errorMessage = `${issue == null ? void 0 : issue.message}
2269
+ Additional information:
2270
+ - Error found at path ${issue.path.join(
2212
2271
  "."
2213
- )}`;
2272
+ )}
2273
+ `;
2214
2274
  const errorMessages = [errorMessage, ...moreInfo];
2215
2275
  return {
2216
2276
  errors: errorMessages
@@ -2245,6 +2305,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2245
2305
  });
2246
2306
  }
2247
2307
  });
2308
+ const duplicateFieldErrorMessage = (fields) => `Fields must have unique names. Found duplicate field names: [${fields}]`;
2309
+ const duplicateTemplateErrorMessage = (templates) => `Templates must have unique names. Found duplicate template names: [${templates}]`;
2310
+ const duplicateCollectionErrorMessage = (collection) => `Collections must have unique names. Found duplicate collection names: [${collection}]`;
2248
2311
  const TypeName = [
2249
2312
  "string",
2250
2313
  "boolean",
@@ -2255,10 +2318,11 @@ const TypeName = [
2255
2318
  "reference",
2256
2319
  "rich-text"
2257
2320
  ];
2258
- const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
2259
- const typeRequiredError = `type is required and must be one of ${TypeName.join(
2260
- ", "
2261
- )}`;
2321
+ const formattedTypes = ` - ${TypeName.join("\n - ")}`;
2322
+ const typeTypeError = `Invalid \`type\` property. \`type\` expected to be one of the following values:
2323
+ ${formattedTypes}`;
2324
+ const typeRequiredError = `Missing \`type\` property. Please add a \`type\` property with one of the following:
2325
+ ${formattedTypes}`;
2262
2326
  const Option = z.union(
2263
2327
  [
2264
2328
  z.string(),
@@ -2344,7 +2408,7 @@ const TinaFieldZod = z.lazy(() => {
2344
2408
  if (dups) {
2345
2409
  ctx.addIssue({
2346
2410
  code: z.ZodIssueCode.custom,
2347
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2411
+ message: duplicateFieldErrorMessage(dups)
2348
2412
  });
2349
2413
  }
2350
2414
  });
@@ -2354,21 +2418,21 @@ const TinaFieldZod = z.lazy(() => {
2354
2418
  invalid_type_error: typeTypeError,
2355
2419
  required_error: typeRequiredError
2356
2420
  }),
2357
- fields: z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2421
+ fields: z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2358
2422
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2359
2423
  if (dups) {
2360
2424
  ctx.addIssue({
2361
2425
  code: z.ZodIssueCode.custom,
2362
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2426
+ message: duplicateFieldErrorMessage(dups)
2363
2427
  });
2364
2428
  }
2365
2429
  }),
2366
- templates: z.array(TemplateTemp).min(1).optional().superRefine((val, ctx) => {
2430
+ templates: z.array(TemplateTemp).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2367
2431
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2368
2432
  if (dups) {
2369
2433
  ctx.addIssue({
2370
2434
  code: z.ZodIssueCode.custom,
2371
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2435
+ message: duplicateTemplateErrorMessage(dups)
2372
2436
  });
2373
2437
  }
2374
2438
  })
@@ -2383,7 +2447,7 @@ const TinaFieldZod = z.lazy(() => {
2383
2447
  if (dups) {
2384
2448
  ctx.addIssue({
2385
2449
  code: z.ZodIssueCode.custom,
2386
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2450
+ message: duplicateTemplateErrorMessage(dups)
2387
2451
  });
2388
2452
  }
2389
2453
  })
@@ -2403,10 +2467,17 @@ const TinaFieldZod = z.lazy(() => {
2403
2467
  ],
2404
2468
  {
2405
2469
  errorMap: (issue, ctx) => {
2406
- var _a;
2470
+ var _a, _b;
2407
2471
  if (issue.code === "invalid_union_discriminator") {
2472
+ if (!((_a = ctx.data) == null ? void 0 : _a.type)) {
2473
+ return {
2474
+ message: `Missing \`type\` property in field \`${ctx.data.name}\`. Please add a \`type\` property with one of the following:
2475
+ ${formattedTypes}`
2476
+ };
2477
+ }
2408
2478
  return {
2409
- message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
2479
+ message: `Invalid \`type\` property in field \`${ctx.data.name}\`. In the schema is 'type: ${(_b = ctx.data) == null ? void 0 : _b.type}' but expected one of the following:
2480
+ ${formattedTypes}`
2410
2481
  };
2411
2482
  }
2412
2483
  return {
@@ -2416,77 +2487,52 @@ const TinaFieldZod = z.lazy(() => {
2416
2487
  }
2417
2488
  ).superRefine((val, ctx) => {
2418
2489
  if (val.type === "string") {
2419
- if (val.isTitle) {
2420
- if (val.list) {
2421
- ctx.addIssue({
2422
- code: z.ZodIssueCode.custom,
2423
- message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
2424
- ${JSON.stringify(
2425
- val,
2426
- null,
2427
- 2
2428
- )}
2429
- `
2430
- });
2431
- }
2432
- if (!val.required) {
2433
- ctx.addIssue({
2434
- code: z.ZodIssueCode.custom,
2435
- message: `Must have { required: true } when using \`isTitle\` Error in value
2436
- ${JSON.stringify(
2437
- val,
2438
- null,
2439
- 2
2440
- )}
2441
- `
2442
- });
2443
- }
2490
+ const stringifiedField = JSON.stringify(val, null, 2);
2491
+ if (val.isTitle && val.list) {
2492
+ ctx.addIssue({
2493
+ code: z.ZodIssueCode.custom,
2494
+ message: `\`list: true\` is not allowed when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2495
+ ${stringifiedField}`
2496
+ });
2497
+ }
2498
+ if (val.isTitle && !val.required) {
2499
+ ctx.addIssue({
2500
+ code: z.ZodIssueCode.custom,
2501
+ message: `Property \`required: true\` is required when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2502
+ ${stringifiedField}`
2503
+ });
2444
2504
  }
2445
- if (val.uid) {
2505
+ if (val.uid && val.list) {
2446
2506
  if (val.list) {
2447
2507
  ctx.addIssue({
2448
2508
  code: z.ZodIssueCode.custom,
2449
- message: `Can not have \`list: true\` when using \`uid\`. Error in value
2450
- ${JSON.stringify(
2451
- val,
2452
- null,
2453
- 2
2454
- )}
2455
- `
2456
- });
2457
- }
2458
- if (!val.required) {
2459
- ctx.addIssue({
2460
- code: z.ZodIssueCode.custom,
2461
- message: `Must have { required: true } when using \`uid\` Error in value
2462
- ${JSON.stringify(
2463
- val,
2464
- null,
2465
- 2
2466
- )}
2467
- `
2509
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2510
+ ${stringifiedField}`
2468
2511
  });
2469
2512
  }
2470
2513
  }
2514
+ if (val.uid && !val.required) {
2515
+ ctx.addIssue({
2516
+ code: z.ZodIssueCode.custom,
2517
+ message: `Property \`required: true\` is required when using \`uid\` for fields of \`type: string\`. Error found in field:
2518
+ ${stringifiedField}`
2519
+ });
2520
+ }
2471
2521
  }
2472
2522
  if (val.type === "object") {
2473
- const message = "Must provide one of templates or fields in your collection";
2474
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2475
- if (!isValid) {
2523
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2476
2524
  ctx.addIssue({
2477
2525
  code: z.ZodIssueCode.custom,
2478
- message
2526
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2527
+ });
2528
+ return false;
2529
+ }
2530
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2531
+ ctx.addIssue({
2532
+ code: z.ZodIssueCode.custom,
2533
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2479
2534
  });
2480
2535
  return false;
2481
- } else {
2482
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2483
- if (!isValid) {
2484
- ctx.addIssue({
2485
- code: z.ZodIssueCode.custom,
2486
- message
2487
- });
2488
- }
2489
- return isValid;
2490
2536
  }
2491
2537
  }
2492
2538
  return true;
@@ -2519,19 +2565,10 @@ const validateTinaCloudSchemaConfig = (config) => {
2519
2565
  const newConfig = tinaConfigZod.parse(config);
2520
2566
  return newConfig;
2521
2567
  };
2522
- const FORMATS = [
2523
- "json",
2524
- "md",
2525
- "markdown",
2526
- "mdx",
2527
- "toml",
2528
- "yaml",
2529
- "yml"
2530
- ];
2531
2568
  const Template = z.object({
2532
2569
  label: z.string({
2533
- invalid_type_error: "label must be a string",
2534
- required_error: "label was not provided but is required"
2570
+ invalid_type_error: "Invalid data type for property `label`. Must be of type `string`",
2571
+ required_error: "Missing `label` property. Property `label` is required."
2535
2572
  }),
2536
2573
  name,
2537
2574
  fields: z.array(TinaFieldZod)
@@ -2541,7 +2578,7 @@ const Template = z.object({
2541
2578
  if (dups) {
2542
2579
  ctx.addIssue({
2543
2580
  code: z.ZodIssueCode.custom,
2544
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2581
+ message: duplicateFieldErrorMessage(dups)
2545
2582
  });
2546
2583
  }
2547
2584
  });
@@ -2551,7 +2588,7 @@ const CollectionBaseSchema = z.object({
2551
2588
  if (val === "relativePath") {
2552
2589
  ctx.addIssue({
2553
2590
  code: z.ZodIssueCode.custom,
2554
- message: `name cannot be 'relativePath'. 'relativePath' is a reserved field name.`
2591
+ message: "Invalid `name` property. `name` cannot be 'relativePath' as it is a reserved field name."
2555
2592
  });
2556
2593
  }
2557
2594
  }),
@@ -2559,72 +2596,73 @@ const CollectionBaseSchema = z.object({
2559
2596
  if (val === ".") {
2560
2597
  ctx.addIssue({
2561
2598
  code: z.ZodIssueCode.custom,
2562
- message: `path cannot be '.'. Please use '/' or '' instead. `
2599
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2563
2600
  });
2564
2601
  }
2565
2602
  }),
2566
- format: z.enum(FORMATS).optional(),
2603
+ format: z.enum(CONTENT_FORMATS).optional(),
2567
2604
  isAuthCollection: z.boolean().optional(),
2568
2605
  isDetached: z.boolean().optional()
2569
2606
  });
2570
2607
  const TinaCloudCollection = CollectionBaseSchema.extend({
2571
- fields: z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2608
+ fields: z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2572
2609
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2573
2610
  if (dups) {
2574
2611
  ctx.addIssue({
2575
2612
  code: z.ZodIssueCode.custom,
2576
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2613
+ message: duplicateFieldErrorMessage(dups)
2577
2614
  });
2578
2615
  }
2579
- }).refine(
2580
- // It is valid if it is 0 or 1
2581
- (val) => {
2582
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2583
- return arr.length < 2;
2584
- },
2585
- {
2586
- message: "Fields can only have one use of `isTitle`"
2587
- }
2588
- ).refine(
2589
- // It is valid if it is 0 or 1
2590
- (val) => {
2591
- const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2592
- return arr.length < 2;
2593
- },
2594
- {
2595
- message: "Fields can only have one use of `uid`"
2596
- }
2597
- ).refine(
2598
- // It is valid if it is 0 or 1
2599
- (val) => {
2600
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2601
- return arr.length < 2;
2602
- },
2603
- {
2604
- message: "Fields can only have one use of `password` type"
2616
+ }).superRefine((val, ctx) => {
2617
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2618
+ if (arr.length > 1) {
2619
+ ctx.addIssue({
2620
+ code: z.ZodIssueCode.custom,
2621
+ message: `The following fields have the property \`isTitle\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`isTitle\`.`
2622
+ });
2623
+ }
2624
+ }).superRefine((val, ctx) => {
2625
+ const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2626
+ if (arr.length > 2) {
2627
+ ctx.addIssue({
2628
+ code: z.ZodIssueCode.custom,
2629
+ message: `The following fields have the property \`uid\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`uid\`.`
2630
+ });
2605
2631
  }
2606
- ),
2607
- templates: z.array(Template).min(1).optional().superRefine((val, ctx) => {
2632
+ }).superRefine((val, ctx) => {
2633
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2634
+ if (arr.length > 2) {
2635
+ ctx.addIssue({
2636
+ code: z.ZodIssueCode.custom,
2637
+ message: `The following fields have \`type: password\`: [${arr.map((field) => field.name).join(", ")}]. Only one can be of \`type: password\`.`
2638
+ });
2639
+ }
2640
+ }),
2641
+ templates: z.array(Template).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2608
2642
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2609
2643
  if (dups) {
2610
2644
  ctx.addIssue({
2611
2645
  code: z.ZodIssueCode.custom,
2612
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2646
+ message: duplicateFieldErrorMessage(dups)
2613
2647
  });
2614
2648
  }
2615
2649
  })
2616
- }).refine(
2617
- (val) => {
2618
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2619
- if (!isValid) {
2620
- return false;
2621
- } else {
2622
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2623
- return isValid;
2624
- }
2625
- },
2626
- { message: "Must provide one of templates or fields in your collection" }
2627
- );
2650
+ }).superRefine((val, ctx) => {
2651
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2652
+ ctx.addIssue({
2653
+ code: z.ZodIssueCode.custom,
2654
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2655
+ });
2656
+ return false;
2657
+ }
2658
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2659
+ ctx.addIssue({
2660
+ code: z.ZodIssueCode.custom,
2661
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2662
+ });
2663
+ return false;
2664
+ }
2665
+ });
2628
2666
  const TinaCloudSchemaZod = z.object({
2629
2667
  collections: z.array(TinaCloudCollection),
2630
2668
  config: tinaConfigZod.optional()
@@ -2634,14 +2672,14 @@ const TinaCloudSchemaZod = z.object({
2634
2672
  if (dups) {
2635
2673
  ctx.addIssue({
2636
2674
  code: z.ZodIssueCode.custom,
2637
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2675
+ message: duplicateCollectionErrorMessage(dups),
2638
2676
  fatal: true
2639
2677
  });
2640
2678
  }
2641
2679
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2642
2680
  ctx.addIssue({
2643
2681
  code: z.ZodIssueCode.custom,
2644
- message: `Only one collection can be marked as isAuthCollection`,
2682
+ message: "Only one collection can be marked as `isAuthCollection`.",
2645
2683
  fatal: true
2646
2684
  });
2647
2685
  }
@@ -2649,7 +2687,7 @@ const TinaCloudSchemaZod = z.object({
2649
2687
  if (media && media.tina && media.loadCustomStore) {
2650
2688
  ctx.addIssue({
2651
2689
  code: z.ZodIssueCode.custom,
2652
- message: "can not have both loadCustomStore and tina. Must use one or the other",
2690
+ message: "Cannot have both `loadCustomStore` and `tina`. Must use one or the other.",
2653
2691
  fatal: true,
2654
2692
  path: ["config", "media"]
2655
2693
  });
@@ -2658,7 +2696,7 @@ const TinaCloudSchemaZod = z.object({
2658
2696
  if (search && search.tina && search.searchClient) {
2659
2697
  ctx.addIssue({
2660
2698
  code: z.ZodIssueCode.custom,
2661
- message: "can not have both searchClient and tina. Must use one or the other",
2699
+ message: "Cannot have both `searchClient` and `tina`. Must use one or the other.",
2662
2700
  fatal: true,
2663
2701
  path: ["config", "search"]
2664
2702
  });
@@ -2676,17 +2714,19 @@ const validateSchema = ({ schema }) => {
2676
2714
  } catch (e) {
2677
2715
  if (e instanceof ZodError) {
2678
2716
  const errors = parseZodError({ zodError: e });
2679
- throw new TinaSchemaValidationError(errors.join(", \n"));
2717
+ throw new TinaSchemaValidationError(errors.join("\n"));
2680
2718
  }
2681
2719
  throw new Error(e);
2682
2720
  }
2683
2721
  };
2684
2722
  export {
2723
+ CONTENT_FORMATS,
2685
2724
  NAMER,
2686
2725
  TINA_HOST,
2687
2726
  TinaSchema,
2688
2727
  TinaSchemaValidationError,
2689
2728
  addNamespaceToSchema,
2729
+ canonicalPath,
2690
2730
  normalizePath,
2691
2731
  parseURL,
2692
2732
  resolveField,
@@ -28,6 +28,7 @@ export declare class TinaSchema {
28
28
  } & Schema);
29
29
  getIsTitleFieldName: (collection: string) => string;
30
30
  getCollectionsByName: (collectionNames: string[]) => Collection<true>[];
31
+ findReferencesFromCollection(name: string): Record<string, string[]>;
31
32
  getCollection: (collectionName: string) => Collection<true>;
32
33
  getCollections: () => Collection<true>[];
33
34
  getCollectionByFullPath: (filepath: string) => Collection<true>;
@@ -59,7 +60,26 @@ export declare class TinaSchema {
59
60
  *
60
61
  */
61
62
  getTemplatesForCollectable: (collection: Collectable) => CollectionTemplateable;
62
- walkFields: (cb: (args: {
63
+ /**
64
+ * Walk all fields in tina schema
65
+ *
66
+ * @param cb callback function invoked for each field
67
+ */
68
+ walkFields(cb: (args: {
69
+ field: any;
70
+ collection: any;
71
+ path: string;
72
+ isListItem?: boolean;
73
+ }) => void): void;
74
+ /**
75
+ * Walk all fields in Tina Schema
76
+ *
77
+ * This is a legacy version to preserve backwards compatibility for the tina generated schema. It does not
78
+ * traverse fields in object lists in rich-text templates.
79
+ *
80
+ * @param cb callback function invoked for each field
81
+ */
82
+ legacyWalkFields: (cb: (args: {
63
83
  field: TinaField;
64
84
  collection: Collection;
65
85
  path: string[];