@tinacms/schema-tools 0.0.0-e0ddb8c-20241004065742 → 0.0.0-e5c0e91-20250421003142

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.d.ts CHANGED
@@ -1,6 +1 @@
1
- export * from './schema';
2
- export * from './types/index';
3
- export * from './validate';
4
- export * from './util/namer';
5
- export * from './util/parseURL';
6
- export * from './util/normalizePath';
1
+ export * from "../src/index"
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;
@@ -1961,7 +1945,7 @@
1961
1945
  }
1962
1946
  }
1963
1947
  };
1964
- this.walkFields = (cb) => {
1948
+ this.legacyWalkFields = (cb) => {
1965
1949
  const walk = (collectionOrObject, collection, path) => {
1966
1950
  if (collectionOrObject.templates) {
1967
1951
  collectionOrObject.templates.forEach((template) => {
@@ -1983,7 +1967,7 @@
1983
1967
  collections.forEach((collection) => walk(collection, collection, []));
1984
1968
  };
1985
1969
  this.schema = config;
1986
- this.walkFields(({ field, collection, path }) => {
1970
+ this.legacyWalkFields(({ field, collection }) => {
1987
1971
  if (!("searchable" in field)) {
1988
1972
  if (field.type === "image") {
1989
1973
  field.searchable = false;
@@ -2004,6 +1988,67 @@
2004
1988
  field.uid = field.uid || false;
2005
1989
  });
2006
1990
  }
1991
+ findReferencesFromCollection(name2) {
1992
+ const result = {};
1993
+ this.walkFields(({ field, collection: c, path }) => {
1994
+ if (c.name !== name2) {
1995
+ return;
1996
+ }
1997
+ if (field.type === "reference") {
1998
+ field.collections.forEach((name22) => {
1999
+ if (result[name22] === void 0) {
2000
+ result[name22] = [];
2001
+ }
2002
+ result[name22].push(path);
2003
+ });
2004
+ }
2005
+ });
2006
+ return result;
2007
+ }
2008
+ /**
2009
+ * Walk all fields in tina schema
2010
+ *
2011
+ * @param cb callback function invoked for each field
2012
+ */
2013
+ walkFields(cb) {
2014
+ const walk = (collectionOrObject, collection, path = "$") => {
2015
+ if (collectionOrObject.templates) {
2016
+ collectionOrObject.templates.forEach((template) => {
2017
+ const templatePath = `${path}.${template.name}`;
2018
+ template.fields.forEach((field) => {
2019
+ const fieldPath = field.list ? `${templatePath}[*].${field.name}` : `${templatePath}.${field.name}`;
2020
+ cb({ field, collection, path: fieldPath });
2021
+ if (field.type === "object") {
2022
+ walk(field, collection, fieldPath);
2023
+ }
2024
+ });
2025
+ });
2026
+ }
2027
+ if (collectionOrObject.fields) {
2028
+ collectionOrObject.fields.forEach((field) => {
2029
+ const fieldPath = field.list ? `${path}.${field.name}[*]` : `${path}.${field.name}`;
2030
+ cb({ field, collection, path: fieldPath });
2031
+ if (field.type === "object" && field.fields) {
2032
+ walk(field, collection, fieldPath);
2033
+ } else if (field.templates) {
2034
+ field.templates.forEach((template) => {
2035
+ const templatePath = `${fieldPath}.${template.name}`;
2036
+ template.fields.forEach((field2) => {
2037
+ const fieldPath2 = field2.list ? `${templatePath}[*].${field2.name}` : `${templatePath}.${field2.name}`;
2038
+ cb({ field: field2, collection, path: fieldPath2 });
2039
+ if (field2.type === "object") {
2040
+ walk(field2, collection, fieldPath2);
2041
+ }
2042
+ });
2043
+ });
2044
+ }
2045
+ });
2046
+ }
2047
+ };
2048
+ this.getCollections().forEach((collection) => {
2049
+ walk(collection, collection);
2050
+ });
2051
+ }
2007
2052
  /**
2008
2053
  * This function returns an array of glob matches for a given collection.
2009
2054
  *
@@ -2226,9 +2271,12 @@
2226
2271
  moreInfo.push(parseZodError({ zodError: unionError }));
2227
2272
  });
2228
2273
  }
2229
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
2274
+ const errorMessage = `${issue == null ? void 0 : issue.message}
2275
+ Additional information:
2276
+ - Error found at path ${issue.path.join(
2230
2277
  "."
2231
- )}`;
2278
+ )}
2279
+ `;
2232
2280
  const errorMessages = [errorMessage, ...moreInfo];
2233
2281
  return {
2234
2282
  errors: errorMessages
@@ -2263,6 +2311,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2263
2311
  });
2264
2312
  }
2265
2313
  });
2314
+ const duplicateFieldErrorMessage = (fields) => `Fields must have unique names. Found duplicate field names: [${fields}]`;
2315
+ const duplicateTemplateErrorMessage = (templates) => `Templates must have unique names. Found duplicate template names: [${templates}]`;
2316
+ const duplicateCollectionErrorMessage = (collection) => `Collections must have unique names. Found duplicate collection names: [${collection}]`;
2266
2317
  const TypeName = [
2267
2318
  "string",
2268
2319
  "boolean",
@@ -2273,10 +2324,11 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2273
2324
  "reference",
2274
2325
  "rich-text"
2275
2326
  ];
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
- )}`;
2327
+ const formattedTypes = ` - ${TypeName.join("\n - ")}`;
2328
+ const typeTypeError = `Invalid \`type\` property. \`type\` expected to be one of the following values:
2329
+ ${formattedTypes}`;
2330
+ const typeRequiredError = `Missing \`type\` property. Please add a \`type\` property with one of the following:
2331
+ ${formattedTypes}`;
2280
2332
  const Option = z.z.union(
2281
2333
  [
2282
2334
  z.z.string(),
@@ -2362,7 +2414,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2362
2414
  if (dups) {
2363
2415
  ctx.addIssue({
2364
2416
  code: z.z.ZodIssueCode.custom,
2365
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2417
+ message: duplicateFieldErrorMessage(dups)
2366
2418
  });
2367
2419
  }
2368
2420
  });
@@ -2372,21 +2424,21 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2372
2424
  invalid_type_error: typeTypeError,
2373
2425
  required_error: typeRequiredError
2374
2426
  }),
2375
- fields: z.z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2427
+ fields: z.z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2376
2428
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2377
2429
  if (dups) {
2378
2430
  ctx.addIssue({
2379
2431
  code: z.z.ZodIssueCode.custom,
2380
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2432
+ message: duplicateFieldErrorMessage(dups)
2381
2433
  });
2382
2434
  }
2383
2435
  }),
2384
- templates: z.z.array(TemplateTemp).min(1).optional().superRefine((val, ctx) => {
2436
+ templates: z.z.array(TemplateTemp).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2385
2437
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2386
2438
  if (dups) {
2387
2439
  ctx.addIssue({
2388
2440
  code: z.z.ZodIssueCode.custom,
2389
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2441
+ message: duplicateTemplateErrorMessage(dups)
2390
2442
  });
2391
2443
  }
2392
2444
  })
@@ -2401,7 +2453,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2401
2453
  if (dups) {
2402
2454
  ctx.addIssue({
2403
2455
  code: z.z.ZodIssueCode.custom,
2404
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2456
+ message: duplicateTemplateErrorMessage(dups)
2405
2457
  });
2406
2458
  }
2407
2459
  })
@@ -2421,10 +2473,17 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2421
2473
  ],
2422
2474
  {
2423
2475
  errorMap: (issue, ctx) => {
2424
- var _a;
2476
+ var _a, _b;
2425
2477
  if (issue.code === "invalid_union_discriminator") {
2478
+ if (!((_a = ctx.data) == null ? void 0 : _a.type)) {
2479
+ return {
2480
+ message: `Missing \`type\` property in field \`${ctx.data.name}\`. Please add a \`type\` property with one of the following:
2481
+ ${formattedTypes}`
2482
+ };
2483
+ }
2426
2484
  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(", ")}`
2485
+ 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:
2486
+ ${formattedTypes}`
2428
2487
  };
2429
2488
  }
2430
2489
  return {
@@ -2434,77 +2493,52 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2434
2493
  }
2435
2494
  ).superRefine((val, ctx) => {
2436
2495
  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
- }
2496
+ const stringifiedField = JSON.stringify(val, null, 2);
2497
+ if (val.isTitle && val.list) {
2498
+ ctx.addIssue({
2499
+ code: z.z.ZodIssueCode.custom,
2500
+ message: `\`list: true\` is not allowed when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2501
+ ${stringifiedField}`
2502
+ });
2503
+ }
2504
+ if (val.isTitle && !val.required) {
2505
+ ctx.addIssue({
2506
+ code: z.z.ZodIssueCode.custom,
2507
+ message: `Property \`required: true\` is required when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2508
+ ${stringifiedField}`
2509
+ });
2462
2510
  }
2463
- if (val.uid) {
2511
+ if (val.uid && val.list) {
2464
2512
  if (val.list) {
2465
2513
  ctx.addIssue({
2466
2514
  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
- `
2515
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2516
+ ${stringifiedField}`
2486
2517
  });
2487
2518
  }
2488
2519
  }
2520
+ if (val.uid && !val.required) {
2521
+ ctx.addIssue({
2522
+ code: z.z.ZodIssueCode.custom,
2523
+ message: `Property \`required: true\` is required when using \`uid\` for fields of \`type: string\`. Error found in field:
2524
+ ${stringifiedField}`
2525
+ });
2526
+ }
2489
2527
  }
2490
2528
  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) {
2529
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2494
2530
  ctx.addIssue({
2495
2531
  code: z.z.ZodIssueCode.custom,
2496
- message
2532
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2533
+ });
2534
+ return false;
2535
+ }
2536
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2537
+ ctx.addIssue({
2538
+ code: z.z.ZodIssueCode.custom,
2539
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2497
2540
  });
2498
2541
  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
2542
  }
2509
2543
  }
2510
2544
  return true;
@@ -2548,8 +2582,8 @@ ${JSON.stringify(
2548
2582
  ];
2549
2583
  const Template = z.z.object({
2550
2584
  label: z.z.string({
2551
- invalid_type_error: "label must be a string",
2552
- required_error: "label was not provided but is required"
2585
+ invalid_type_error: "Invalid data type for property `label`. Must be of type `string`",
2586
+ required_error: "Missing `label` property. Property `label` is required."
2553
2587
  }),
2554
2588
  name,
2555
2589
  fields: z.z.array(TinaFieldZod)
@@ -2559,7 +2593,7 @@ ${JSON.stringify(
2559
2593
  if (dups) {
2560
2594
  ctx.addIssue({
2561
2595
  code: z.z.ZodIssueCode.custom,
2562
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2596
+ message: duplicateFieldErrorMessage(dups)
2563
2597
  });
2564
2598
  }
2565
2599
  });
@@ -2569,7 +2603,7 @@ ${JSON.stringify(
2569
2603
  if (val === "relativePath") {
2570
2604
  ctx.addIssue({
2571
2605
  code: z.z.ZodIssueCode.custom,
2572
- message: `name cannot be 'relativePath'. 'relativePath' is a reserved field name.`
2606
+ message: "Invalid `name` property. `name` cannot be 'relativePath' as it is a reserved field name."
2573
2607
  });
2574
2608
  }
2575
2609
  }),
@@ -2577,7 +2611,7 @@ ${JSON.stringify(
2577
2611
  if (val === ".") {
2578
2612
  ctx.addIssue({
2579
2613
  code: z.z.ZodIssueCode.custom,
2580
- message: `path cannot be '.'. Please use '/' or '' instead. `
2614
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2581
2615
  });
2582
2616
  }
2583
2617
  }),
@@ -2586,63 +2620,64 @@ ${JSON.stringify(
2586
2620
  isDetached: z.z.boolean().optional()
2587
2621
  });
2588
2622
  const TinaCloudCollection = CollectionBaseSchema.extend({
2589
- fields: z.z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2623
+ fields: z.z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2590
2624
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2591
2625
  if (dups) {
2592
2626
  ctx.addIssue({
2593
2627
  code: z.z.ZodIssueCode.custom,
2594
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2628
+ message: duplicateFieldErrorMessage(dups)
2595
2629
  });
2596
2630
  }
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"
2631
+ }).superRefine((val, ctx) => {
2632
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2633
+ if (arr.length > 1) {
2634
+ ctx.addIssue({
2635
+ code: z.z.ZodIssueCode.custom,
2636
+ message: `The following fields have the property \`isTitle\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`isTitle\`.`
2637
+ });
2638
+ }
2639
+ }).superRefine((val, ctx) => {
2640
+ const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2641
+ if (arr.length > 2) {
2642
+ ctx.addIssue({
2643
+ code: z.z.ZodIssueCode.custom,
2644
+ message: `The following fields have the property \`uid\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`uid\`.`
2645
+ });
2623
2646
  }
2624
- ),
2625
- templates: z.z.array(Template).min(1).optional().superRefine((val, ctx) => {
2647
+ }).superRefine((val, ctx) => {
2648
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2649
+ if (arr.length > 2) {
2650
+ ctx.addIssue({
2651
+ code: z.z.ZodIssueCode.custom,
2652
+ message: `The following fields have \`type: password\`: [${arr.map((field) => field.name).join(", ")}]. Only one can be of \`type: password\`.`
2653
+ });
2654
+ }
2655
+ }),
2656
+ templates: z.z.array(Template).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2626
2657
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2627
2658
  if (dups) {
2628
2659
  ctx.addIssue({
2629
2660
  code: z.z.ZodIssueCode.custom,
2630
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2661
+ message: duplicateFieldErrorMessage(dups)
2631
2662
  });
2632
2663
  }
2633
2664
  })
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
- );
2665
+ }).superRefine((val, ctx) => {
2666
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2667
+ ctx.addIssue({
2668
+ code: z.z.ZodIssueCode.custom,
2669
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2670
+ });
2671
+ return false;
2672
+ }
2673
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2674
+ ctx.addIssue({
2675
+ code: z.z.ZodIssueCode.custom,
2676
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2677
+ });
2678
+ return false;
2679
+ }
2680
+ });
2646
2681
  const TinaCloudSchemaZod = z.z.object({
2647
2682
  collections: z.z.array(TinaCloudCollection),
2648
2683
  config: tinaConfigZod.optional()
@@ -2652,14 +2687,14 @@ ${JSON.stringify(
2652
2687
  if (dups) {
2653
2688
  ctx.addIssue({
2654
2689
  code: z.z.ZodIssueCode.custom,
2655
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2690
+ message: duplicateCollectionErrorMessage(dups),
2656
2691
  fatal: true
2657
2692
  });
2658
2693
  }
2659
2694
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2660
2695
  ctx.addIssue({
2661
2696
  code: z.z.ZodIssueCode.custom,
2662
- message: `Only one collection can be marked as isAuthCollection`,
2697
+ message: "Only one collection can be marked as `isAuthCollection`.",
2663
2698
  fatal: true
2664
2699
  });
2665
2700
  }
@@ -2667,7 +2702,7 @@ ${JSON.stringify(
2667
2702
  if (media && media.tina && media.loadCustomStore) {
2668
2703
  ctx.addIssue({
2669
2704
  code: z.z.ZodIssueCode.custom,
2670
- message: "can not have both loadCustomStore and tina. Must use one or the other",
2705
+ message: "Cannot have both `loadCustomStore` and `tina`. Must use one or the other.",
2671
2706
  fatal: true,
2672
2707
  path: ["config", "media"]
2673
2708
  });
@@ -2676,7 +2711,7 @@ ${JSON.stringify(
2676
2711
  if (search && search.tina && search.searchClient) {
2677
2712
  ctx.addIssue({
2678
2713
  code: z.z.ZodIssueCode.custom,
2679
- message: "can not have both searchClient and tina. Must use one or the other",
2714
+ message: "Cannot have both `searchClient` and `tina`. Must use one or the other.",
2680
2715
  fatal: true,
2681
2716
  path: ["config", "search"]
2682
2717
  });
@@ -2694,7 +2729,7 @@ ${JSON.stringify(
2694
2729
  } catch (e) {
2695
2730
  if (e instanceof z.ZodError) {
2696
2731
  const errors = parseZodError({ zodError: e });
2697
- throw new TinaSchemaValidationError(errors.join(", \n"));
2732
+ throw new TinaSchemaValidationError(errors.join("\n"));
2698
2733
  }
2699
2734
  throw new Error(e);
2700
2735
  }
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;
@@ -1943,7 +1927,7 @@ class TinaSchema {
1943
1927
  }
1944
1928
  }
1945
1929
  };
1946
- this.walkFields = (cb) => {
1930
+ this.legacyWalkFields = (cb) => {
1947
1931
  const walk = (collectionOrObject, collection, path) => {
1948
1932
  if (collectionOrObject.templates) {
1949
1933
  collectionOrObject.templates.forEach((template) => {
@@ -1965,7 +1949,7 @@ class TinaSchema {
1965
1949
  collections.forEach((collection) => walk(collection, collection, []));
1966
1950
  };
1967
1951
  this.schema = config;
1968
- this.walkFields(({ field, collection, path }) => {
1952
+ this.legacyWalkFields(({ field, collection }) => {
1969
1953
  if (!("searchable" in field)) {
1970
1954
  if (field.type === "image") {
1971
1955
  field.searchable = false;
@@ -1986,6 +1970,67 @@ class TinaSchema {
1986
1970
  field.uid = field.uid || false;
1987
1971
  });
1988
1972
  }
1973
+ findReferencesFromCollection(name2) {
1974
+ const result = {};
1975
+ this.walkFields(({ field, collection: c, path }) => {
1976
+ if (c.name !== name2) {
1977
+ return;
1978
+ }
1979
+ if (field.type === "reference") {
1980
+ field.collections.forEach((name22) => {
1981
+ if (result[name22] === void 0) {
1982
+ result[name22] = [];
1983
+ }
1984
+ result[name22].push(path);
1985
+ });
1986
+ }
1987
+ });
1988
+ return result;
1989
+ }
1990
+ /**
1991
+ * Walk all fields in tina schema
1992
+ *
1993
+ * @param cb callback function invoked for each field
1994
+ */
1995
+ walkFields(cb) {
1996
+ const walk = (collectionOrObject, collection, path = "$") => {
1997
+ if (collectionOrObject.templates) {
1998
+ collectionOrObject.templates.forEach((template) => {
1999
+ const templatePath = `${path}.${template.name}`;
2000
+ template.fields.forEach((field) => {
2001
+ const fieldPath = field.list ? `${templatePath}[*].${field.name}` : `${templatePath}.${field.name}`;
2002
+ cb({ field, collection, path: fieldPath });
2003
+ if (field.type === "object") {
2004
+ walk(field, collection, fieldPath);
2005
+ }
2006
+ });
2007
+ });
2008
+ }
2009
+ if (collectionOrObject.fields) {
2010
+ collectionOrObject.fields.forEach((field) => {
2011
+ const fieldPath = field.list ? `${path}.${field.name}[*]` : `${path}.${field.name}`;
2012
+ cb({ field, collection, path: fieldPath });
2013
+ if (field.type === "object" && field.fields) {
2014
+ walk(field, collection, fieldPath);
2015
+ } else if (field.templates) {
2016
+ field.templates.forEach((template) => {
2017
+ const templatePath = `${fieldPath}.${template.name}`;
2018
+ template.fields.forEach((field2) => {
2019
+ const fieldPath2 = field2.list ? `${templatePath}[*].${field2.name}` : `${templatePath}.${field2.name}`;
2020
+ cb({ field: field2, collection, path: fieldPath2 });
2021
+ if (field2.type === "object") {
2022
+ walk(field2, collection, fieldPath2);
2023
+ }
2024
+ });
2025
+ });
2026
+ }
2027
+ });
2028
+ }
2029
+ };
2030
+ this.getCollections().forEach((collection) => {
2031
+ walk(collection, collection);
2032
+ });
2033
+ }
1989
2034
  /**
1990
2035
  * This function returns an array of glob matches for a given collection.
1991
2036
  *
@@ -2208,9 +2253,12 @@ const parseZodError = ({ zodError }) => {
2208
2253
  moreInfo.push(parseZodError({ zodError: unionError }));
2209
2254
  });
2210
2255
  }
2211
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
2256
+ const errorMessage = `${issue == null ? void 0 : issue.message}
2257
+ Additional information:
2258
+ - Error found at path ${issue.path.join(
2212
2259
  "."
2213
- )}`;
2260
+ )}
2261
+ `;
2214
2262
  const errorMessages = [errorMessage, ...moreInfo];
2215
2263
  return {
2216
2264
  errors: errorMessages
@@ -2245,6 +2293,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2245
2293
  });
2246
2294
  }
2247
2295
  });
2296
+ const duplicateFieldErrorMessage = (fields) => `Fields must have unique names. Found duplicate field names: [${fields}]`;
2297
+ const duplicateTemplateErrorMessage = (templates) => `Templates must have unique names. Found duplicate template names: [${templates}]`;
2298
+ const duplicateCollectionErrorMessage = (collection) => `Collections must have unique names. Found duplicate collection names: [${collection}]`;
2248
2299
  const TypeName = [
2249
2300
  "string",
2250
2301
  "boolean",
@@ -2255,10 +2306,11 @@ const TypeName = [
2255
2306
  "reference",
2256
2307
  "rich-text"
2257
2308
  ];
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
- )}`;
2309
+ const formattedTypes = ` - ${TypeName.join("\n - ")}`;
2310
+ const typeTypeError = `Invalid \`type\` property. \`type\` expected to be one of the following values:
2311
+ ${formattedTypes}`;
2312
+ const typeRequiredError = `Missing \`type\` property. Please add a \`type\` property with one of the following:
2313
+ ${formattedTypes}`;
2262
2314
  const Option = z.union(
2263
2315
  [
2264
2316
  z.string(),
@@ -2344,7 +2396,7 @@ const TinaFieldZod = z.lazy(() => {
2344
2396
  if (dups) {
2345
2397
  ctx.addIssue({
2346
2398
  code: z.ZodIssueCode.custom,
2347
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2399
+ message: duplicateFieldErrorMessage(dups)
2348
2400
  });
2349
2401
  }
2350
2402
  });
@@ -2354,21 +2406,21 @@ const TinaFieldZod = z.lazy(() => {
2354
2406
  invalid_type_error: typeTypeError,
2355
2407
  required_error: typeRequiredError
2356
2408
  }),
2357
- fields: z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2409
+ fields: z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2358
2410
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2359
2411
  if (dups) {
2360
2412
  ctx.addIssue({
2361
2413
  code: z.ZodIssueCode.custom,
2362
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2414
+ message: duplicateFieldErrorMessage(dups)
2363
2415
  });
2364
2416
  }
2365
2417
  }),
2366
- templates: z.array(TemplateTemp).min(1).optional().superRefine((val, ctx) => {
2418
+ templates: z.array(TemplateTemp).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2367
2419
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2368
2420
  if (dups) {
2369
2421
  ctx.addIssue({
2370
2422
  code: z.ZodIssueCode.custom,
2371
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2423
+ message: duplicateTemplateErrorMessage(dups)
2372
2424
  });
2373
2425
  }
2374
2426
  })
@@ -2383,7 +2435,7 @@ const TinaFieldZod = z.lazy(() => {
2383
2435
  if (dups) {
2384
2436
  ctx.addIssue({
2385
2437
  code: z.ZodIssueCode.custom,
2386
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2438
+ message: duplicateTemplateErrorMessage(dups)
2387
2439
  });
2388
2440
  }
2389
2441
  })
@@ -2403,10 +2455,17 @@ const TinaFieldZod = z.lazy(() => {
2403
2455
  ],
2404
2456
  {
2405
2457
  errorMap: (issue, ctx) => {
2406
- var _a;
2458
+ var _a, _b;
2407
2459
  if (issue.code === "invalid_union_discriminator") {
2460
+ if (!((_a = ctx.data) == null ? void 0 : _a.type)) {
2461
+ return {
2462
+ message: `Missing \`type\` property in field \`${ctx.data.name}\`. Please add a \`type\` property with one of the following:
2463
+ ${formattedTypes}`
2464
+ };
2465
+ }
2408
2466
  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(", ")}`
2467
+ 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:
2468
+ ${formattedTypes}`
2410
2469
  };
2411
2470
  }
2412
2471
  return {
@@ -2416,77 +2475,52 @@ const TinaFieldZod = z.lazy(() => {
2416
2475
  }
2417
2476
  ).superRefine((val, ctx) => {
2418
2477
  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
- }
2478
+ const stringifiedField = JSON.stringify(val, null, 2);
2479
+ if (val.isTitle && val.list) {
2480
+ ctx.addIssue({
2481
+ code: z.ZodIssueCode.custom,
2482
+ message: `\`list: true\` is not allowed when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2483
+ ${stringifiedField}`
2484
+ });
2485
+ }
2486
+ if (val.isTitle && !val.required) {
2487
+ ctx.addIssue({
2488
+ code: z.ZodIssueCode.custom,
2489
+ message: `Property \`required: true\` is required when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2490
+ ${stringifiedField}`
2491
+ });
2444
2492
  }
2445
- if (val.uid) {
2493
+ if (val.uid && val.list) {
2446
2494
  if (val.list) {
2447
2495
  ctx.addIssue({
2448
2496
  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
- `
2497
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2498
+ ${stringifiedField}`
2468
2499
  });
2469
2500
  }
2470
2501
  }
2502
+ if (val.uid && !val.required) {
2503
+ ctx.addIssue({
2504
+ code: z.ZodIssueCode.custom,
2505
+ message: `Property \`required: true\` is required when using \`uid\` for fields of \`type: string\`. Error found in field:
2506
+ ${stringifiedField}`
2507
+ });
2508
+ }
2471
2509
  }
2472
2510
  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) {
2511
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2476
2512
  ctx.addIssue({
2477
2513
  code: z.ZodIssueCode.custom,
2478
- message
2514
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2515
+ });
2516
+ return false;
2517
+ }
2518
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2519
+ ctx.addIssue({
2520
+ code: z.ZodIssueCode.custom,
2521
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2479
2522
  });
2480
2523
  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
2524
  }
2491
2525
  }
2492
2526
  return true;
@@ -2530,8 +2564,8 @@ const FORMATS = [
2530
2564
  ];
2531
2565
  const Template = z.object({
2532
2566
  label: z.string({
2533
- invalid_type_error: "label must be a string",
2534
- required_error: "label was not provided but is required"
2567
+ invalid_type_error: "Invalid data type for property `label`. Must be of type `string`",
2568
+ required_error: "Missing `label` property. Property `label` is required."
2535
2569
  }),
2536
2570
  name,
2537
2571
  fields: z.array(TinaFieldZod)
@@ -2541,7 +2575,7 @@ const Template = z.object({
2541
2575
  if (dups) {
2542
2576
  ctx.addIssue({
2543
2577
  code: z.ZodIssueCode.custom,
2544
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2578
+ message: duplicateFieldErrorMessage(dups)
2545
2579
  });
2546
2580
  }
2547
2581
  });
@@ -2551,7 +2585,7 @@ const CollectionBaseSchema = z.object({
2551
2585
  if (val === "relativePath") {
2552
2586
  ctx.addIssue({
2553
2587
  code: z.ZodIssueCode.custom,
2554
- message: `name cannot be 'relativePath'. 'relativePath' is a reserved field name.`
2588
+ message: "Invalid `name` property. `name` cannot be 'relativePath' as it is a reserved field name."
2555
2589
  });
2556
2590
  }
2557
2591
  }),
@@ -2559,7 +2593,7 @@ const CollectionBaseSchema = z.object({
2559
2593
  if (val === ".") {
2560
2594
  ctx.addIssue({
2561
2595
  code: z.ZodIssueCode.custom,
2562
- message: `path cannot be '.'. Please use '/' or '' instead. `
2596
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2563
2597
  });
2564
2598
  }
2565
2599
  }),
@@ -2568,63 +2602,64 @@ const CollectionBaseSchema = z.object({
2568
2602
  isDetached: z.boolean().optional()
2569
2603
  });
2570
2604
  const TinaCloudCollection = CollectionBaseSchema.extend({
2571
- fields: z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2605
+ fields: z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2572
2606
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2573
2607
  if (dups) {
2574
2608
  ctx.addIssue({
2575
2609
  code: z.ZodIssueCode.custom,
2576
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2610
+ message: duplicateFieldErrorMessage(dups)
2577
2611
  });
2578
2612
  }
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"
2613
+ }).superRefine((val, ctx) => {
2614
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2615
+ if (arr.length > 1) {
2616
+ ctx.addIssue({
2617
+ code: z.ZodIssueCode.custom,
2618
+ message: `The following fields have the property \`isTitle\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`isTitle\`.`
2619
+ });
2620
+ }
2621
+ }).superRefine((val, ctx) => {
2622
+ const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2623
+ if (arr.length > 2) {
2624
+ ctx.addIssue({
2625
+ code: z.ZodIssueCode.custom,
2626
+ message: `The following fields have the property \`uid\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`uid\`.`
2627
+ });
2605
2628
  }
2606
- ),
2607
- templates: z.array(Template).min(1).optional().superRefine((val, ctx) => {
2629
+ }).superRefine((val, ctx) => {
2630
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2631
+ if (arr.length > 2) {
2632
+ ctx.addIssue({
2633
+ code: z.ZodIssueCode.custom,
2634
+ message: `The following fields have \`type: password\`: [${arr.map((field) => field.name).join(", ")}]. Only one can be of \`type: password\`.`
2635
+ });
2636
+ }
2637
+ }),
2638
+ templates: z.array(Template).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2608
2639
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2609
2640
  if (dups) {
2610
2641
  ctx.addIssue({
2611
2642
  code: z.ZodIssueCode.custom,
2612
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2643
+ message: duplicateFieldErrorMessage(dups)
2613
2644
  });
2614
2645
  }
2615
2646
  })
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
- );
2647
+ }).superRefine((val, ctx) => {
2648
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2649
+ ctx.addIssue({
2650
+ code: z.ZodIssueCode.custom,
2651
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2652
+ });
2653
+ return false;
2654
+ }
2655
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2656
+ ctx.addIssue({
2657
+ code: z.ZodIssueCode.custom,
2658
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2659
+ });
2660
+ return false;
2661
+ }
2662
+ });
2628
2663
  const TinaCloudSchemaZod = z.object({
2629
2664
  collections: z.array(TinaCloudCollection),
2630
2665
  config: tinaConfigZod.optional()
@@ -2634,14 +2669,14 @@ const TinaCloudSchemaZod = z.object({
2634
2669
  if (dups) {
2635
2670
  ctx.addIssue({
2636
2671
  code: z.ZodIssueCode.custom,
2637
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2672
+ message: duplicateCollectionErrorMessage(dups),
2638
2673
  fatal: true
2639
2674
  });
2640
2675
  }
2641
2676
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2642
2677
  ctx.addIssue({
2643
2678
  code: z.ZodIssueCode.custom,
2644
- message: `Only one collection can be marked as isAuthCollection`,
2679
+ message: "Only one collection can be marked as `isAuthCollection`.",
2645
2680
  fatal: true
2646
2681
  });
2647
2682
  }
@@ -2649,7 +2684,7 @@ const TinaCloudSchemaZod = z.object({
2649
2684
  if (media && media.tina && media.loadCustomStore) {
2650
2685
  ctx.addIssue({
2651
2686
  code: z.ZodIssueCode.custom,
2652
- message: "can not have both loadCustomStore and tina. Must use one or the other",
2687
+ message: "Cannot have both `loadCustomStore` and `tina`. Must use one or the other.",
2653
2688
  fatal: true,
2654
2689
  path: ["config", "media"]
2655
2690
  });
@@ -2658,7 +2693,7 @@ const TinaCloudSchemaZod = z.object({
2658
2693
  if (search && search.tina && search.searchClient) {
2659
2694
  ctx.addIssue({
2660
2695
  code: z.ZodIssueCode.custom,
2661
- message: "can not have both searchClient and tina. Must use one or the other",
2696
+ message: "Cannot have both `searchClient` and `tina`. Must use one or the other.",
2662
2697
  fatal: true,
2663
2698
  path: ["config", "search"]
2664
2699
  });
@@ -2676,7 +2711,7 @@ const validateSchema = ({ schema }) => {
2676
2711
  } catch (e) {
2677
2712
  if (e instanceof ZodError) {
2678
2713
  const errors = parseZodError({ zodError: e });
2679
- throw new TinaSchemaValidationError(errors.join(", \n"));
2714
+ throw new TinaSchemaValidationError(errors.join("\n"));
2680
2715
  }
2681
2716
  throw new Error(e);
2682
2717
  }
@@ -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[];
@@ -1,4 +1,8 @@
1
- /**
2
-
3
- */
4
- export declare function addNamespaceToSchema<T extends object | string>(maybeNode: T, namespace?: string[]): T;
1
+ type Node = {
2
+ name?: string;
3
+ value?: string;
4
+ namespace?: string[];
5
+ [key: string]: any;
6
+ };
7
+ export declare function addNamespaceToSchema<T extends Node | string>(maybeNode: T, namespace?: string[]): T;
8
+ export {};
@@ -185,7 +185,7 @@ export type ReferenceField = (FieldGeneric<string, undefined, ReferenceFieldOpti
185
185
  export type PasswordField = (FieldGeneric<string, undefined> | FieldGeneric<string, false>) & BaseField & {
186
186
  type: 'password';
187
187
  };
188
- type toolbarItemName = 'heading' | 'link' | 'image' | 'quote' | 'ul' | 'ol' | 'code' | 'codeBlock' | 'bold' | 'italic' | 'raw' | 'embed' | 'mermaid';
188
+ type ToolbarOverrideType = 'heading' | 'link' | 'image' | 'quote' | 'ul' | 'ol' | 'code' | 'codeBlock' | 'bold' | 'italic' | 'raw' | 'embed' | 'mermaid' | 'table';
189
189
  type RichTextAst = {
190
190
  type: 'root';
191
191
  children: Record<string, unknown>[];
@@ -198,8 +198,14 @@ export type RichTextField<WithNamespace extends boolean = false> = (FieldGeneric
198
198
  * will be stored as frontmatter
199
199
  */
200
200
  isBody?: boolean;
201
- toolbarOverride?: toolbarItemName[];
201
+ /**@deprecated use overrides.toolbar */
202
+ toolbarOverride?: ToolbarOverrideType[];
202
203
  templates?: RichTextTemplate<WithNamespace>[];
204
+ overrides?: {
205
+ toolbar?: ToolbarOverrideType[];
206
+ /**Default set to true */
207
+ showFloatingToolbar?: boolean;
208
+ };
203
209
  /**
204
210
  * By default, Tina parses markdown with MDX, this is a more strict parser
205
211
  * that allows you to use structured content inside markdown (via `templates`).
@@ -357,6 +363,7 @@ interface AuthHooks {
357
363
  type AuthOptions = AuthHooks & AuthProvider;
358
364
  export interface Config<CMSCallback = undefined, FormifyCallback = undefined, DocumentCreatorCallback = undefined, Store = undefined, SearchClient = undefined> {
359
365
  contentApiUrlOverride?: string;
366
+ oauth2?: boolean;
360
367
  authProvider?: AuthProvider;
361
368
  admin?: {
362
369
  /**
@@ -394,7 +401,7 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
394
401
  token?: string | null;
395
402
  ui?: {
396
403
  /**
397
- * When using Tina Cloud's branching feature, provide the URL for your given branch
404
+ * When using TinaCloud's branching feature, provide the URL for your given branch
398
405
  *
399
406
  * Eg. If you're deplying to Vercel, and your repo name is 'my-app',
400
407
  * Vercel's preview URL would be based on the branch:
@@ -521,7 +528,7 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
521
528
  } | {
522
529
  searchClient?: never;
523
530
  /**
524
- * Use the Tina Cloud search index
531
+ * Use the TinaCloud search index
525
532
  */
526
533
  tina: {
527
534
  /**
@@ -548,7 +555,7 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
548
555
  maxSearchIndexFieldLength?: number;
549
556
  };
550
557
  /**
551
- * Used to override the default Tina Cloud API URL
558
+ * Used to override the default TinaCloud API URL
552
559
  *
553
560
  * [mostly for internal use only]
554
561
  */
@@ -630,6 +637,7 @@ type Document = {
630
637
  relativePath: string;
631
638
  filename: string;
632
639
  extension: string;
640
+ hasReferences?: boolean;
633
641
  };
634
642
  };
635
643
  export interface UICollection<Form = any, CMS = any, TinaForm = any> {
@@ -1,6 +1,3 @@
1
- /**
2
-
3
- */
4
1
  import { z } from 'zod';
5
2
  import type { TinaField as TinaFieldType } from '../types/index';
6
3
  export declare const TinaFieldZod: z.ZodType<TinaFieldType>;
@@ -1,6 +1,3 @@
1
- /**
2
-
3
- */
4
1
  import { z } from 'zod';
5
2
  export declare const CollectionBaseSchema: z.ZodObject<{
6
3
  label: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,3 @@
1
+ export declare const duplicateFieldErrorMessage: (fields: string) => string;
2
+ export declare const duplicateTemplateErrorMessage: (templates: string) => string;
3
+ export declare const duplicateCollectionErrorMessage: (collection: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/schema-tools",
3
- "version": "0.0.0-e0ddb8c-20241004065742",
3
+ "version": "0.0.0-e5c0e91-20250421003142",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "exports": {
@@ -23,16 +23,16 @@
23
23
  ]
24
24
  },
25
25
  "devDependencies": {
26
- "@types/jest": "^29.5.13",
26
+ "@types/jest": "^29.5.14",
27
27
  "@types/micromatch": "^4.0.9",
28
- "@types/react": "^18.3.10",
28
+ "@types/react": "^18.3.18",
29
29
  "@types/yup": "^0.29.14",
30
30
  "jest": "^29.7.0",
31
31
  "react": "^18.3.1",
32
32
  "ts-jest": "^29.2.5",
33
- "typescript": "^5.6.2",
33
+ "typescript": "^5.7.3",
34
34
  "yup": "^0.32.11",
35
- "@tinacms/scripts": "1.2.3"
35
+ "@tinacms/scripts": "1.3.4"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": ">=16.14.0",
@@ -48,7 +48,7 @@
48
48
  "dependencies": {
49
49
  "picomatch-browser": "2.2.6",
50
50
  "url-pattern": "^1.0.3",
51
- "zod": "^3.23.8"
51
+ "zod": "^3.24.2"
52
52
  },
53
53
  "scripts": {
54
54
  "build": "tinacms-scripts build",