@tinacms/schema-tools 0.0.0-ecea7ac-20241011043815 → 0.0.0-ed6025e-20251201040055

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.js CHANGED
@@ -20,42 +20,26 @@
20
20
  }
21
21
  const yup__namespace = /* @__PURE__ */ _interopNamespaceDefault(yup);
22
22
  function addNamespaceToSchema(maybeNode, namespace = []) {
23
- if (typeof maybeNode === "string") {
23
+ if (typeof maybeNode !== "object" || maybeNode === null) {
24
24
  return maybeNode;
25
25
  }
26
- if (typeof maybeNode === "boolean") {
27
- return maybeNode;
28
- }
29
- if (typeof maybeNode === "function") {
30
- return maybeNode;
31
- }
32
- const newNode = { ...maybeNode };
33
- const keys = Object.keys(maybeNode);
34
- Object.values(maybeNode).map((m, index) => {
35
- const key = keys[index];
36
- if (Array.isArray(m)) {
37
- newNode[key] = m.map((element) => {
38
- if (!element) {
39
- return;
40
- }
41
- if (!element.hasOwnProperty("name")) {
42
- return element;
26
+ const newNode = { ...maybeNode, namespace: [...namespace] };
27
+ Object.entries(maybeNode).forEach(([key, value]) => {
28
+ if (Array.isArray(value)) {
29
+ newNode[key] = value.map((element) => {
30
+ if (element && typeof element === "object" && "name" in element) {
31
+ const valueName = element.name || element.value;
32
+ return addNamespaceToSchema(element, [...namespace, valueName]);
43
33
  }
44
- const value = element.name || element.value;
45
- return addNamespaceToSchema(element, [...namespace, value]);
34
+ return element;
46
35
  });
36
+ } else if (value && typeof value === "object" && "name" in value) {
37
+ newNode[key] = addNamespaceToSchema(value, [...namespace, value.name]);
47
38
  } else {
48
- if (!m) {
49
- return;
50
- }
51
- if (!m.hasOwnProperty("name")) {
52
- newNode[key] = m;
53
- } else {
54
- newNode[key] = addNamespaceToSchema(m, [...namespace, m.name]);
55
- }
39
+ newNode[key] = value;
56
40
  }
57
41
  });
58
- return { ...newNode, namespace };
42
+ return newNode;
59
43
  }
60
44
  function getDefaultExportFromCjs(x) {
61
45
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -1680,6 +1664,9 @@
1680
1664
  };
1681
1665
  };
1682
1666
  const normalizePath = (filepath) => filepath.replace(/\\/g, "/");
1667
+ const canonicalPath = (filepath) => {
1668
+ return normalizePath(filepath).split("/").filter((name2) => name2 !== "").join("/");
1669
+ };
1683
1670
  class TinaSchema {
1684
1671
  /**
1685
1672
  * Create a schema class from a user defined schema object
@@ -1729,21 +1716,21 @@
1729
1716
  };
1730
1717
  this.getCollectionByFullPath = (filepath) => {
1731
1718
  const fileExtension = filepath.split(".").pop();
1732
- const normalizedPath = filepath.replace(/\\/g, "/");
1719
+ const canonicalFilepath = canonicalPath(filepath);
1733
1720
  const possibleCollections = this.getCollections().filter((collection) => {
1734
1721
  var _a, _b;
1735
- if (!normalizedPath.endsWith(`.gitkeep.${collection.format || "md"}`) && fileExtension !== (collection.format || "md")) {
1722
+ if (!canonicalFilepath.endsWith(`.gitkeep.${collection.format || "md"}`) && fileExtension !== (collection.format || "md")) {
1736
1723
  return false;
1737
1724
  }
1738
1725
  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)) {
1739
1726
  const matches = this.getMatches({ collection });
1740
- const match = picomatch$1.isMatch(normalizedPath, matches);
1727
+ const match = picomatch$1.isMatch(canonicalFilepath, matches);
1741
1728
  if (!match) {
1742
1729
  return false;
1743
1730
  }
1744
1731
  }
1745
- const path = collection.path ? collection.path.replace(/\/?$/, "/") : "";
1746
- return normalizedPath.startsWith(path);
1732
+ const collectionPath = canonicalPath(collection.path);
1733
+ return collectionPath === "" || canonicalFilepath.startsWith(`${collectionPath}/`);
1747
1734
  });
1748
1735
  if (possibleCollections.length === 0) {
1749
1736
  throw new Error(`Unable to find collection for file at ${filepath}`);
@@ -1961,7 +1948,7 @@
1961
1948
  }
1962
1949
  }
1963
1950
  };
1964
- this.walkFields = (cb) => {
1951
+ this.legacyWalkFields = (cb) => {
1965
1952
  const walk = (collectionOrObject, collection, path) => {
1966
1953
  if (collectionOrObject.templates) {
1967
1954
  collectionOrObject.templates.forEach((template) => {
@@ -1983,7 +1970,7 @@
1983
1970
  collections.forEach((collection) => walk(collection, collection, []));
1984
1971
  };
1985
1972
  this.schema = config;
1986
- this.walkFields(({ field, collection, path }) => {
1973
+ this.legacyWalkFields(({ field, collection }) => {
1987
1974
  if (!("searchable" in field)) {
1988
1975
  if (field.type === "image") {
1989
1976
  field.searchable = false;
@@ -2004,6 +1991,67 @@
2004
1991
  field.uid = field.uid || false;
2005
1992
  });
2006
1993
  }
1994
+ findReferencesFromCollection(name2) {
1995
+ const result = {};
1996
+ this.walkFields(({ field, collection: c, path }) => {
1997
+ if (c.name !== name2) {
1998
+ return;
1999
+ }
2000
+ if (field.type === "reference") {
2001
+ field.collections.forEach((name22) => {
2002
+ if (result[name22] === void 0) {
2003
+ result[name22] = [];
2004
+ }
2005
+ result[name22].push(path);
2006
+ });
2007
+ }
2008
+ });
2009
+ return result;
2010
+ }
2011
+ /**
2012
+ * Walk all fields in tina schema
2013
+ *
2014
+ * @param cb callback function invoked for each field
2015
+ */
2016
+ walkFields(cb) {
2017
+ const walk = (collectionOrObject, collection, path = "$") => {
2018
+ if (collectionOrObject.templates) {
2019
+ collectionOrObject.templates.forEach((template) => {
2020
+ const templatePath = `${path}.${template.name}`;
2021
+ template.fields.forEach((field) => {
2022
+ const fieldPath = field.list ? `${templatePath}[*].${field.name}` : `${templatePath}.${field.name}`;
2023
+ cb({ field, collection, path: fieldPath });
2024
+ if (field.type === "object") {
2025
+ walk(field, collection, fieldPath);
2026
+ }
2027
+ });
2028
+ });
2029
+ }
2030
+ if (collectionOrObject.fields) {
2031
+ collectionOrObject.fields.forEach((field) => {
2032
+ const fieldPath = field.list ? `${path}.${field.name}[*]` : `${path}.${field.name}`;
2033
+ cb({ field, collection, path: fieldPath });
2034
+ if (field.type === "object" && field.fields) {
2035
+ walk(field, collection, fieldPath);
2036
+ } else if (field.templates) {
2037
+ field.templates.forEach((template) => {
2038
+ const templatePath = `${fieldPath}.${template.name}`;
2039
+ template.fields.forEach((field2) => {
2040
+ const fieldPath2 = field2.list ? `${templatePath}[*].${field2.name}` : `${templatePath}.${field2.name}`;
2041
+ cb({ field: field2, collection, path: fieldPath2 });
2042
+ if (field2.type === "object") {
2043
+ walk(field2, collection, fieldPath2);
2044
+ }
2045
+ });
2046
+ });
2047
+ }
2048
+ });
2049
+ }
2050
+ };
2051
+ this.getCollections().forEach((collection) => {
2052
+ walk(collection, collection);
2053
+ });
2054
+ }
2007
2055
  /**
2008
2056
  * This function returns an array of glob matches for a given collection.
2009
2057
  *
@@ -2015,16 +2063,16 @@
2015
2063
  }) {
2016
2064
  var _a, _b;
2017
2065
  const collection = typeof collectionOrString === "string" ? this.getCollection(collectionOrString) : collectionOrString;
2018
- const normalPath = normalizePath(collection.path);
2019
- const pathSuffix = normalPath ? "/" : "";
2066
+ const collectionPath = canonicalPath(collection.path);
2067
+ const pathSuffix = collectionPath ? "/" : "";
2020
2068
  const format = collection.format || "md";
2021
2069
  const matches = [];
2022
2070
  if ((_a = collection == null ? void 0 : collection.match) == null ? void 0 : _a.include) {
2023
- const match = `${normalPath}${pathSuffix}${collection.match.include}.${format}`;
2071
+ const match = `${collectionPath}${pathSuffix}${collection.match.include}.${format}`;
2024
2072
  matches.push(match);
2025
2073
  }
2026
2074
  if ((_b = collection == null ? void 0 : collection.match) == null ? void 0 : _b.exclude) {
2027
- const exclude = `!(${normalPath}${pathSuffix}${collection.match.exclude}.${format})`;
2075
+ const exclude = `!(${collectionPath}${pathSuffix}${collection.match.exclude}.${format})`;
2028
2076
  matches.push(exclude);
2029
2077
  }
2030
2078
  return matches;
@@ -2217,6 +2265,15 @@
2217
2265
  })
2218
2266
  };
2219
2267
  };
2268
+ const CONTENT_FORMATS = [
2269
+ "mdx",
2270
+ "md",
2271
+ "markdown",
2272
+ "json",
2273
+ "yaml",
2274
+ "yml",
2275
+ "toml"
2276
+ ];
2220
2277
  const parseZodError = ({ zodError }) => {
2221
2278
  var _a, _b, _c, _d;
2222
2279
  const errors = zodError.flatten((issue) => {
@@ -2226,9 +2283,12 @@
2226
2283
  moreInfo.push(parseZodError({ zodError: unionError }));
2227
2284
  });
2228
2285
  }
2229
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
2286
+ const errorMessage = `${issue == null ? void 0 : issue.message}
2287
+ Additional information:
2288
+ - Error found at path ${issue.path.join(
2230
2289
  "."
2231
- )}`;
2290
+ )}
2291
+ `;
2232
2292
  const errorMessages = [errorMessage, ...moreInfo];
2233
2293
  return {
2234
2294
  errors: errorMessages
@@ -2263,6 +2323,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2263
2323
  });
2264
2324
  }
2265
2325
  });
2326
+ const duplicateFieldErrorMessage = (fields) => `Fields must have unique names. Found duplicate field names: [${fields}]`;
2327
+ const duplicateTemplateErrorMessage = (templates) => `Templates must have unique names. Found duplicate template names: [${templates}]`;
2328
+ const duplicateCollectionErrorMessage = (collection) => `Collections must have unique names. Found duplicate collection names: [${collection}]`;
2266
2329
  const TypeName = [
2267
2330
  "string",
2268
2331
  "boolean",
@@ -2273,10 +2336,11 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2273
2336
  "reference",
2274
2337
  "rich-text"
2275
2338
  ];
2276
- const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
2277
- const typeRequiredError = `type is required and must be one of ${TypeName.join(
2278
- ", "
2279
- )}`;
2339
+ const formattedTypes = ` - ${TypeName.join("\n - ")}`;
2340
+ const typeTypeError = `Invalid \`type\` property. \`type\` expected to be one of the following values:
2341
+ ${formattedTypes}`;
2342
+ const typeRequiredError = `Missing \`type\` property. Please add a \`type\` property with one of the following:
2343
+ ${formattedTypes}`;
2280
2344
  const Option = z.z.union(
2281
2345
  [
2282
2346
  z.z.string(),
@@ -2331,7 +2395,8 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2331
2395
  type: z.z.literal("image", {
2332
2396
  invalid_type_error: typeTypeError,
2333
2397
  required_error: typeRequiredError
2334
- })
2398
+ }),
2399
+ uploadDir: z.z.function().args(z.z.any()).returns(z.z.string()).optional()
2335
2400
  });
2336
2401
  const DateTimeField = TinaScalerBase.extend({
2337
2402
  type: z.z.literal("datetime", {
@@ -2362,7 +2427,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2362
2427
  if (dups) {
2363
2428
  ctx.addIssue({
2364
2429
  code: z.z.ZodIssueCode.custom,
2365
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2430
+ message: duplicateFieldErrorMessage(dups)
2366
2431
  });
2367
2432
  }
2368
2433
  });
@@ -2372,21 +2437,21 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2372
2437
  invalid_type_error: typeTypeError,
2373
2438
  required_error: typeRequiredError
2374
2439
  }),
2375
- fields: z.z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2440
+ fields: z.z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2376
2441
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2377
2442
  if (dups) {
2378
2443
  ctx.addIssue({
2379
2444
  code: z.z.ZodIssueCode.custom,
2380
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2445
+ message: duplicateFieldErrorMessage(dups)
2381
2446
  });
2382
2447
  }
2383
2448
  }),
2384
- templates: z.z.array(TemplateTemp).min(1).optional().superRefine((val, ctx) => {
2449
+ templates: z.z.array(TemplateTemp).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2385
2450
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2386
2451
  if (dups) {
2387
2452
  ctx.addIssue({
2388
2453
  code: z.z.ZodIssueCode.custom,
2389
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2454
+ message: duplicateTemplateErrorMessage(dups)
2390
2455
  });
2391
2456
  }
2392
2457
  })
@@ -2401,7 +2466,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2401
2466
  if (dups) {
2402
2467
  ctx.addIssue({
2403
2468
  code: z.z.ZodIssueCode.custom,
2404
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2469
+ message: duplicateTemplateErrorMessage(dups)
2405
2470
  });
2406
2471
  }
2407
2472
  })
@@ -2421,10 +2486,17 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2421
2486
  ],
2422
2487
  {
2423
2488
  errorMap: (issue, ctx) => {
2424
- var _a;
2489
+ var _a, _b;
2425
2490
  if (issue.code === "invalid_union_discriminator") {
2491
+ if (!((_a = ctx.data) == null ? void 0 : _a.type)) {
2492
+ return {
2493
+ message: `Missing \`type\` property in field \`${ctx.data.name}\`. Please add a \`type\` property with one of the following:
2494
+ ${formattedTypes}`
2495
+ };
2496
+ }
2426
2497
  return {
2427
- message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
2498
+ 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:
2499
+ ${formattedTypes}`
2428
2500
  };
2429
2501
  }
2430
2502
  return {
@@ -2434,77 +2506,52 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2434
2506
  }
2435
2507
  ).superRefine((val, ctx) => {
2436
2508
  if (val.type === "string") {
2437
- if (val.isTitle) {
2438
- if (val.list) {
2439
- ctx.addIssue({
2440
- code: z.z.ZodIssueCode.custom,
2441
- message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
2442
- ${JSON.stringify(
2443
- val,
2444
- null,
2445
- 2
2446
- )}
2447
- `
2448
- });
2449
- }
2450
- if (!val.required) {
2451
- ctx.addIssue({
2452
- code: z.z.ZodIssueCode.custom,
2453
- message: `Must have { required: true } when using \`isTitle\` Error in value
2454
- ${JSON.stringify(
2455
- val,
2456
- null,
2457
- 2
2458
- )}
2459
- `
2460
- });
2461
- }
2509
+ const stringifiedField = JSON.stringify(val, null, 2);
2510
+ if (val.isTitle && val.list) {
2511
+ ctx.addIssue({
2512
+ code: z.z.ZodIssueCode.custom,
2513
+ message: `\`list: true\` is not allowed when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2514
+ ${stringifiedField}`
2515
+ });
2462
2516
  }
2463
- if (val.uid) {
2517
+ if (val.isTitle && !val.required) {
2518
+ ctx.addIssue({
2519
+ code: z.z.ZodIssueCode.custom,
2520
+ message: `Property \`required: true\` is required when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2521
+ ${stringifiedField}`
2522
+ });
2523
+ }
2524
+ if (val.uid && val.list) {
2464
2525
  if (val.list) {
2465
2526
  ctx.addIssue({
2466
2527
  code: z.z.ZodIssueCode.custom,
2467
- message: `Can not have \`list: true\` when using \`uid\`. Error in value
2468
- ${JSON.stringify(
2469
- val,
2470
- null,
2471
- 2
2472
- )}
2473
- `
2474
- });
2475
- }
2476
- if (!val.required) {
2477
- ctx.addIssue({
2478
- code: z.z.ZodIssueCode.custom,
2479
- message: `Must have { required: true } when using \`uid\` Error in value
2480
- ${JSON.stringify(
2481
- val,
2482
- null,
2483
- 2
2484
- )}
2485
- `
2528
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2529
+ ${stringifiedField}`
2486
2530
  });
2487
2531
  }
2488
2532
  }
2533
+ if (val.uid && !val.required) {
2534
+ ctx.addIssue({
2535
+ code: z.z.ZodIssueCode.custom,
2536
+ message: `Property \`required: true\` is required when using \`uid\` for fields of \`type: string\`. Error found in field:
2537
+ ${stringifiedField}`
2538
+ });
2539
+ }
2489
2540
  }
2490
2541
  if (val.type === "object") {
2491
- const message = "Must provide one of templates or fields in your collection";
2492
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2493
- if (!isValid) {
2542
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2494
2543
  ctx.addIssue({
2495
2544
  code: z.z.ZodIssueCode.custom,
2496
- message
2545
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2546
+ });
2547
+ return false;
2548
+ }
2549
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2550
+ ctx.addIssue({
2551
+ code: z.z.ZodIssueCode.custom,
2552
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2497
2553
  });
2498
2554
  return false;
2499
- } else {
2500
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2501
- if (!isValid) {
2502
- ctx.addIssue({
2503
- code: z.z.ZodIssueCode.custom,
2504
- message
2505
- });
2506
- }
2507
- return isValid;
2508
2555
  }
2509
2556
  }
2510
2557
  return true;
@@ -2531,25 +2578,33 @@ ${JSON.stringify(
2531
2578
  searchClient: z.any().optional(),
2532
2579
  indexBatchSize: z.number().gte(1).optional(),
2533
2580
  maxSearchIndexFieldLength: z.number().gte(1).optional()
2581
+ }).optional(),
2582
+ ui: z.object({
2583
+ previewUrl: z.function().optional(),
2584
+ optOutOfUpdateCheck: z.boolean().optional(),
2585
+ regexValidation: z.object({
2586
+ folderNameRegex: z.string().refine(
2587
+ (val) => {
2588
+ try {
2589
+ new RegExp(val);
2590
+ return true;
2591
+ } catch (error) {
2592
+ return false;
2593
+ }
2594
+ },
2595
+ { message: "folderNameRegex is not a valid regex pattern" }
2596
+ ).optional()
2597
+ }).optional()
2534
2598
  }).optional()
2535
2599
  });
2536
2600
  const validateTinaCloudSchemaConfig = (config) => {
2537
2601
  const newConfig = tinaConfigZod.parse(config);
2538
2602
  return newConfig;
2539
2603
  };
2540
- const FORMATS = [
2541
- "json",
2542
- "md",
2543
- "markdown",
2544
- "mdx",
2545
- "toml",
2546
- "yaml",
2547
- "yml"
2548
- ];
2549
2604
  const Template = z.z.object({
2550
2605
  label: z.z.string({
2551
- invalid_type_error: "label must be a string",
2552
- required_error: "label was not provided but is required"
2606
+ invalid_type_error: "Invalid data type for property `label`. Must be of type `string`",
2607
+ required_error: "Missing `label` property. Property `label` is required."
2553
2608
  }),
2554
2609
  name,
2555
2610
  fields: z.z.array(TinaFieldZod)
@@ -2559,7 +2614,7 @@ ${JSON.stringify(
2559
2614
  if (dups) {
2560
2615
  ctx.addIssue({
2561
2616
  code: z.z.ZodIssueCode.custom,
2562
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2617
+ message: duplicateFieldErrorMessage(dups)
2563
2618
  });
2564
2619
  }
2565
2620
  });
@@ -2569,7 +2624,7 @@ ${JSON.stringify(
2569
2624
  if (val === "relativePath") {
2570
2625
  ctx.addIssue({
2571
2626
  code: z.z.ZodIssueCode.custom,
2572
- message: `name cannot be 'relativePath'. 'relativePath' is a reserved field name.`
2627
+ message: "Invalid `name` property. `name` cannot be 'relativePath' as it is a reserved field name."
2573
2628
  });
2574
2629
  }
2575
2630
  }),
@@ -2577,72 +2632,73 @@ ${JSON.stringify(
2577
2632
  if (val === ".") {
2578
2633
  ctx.addIssue({
2579
2634
  code: z.z.ZodIssueCode.custom,
2580
- message: `path cannot be '.'. Please use '/' or '' instead. `
2635
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2581
2636
  });
2582
2637
  }
2583
2638
  }),
2584
- format: z.z.enum(FORMATS).optional(),
2639
+ format: z.z.enum(CONTENT_FORMATS).optional(),
2585
2640
  isAuthCollection: z.z.boolean().optional(),
2586
2641
  isDetached: z.z.boolean().optional()
2587
2642
  });
2588
2643
  const TinaCloudCollection = CollectionBaseSchema.extend({
2589
- fields: z.z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2644
+ fields: z.z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2590
2645
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2591
2646
  if (dups) {
2592
2647
  ctx.addIssue({
2593
2648
  code: z.z.ZodIssueCode.custom,
2594
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2649
+ message: duplicateFieldErrorMessage(dups)
2595
2650
  });
2596
2651
  }
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 === "string" && x.isTitle)) || [];
2601
- return arr.length < 2;
2602
- },
2603
- {
2604
- message: "Fields can only have one use of `isTitle`"
2605
- }
2606
- ).refine(
2607
- // It is valid if it is 0 or 1
2608
- (val) => {
2609
- const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2610
- return arr.length < 2;
2611
- },
2612
- {
2613
- message: "Fields can only have one use of `uid`"
2614
- }
2615
- ).refine(
2616
- // It is valid if it is 0 or 1
2617
- (val) => {
2618
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2619
- return arr.length < 2;
2620
- },
2621
- {
2622
- message: "Fields can only have one use of `password` type"
2652
+ }).superRefine((val, ctx) => {
2653
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2654
+ if (arr.length > 1) {
2655
+ ctx.addIssue({
2656
+ code: z.z.ZodIssueCode.custom,
2657
+ message: `The following fields have the property \`isTitle\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`isTitle\`.`
2658
+ });
2659
+ }
2660
+ }).superRefine((val, ctx) => {
2661
+ const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2662
+ if (arr.length > 2) {
2663
+ ctx.addIssue({
2664
+ code: z.z.ZodIssueCode.custom,
2665
+ message: `The following fields have the property \`uid\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`uid\`.`
2666
+ });
2623
2667
  }
2624
- ),
2625
- templates: z.z.array(Template).min(1).optional().superRefine((val, ctx) => {
2668
+ }).superRefine((val, ctx) => {
2669
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2670
+ if (arr.length > 2) {
2671
+ ctx.addIssue({
2672
+ code: z.z.ZodIssueCode.custom,
2673
+ message: `The following fields have \`type: password\`: [${arr.map((field) => field.name).join(", ")}]. Only one can be of \`type: password\`.`
2674
+ });
2675
+ }
2676
+ }),
2677
+ templates: z.z.array(Template).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2626
2678
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2627
2679
  if (dups) {
2628
2680
  ctx.addIssue({
2629
2681
  code: z.z.ZodIssueCode.custom,
2630
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2682
+ message: duplicateFieldErrorMessage(dups)
2631
2683
  });
2632
2684
  }
2633
2685
  })
2634
- }).refine(
2635
- (val) => {
2636
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2637
- if (!isValid) {
2638
- return false;
2639
- } else {
2640
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2641
- return isValid;
2642
- }
2643
- },
2644
- { message: "Must provide one of templates or fields in your collection" }
2645
- );
2686
+ }).superRefine((val, ctx) => {
2687
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2688
+ ctx.addIssue({
2689
+ code: z.z.ZodIssueCode.custom,
2690
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2691
+ });
2692
+ return false;
2693
+ }
2694
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2695
+ ctx.addIssue({
2696
+ code: z.z.ZodIssueCode.custom,
2697
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2698
+ });
2699
+ return false;
2700
+ }
2701
+ });
2646
2702
  const TinaCloudSchemaZod = z.z.object({
2647
2703
  collections: z.z.array(TinaCloudCollection),
2648
2704
  config: tinaConfigZod.optional()
@@ -2652,14 +2708,14 @@ ${JSON.stringify(
2652
2708
  if (dups) {
2653
2709
  ctx.addIssue({
2654
2710
  code: z.z.ZodIssueCode.custom,
2655
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2711
+ message: duplicateCollectionErrorMessage(dups),
2656
2712
  fatal: true
2657
2713
  });
2658
2714
  }
2659
2715
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2660
2716
  ctx.addIssue({
2661
2717
  code: z.z.ZodIssueCode.custom,
2662
- message: `Only one collection can be marked as isAuthCollection`,
2718
+ message: "Only one collection can be marked as `isAuthCollection`.",
2663
2719
  fatal: true
2664
2720
  });
2665
2721
  }
@@ -2667,7 +2723,7 @@ ${JSON.stringify(
2667
2723
  if (media && media.tina && media.loadCustomStore) {
2668
2724
  ctx.addIssue({
2669
2725
  code: z.z.ZodIssueCode.custom,
2670
- message: "can not have both loadCustomStore and tina. Must use one or the other",
2726
+ message: "Cannot have both `loadCustomStore` and `tina`. Must use one or the other.",
2671
2727
  fatal: true,
2672
2728
  path: ["config", "media"]
2673
2729
  });
@@ -2676,7 +2732,7 @@ ${JSON.stringify(
2676
2732
  if (search && search.tina && search.searchClient) {
2677
2733
  ctx.addIssue({
2678
2734
  code: z.z.ZodIssueCode.custom,
2679
- message: "can not have both searchClient and tina. Must use one or the other",
2735
+ message: "Cannot have both `searchClient` and `tina`. Must use one or the other.",
2680
2736
  fatal: true,
2681
2737
  path: ["config", "search"]
2682
2738
  });
@@ -2694,16 +2750,18 @@ ${JSON.stringify(
2694
2750
  } catch (e) {
2695
2751
  if (e instanceof z.ZodError) {
2696
2752
  const errors = parseZodError({ zodError: e });
2697
- throw new TinaSchemaValidationError(errors.join(", \n"));
2753
+ throw new TinaSchemaValidationError(errors.join("\n"));
2698
2754
  }
2699
2755
  throw new Error(e);
2700
2756
  }
2701
2757
  };
2758
+ exports2.CONTENT_FORMATS = CONTENT_FORMATS;
2702
2759
  exports2.NAMER = NAMER;
2703
2760
  exports2.TINA_HOST = TINA_HOST;
2704
2761
  exports2.TinaSchema = TinaSchema;
2705
2762
  exports2.TinaSchemaValidationError = TinaSchemaValidationError;
2706
2763
  exports2.addNamespaceToSchema = addNamespaceToSchema;
2764
+ exports2.canonicalPath = canonicalPath;
2707
2765
  exports2.normalizePath = normalizePath;
2708
2766
  exports2.parseURL = parseURL;
2709
2767
  exports2.resolveField = resolveField;