@tinacms/schema-tools 0.0.0-d524599-20241115065930 → 0.0.0-d689189-20250526233934

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,20 +1973,67 @@ class TinaSchema {
1986
1973
  field.uid = field.uid || false;
1987
1974
  });
1988
1975
  }
1989
- findReferences(name2) {
1976
+ findReferencesFromCollection(name2) {
1990
1977
  const result = {};
1991
1978
  this.walkFields(({ field, collection: c, path }) => {
1979
+ if (c.name !== name2) {
1980
+ return;
1981
+ }
1992
1982
  if (field.type === "reference") {
1993
- if (field.collections.includes(name2)) {
1994
- if (result[c.name] === void 0) {
1995
- result[c.name] = [];
1983
+ field.collections.forEach((name22) => {
1984
+ if (result[name22] === void 0) {
1985
+ result[name22] = [];
1996
1986
  }
1997
- result[c.name].push({ path, field });
1998
- }
1987
+ result[name22].push(path);
1988
+ });
1999
1989
  }
2000
1990
  });
2001
1991
  return result;
2002
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
+ }
2003
2037
  /**
2004
2038
  * This function returns an array of glob matches for a given collection.
2005
2039
  *
@@ -2011,16 +2045,16 @@ class TinaSchema {
2011
2045
  }) {
2012
2046
  var _a, _b;
2013
2047
  const collection = typeof collectionOrString === "string" ? this.getCollection(collectionOrString) : collectionOrString;
2014
- const normalPath = normalizePath(collection.path);
2015
- const pathSuffix = normalPath ? "/" : "";
2048
+ const collectionPath = canonicalPath(collection.path);
2049
+ const pathSuffix = collectionPath ? "/" : "";
2016
2050
  const format = collection.format || "md";
2017
2051
  const matches = [];
2018
2052
  if ((_a = collection == null ? void 0 : collection.match) == null ? void 0 : _a.include) {
2019
- const match = `${normalPath}${pathSuffix}${collection.match.include}.${format}`;
2053
+ const match = `${collectionPath}${pathSuffix}${collection.match.include}.${format}`;
2020
2054
  matches.push(match);
2021
2055
  }
2022
2056
  if ((_b = collection == null ? void 0 : collection.match) == null ? void 0 : _b.exclude) {
2023
- const exclude = `!(${normalPath}${pathSuffix}${collection.match.exclude}.${format})`;
2057
+ const exclude = `!(${collectionPath}${pathSuffix}${collection.match.exclude}.${format})`;
2024
2058
  matches.push(exclude);
2025
2059
  }
2026
2060
  return matches;
@@ -2213,6 +2247,15 @@ const resolveForm = ({
2213
2247
  })
2214
2248
  };
2215
2249
  };
2250
+ const CONTENT_FORMATS = [
2251
+ "mdx",
2252
+ "md",
2253
+ "markdown",
2254
+ "json",
2255
+ "yaml",
2256
+ "yml",
2257
+ "toml"
2258
+ ];
2216
2259
  const parseZodError = ({ zodError }) => {
2217
2260
  var _a, _b, _c, _d;
2218
2261
  const errors = zodError.flatten((issue) => {
@@ -2222,9 +2265,12 @@ const parseZodError = ({ zodError }) => {
2222
2265
  moreInfo.push(parseZodError({ zodError: unionError }));
2223
2266
  });
2224
2267
  }
2225
- 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(
2226
2271
  "."
2227
- )}`;
2272
+ )}
2273
+ `;
2228
2274
  const errorMessages = [errorMessage, ...moreInfo];
2229
2275
  return {
2230
2276
  errors: errorMessages
@@ -2259,6 +2305,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2259
2305
  });
2260
2306
  }
2261
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}]`;
2262
2311
  const TypeName = [
2263
2312
  "string",
2264
2313
  "boolean",
@@ -2269,10 +2318,11 @@ const TypeName = [
2269
2318
  "reference",
2270
2319
  "rich-text"
2271
2320
  ];
2272
- const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
2273
- const typeRequiredError = `type is required and must be one of ${TypeName.join(
2274
- ", "
2275
- )}`;
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}`;
2276
2326
  const Option = z.union(
2277
2327
  [
2278
2328
  z.string(),
@@ -2358,7 +2408,7 @@ const TinaFieldZod = z.lazy(() => {
2358
2408
  if (dups) {
2359
2409
  ctx.addIssue({
2360
2410
  code: z.ZodIssueCode.custom,
2361
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2411
+ message: duplicateFieldErrorMessage(dups)
2362
2412
  });
2363
2413
  }
2364
2414
  });
@@ -2368,21 +2418,21 @@ const TinaFieldZod = z.lazy(() => {
2368
2418
  invalid_type_error: typeTypeError,
2369
2419
  required_error: typeRequiredError
2370
2420
  }),
2371
- 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) => {
2372
2422
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2373
2423
  if (dups) {
2374
2424
  ctx.addIssue({
2375
2425
  code: z.ZodIssueCode.custom,
2376
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2426
+ message: duplicateFieldErrorMessage(dups)
2377
2427
  });
2378
2428
  }
2379
2429
  }),
2380
- 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) => {
2381
2431
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2382
2432
  if (dups) {
2383
2433
  ctx.addIssue({
2384
2434
  code: z.ZodIssueCode.custom,
2385
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2435
+ message: duplicateTemplateErrorMessage(dups)
2386
2436
  });
2387
2437
  }
2388
2438
  })
@@ -2397,7 +2447,7 @@ const TinaFieldZod = z.lazy(() => {
2397
2447
  if (dups) {
2398
2448
  ctx.addIssue({
2399
2449
  code: z.ZodIssueCode.custom,
2400
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2450
+ message: duplicateTemplateErrorMessage(dups)
2401
2451
  });
2402
2452
  }
2403
2453
  })
@@ -2417,10 +2467,17 @@ const TinaFieldZod = z.lazy(() => {
2417
2467
  ],
2418
2468
  {
2419
2469
  errorMap: (issue, ctx) => {
2420
- var _a;
2470
+ var _a, _b;
2421
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
+ }
2422
2478
  return {
2423
- 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}`
2424
2481
  };
2425
2482
  }
2426
2483
  return {
@@ -2430,77 +2487,52 @@ const TinaFieldZod = z.lazy(() => {
2430
2487
  }
2431
2488
  ).superRefine((val, ctx) => {
2432
2489
  if (val.type === "string") {
2433
- if (val.isTitle) {
2434
- if (val.list) {
2435
- ctx.addIssue({
2436
- code: z.ZodIssueCode.custom,
2437
- message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
2438
- ${JSON.stringify(
2439
- val,
2440
- null,
2441
- 2
2442
- )}
2443
- `
2444
- });
2445
- }
2446
- if (!val.required) {
2447
- ctx.addIssue({
2448
- code: z.ZodIssueCode.custom,
2449
- message: `Must have { required: true } when using \`isTitle\` Error in value
2450
- ${JSON.stringify(
2451
- val,
2452
- null,
2453
- 2
2454
- )}
2455
- `
2456
- });
2457
- }
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
+ });
2458
2497
  }
2459
- if (val.uid) {
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
+ });
2504
+ }
2505
+ if (val.uid && val.list) {
2460
2506
  if (val.list) {
2461
2507
  ctx.addIssue({
2462
2508
  code: z.ZodIssueCode.custom,
2463
- message: `Can not have \`list: true\` when using \`uid\`. Error in value
2464
- ${JSON.stringify(
2465
- val,
2466
- null,
2467
- 2
2468
- )}
2469
- `
2470
- });
2471
- }
2472
- if (!val.required) {
2473
- ctx.addIssue({
2474
- code: z.ZodIssueCode.custom,
2475
- message: `Must have { required: true } when using \`uid\` Error in value
2476
- ${JSON.stringify(
2477
- val,
2478
- null,
2479
- 2
2480
- )}
2481
- `
2509
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2510
+ ${stringifiedField}`
2482
2511
  });
2483
2512
  }
2484
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
+ }
2485
2521
  }
2486
2522
  if (val.type === "object") {
2487
- const message = "Must provide one of templates or fields in your collection";
2488
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2489
- if (!isValid) {
2523
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2490
2524
  ctx.addIssue({
2491
2525
  code: z.ZodIssueCode.custom,
2492
- 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."
2493
2534
  });
2494
2535
  return false;
2495
- } else {
2496
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2497
- if (!isValid) {
2498
- ctx.addIssue({
2499
- code: z.ZodIssueCode.custom,
2500
- message
2501
- });
2502
- }
2503
- return isValid;
2504
2536
  }
2505
2537
  }
2506
2538
  return true;
@@ -2533,19 +2565,10 @@ const validateTinaCloudSchemaConfig = (config) => {
2533
2565
  const newConfig = tinaConfigZod.parse(config);
2534
2566
  return newConfig;
2535
2567
  };
2536
- const FORMATS = [
2537
- "json",
2538
- "md",
2539
- "markdown",
2540
- "mdx",
2541
- "toml",
2542
- "yaml",
2543
- "yml"
2544
- ];
2545
2568
  const Template = z.object({
2546
2569
  label: z.string({
2547
- invalid_type_error: "label must be a string",
2548
- 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."
2549
2572
  }),
2550
2573
  name,
2551
2574
  fields: z.array(TinaFieldZod)
@@ -2555,7 +2578,7 @@ const Template = z.object({
2555
2578
  if (dups) {
2556
2579
  ctx.addIssue({
2557
2580
  code: z.ZodIssueCode.custom,
2558
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2581
+ message: duplicateFieldErrorMessage(dups)
2559
2582
  });
2560
2583
  }
2561
2584
  });
@@ -2565,7 +2588,7 @@ const CollectionBaseSchema = z.object({
2565
2588
  if (val === "relativePath") {
2566
2589
  ctx.addIssue({
2567
2590
  code: z.ZodIssueCode.custom,
2568
- 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."
2569
2592
  });
2570
2593
  }
2571
2594
  }),
@@ -2573,72 +2596,73 @@ const CollectionBaseSchema = z.object({
2573
2596
  if (val === ".") {
2574
2597
  ctx.addIssue({
2575
2598
  code: z.ZodIssueCode.custom,
2576
- message: `path cannot be '.'. Please use '/' or '' instead. `
2599
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2577
2600
  });
2578
2601
  }
2579
2602
  }),
2580
- format: z.enum(FORMATS).optional(),
2603
+ format: z.enum(CONTENT_FORMATS).optional(),
2581
2604
  isAuthCollection: z.boolean().optional(),
2582
2605
  isDetached: z.boolean().optional()
2583
2606
  });
2584
2607
  const TinaCloudCollection = CollectionBaseSchema.extend({
2585
- 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) => {
2586
2609
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2587
2610
  if (dups) {
2588
2611
  ctx.addIssue({
2589
2612
  code: z.ZodIssueCode.custom,
2590
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2613
+ message: duplicateFieldErrorMessage(dups)
2591
2614
  });
2592
2615
  }
2593
- }).refine(
2594
- // It is valid if it is 0 or 1
2595
- (val) => {
2596
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2597
- return arr.length < 2;
2598
- },
2599
- {
2600
- message: "Fields can only have one use of `isTitle`"
2601
- }
2602
- ).refine(
2603
- // It is valid if it is 0 or 1
2604
- (val) => {
2605
- const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2606
- return arr.length < 2;
2607
- },
2608
- {
2609
- message: "Fields can only have one use of `uid`"
2610
- }
2611
- ).refine(
2612
- // It is valid if it is 0 or 1
2613
- (val) => {
2614
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2615
- return arr.length < 2;
2616
- },
2617
- {
2618
- 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
+ });
2619
2623
  }
2620
- ),
2621
- templates: z.array(Template).min(1).optional().superRefine((val, ctx) => {
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
+ });
2631
+ }
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) => {
2622
2642
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2623
2643
  if (dups) {
2624
2644
  ctx.addIssue({
2625
2645
  code: z.ZodIssueCode.custom,
2626
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2646
+ message: duplicateFieldErrorMessage(dups)
2627
2647
  });
2628
2648
  }
2629
2649
  })
2630
- }).refine(
2631
- (val) => {
2632
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2633
- if (!isValid) {
2634
- return false;
2635
- } else {
2636
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2637
- return isValid;
2638
- }
2639
- },
2640
- { message: "Must provide one of templates or fields in your collection" }
2641
- );
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
+ });
2642
2666
  const TinaCloudSchemaZod = z.object({
2643
2667
  collections: z.array(TinaCloudCollection),
2644
2668
  config: tinaConfigZod.optional()
@@ -2648,14 +2672,14 @@ const TinaCloudSchemaZod = z.object({
2648
2672
  if (dups) {
2649
2673
  ctx.addIssue({
2650
2674
  code: z.ZodIssueCode.custom,
2651
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2675
+ message: duplicateCollectionErrorMessage(dups),
2652
2676
  fatal: true
2653
2677
  });
2654
2678
  }
2655
2679
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2656
2680
  ctx.addIssue({
2657
2681
  code: z.ZodIssueCode.custom,
2658
- message: `Only one collection can be marked as isAuthCollection`,
2682
+ message: "Only one collection can be marked as `isAuthCollection`.",
2659
2683
  fatal: true
2660
2684
  });
2661
2685
  }
@@ -2663,7 +2687,7 @@ const TinaCloudSchemaZod = z.object({
2663
2687
  if (media && media.tina && media.loadCustomStore) {
2664
2688
  ctx.addIssue({
2665
2689
  code: z.ZodIssueCode.custom,
2666
- 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.",
2667
2691
  fatal: true,
2668
2692
  path: ["config", "media"]
2669
2693
  });
@@ -2672,7 +2696,7 @@ const TinaCloudSchemaZod = z.object({
2672
2696
  if (search && search.tina && search.searchClient) {
2673
2697
  ctx.addIssue({
2674
2698
  code: z.ZodIssueCode.custom,
2675
- 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.",
2676
2700
  fatal: true,
2677
2701
  path: ["config", "search"]
2678
2702
  });
@@ -2690,17 +2714,19 @@ const validateSchema = ({ schema }) => {
2690
2714
  } catch (e) {
2691
2715
  if (e instanceof ZodError) {
2692
2716
  const errors = parseZodError({ zodError: e });
2693
- throw new TinaSchemaValidationError(errors.join(", \n"));
2717
+ throw new TinaSchemaValidationError(errors.join("\n"));
2694
2718
  }
2695
2719
  throw new Error(e);
2696
2720
  }
2697
2721
  };
2698
2722
  export {
2723
+ CONTENT_FORMATS,
2699
2724
  NAMER,
2700
2725
  TINA_HOST,
2701
2726
  TinaSchema,
2702
2727
  TinaSchemaValidationError,
2703
2728
  addNamespaceToSchema,
2729
+ canonicalPath,
2704
2730
  normalizePath,
2705
2731
  parseURL,
2706
2732
  resolveField,