@tinacms/schema-tools 0.0.0-f90ef4d-20241119072342 → 0.0.0-fd664d8-20250407054012

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;
@@ -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,20 +1988,67 @@
2004
1988
  field.uid = field.uid || false;
2005
1989
  });
2006
1990
  }
2007
- findReferences(name2) {
1991
+ findReferencesFromCollection(name2) {
2008
1992
  const result = {};
2009
1993
  this.walkFields(({ field, collection: c, path }) => {
1994
+ if (c.name !== name2) {
1995
+ return;
1996
+ }
2010
1997
  if (field.type === "reference") {
2011
- if (field.collections.includes(name2)) {
2012
- if (result[c.name] === void 0) {
2013
- result[c.name] = [];
1998
+ field.collections.forEach((name22) => {
1999
+ if (result[name22] === void 0) {
2000
+ result[name22] = [];
2014
2001
  }
2015
- result[c.name].push({ path, field });
2016
- }
2002
+ result[name22].push(path);
2003
+ });
2017
2004
  }
2018
2005
  });
2019
2006
  return result;
2020
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
+ }
2021
2052
  /**
2022
2053
  * This function returns an array of glob matches for a given collection.
2023
2054
  *
@@ -2240,9 +2271,12 @@
2240
2271
  moreInfo.push(parseZodError({ zodError: unionError }));
2241
2272
  });
2242
2273
  }
2243
- 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(
2244
2277
  "."
2245
- )}`;
2278
+ )}
2279
+ `;
2246
2280
  const errorMessages = [errorMessage, ...moreInfo];
2247
2281
  return {
2248
2282
  errors: errorMessages
@@ -2277,6 +2311,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2277
2311
  });
2278
2312
  }
2279
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}]`;
2280
2317
  const TypeName = [
2281
2318
  "string",
2282
2319
  "boolean",
@@ -2287,10 +2324,11 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2287
2324
  "reference",
2288
2325
  "rich-text"
2289
2326
  ];
2290
- const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
2291
- const typeRequiredError = `type is required and must be one of ${TypeName.join(
2292
- ", "
2293
- )}`;
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}`;
2294
2332
  const Option = z.z.union(
2295
2333
  [
2296
2334
  z.z.string(),
@@ -2376,7 +2414,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2376
2414
  if (dups) {
2377
2415
  ctx.addIssue({
2378
2416
  code: z.z.ZodIssueCode.custom,
2379
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2417
+ message: duplicateFieldErrorMessage(dups)
2380
2418
  });
2381
2419
  }
2382
2420
  });
@@ -2386,21 +2424,21 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2386
2424
  invalid_type_error: typeTypeError,
2387
2425
  required_error: typeRequiredError
2388
2426
  }),
2389
- 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) => {
2390
2428
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2391
2429
  if (dups) {
2392
2430
  ctx.addIssue({
2393
2431
  code: z.z.ZodIssueCode.custom,
2394
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2432
+ message: duplicateFieldErrorMessage(dups)
2395
2433
  });
2396
2434
  }
2397
2435
  }),
2398
- 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) => {
2399
2437
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2400
2438
  if (dups) {
2401
2439
  ctx.addIssue({
2402
2440
  code: z.z.ZodIssueCode.custom,
2403
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2441
+ message: duplicateTemplateErrorMessage(dups)
2404
2442
  });
2405
2443
  }
2406
2444
  })
@@ -2415,7 +2453,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2415
2453
  if (dups) {
2416
2454
  ctx.addIssue({
2417
2455
  code: z.z.ZodIssueCode.custom,
2418
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2456
+ message: duplicateTemplateErrorMessage(dups)
2419
2457
  });
2420
2458
  }
2421
2459
  })
@@ -2435,10 +2473,17 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2435
2473
  ],
2436
2474
  {
2437
2475
  errorMap: (issue, ctx) => {
2438
- var _a;
2476
+ var _a, _b;
2439
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
+ }
2440
2484
  return {
2441
- 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}`
2442
2487
  };
2443
2488
  }
2444
2489
  return {
@@ -2448,77 +2493,52 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2448
2493
  }
2449
2494
  ).superRefine((val, ctx) => {
2450
2495
  if (val.type === "string") {
2451
- if (val.isTitle) {
2452
- if (val.list) {
2453
- ctx.addIssue({
2454
- code: z.z.ZodIssueCode.custom,
2455
- message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
2456
- ${JSON.stringify(
2457
- val,
2458
- null,
2459
- 2
2460
- )}
2461
- `
2462
- });
2463
- }
2464
- if (!val.required) {
2465
- ctx.addIssue({
2466
- code: z.z.ZodIssueCode.custom,
2467
- message: `Must have { required: true } when using \`isTitle\` Error in value
2468
- ${JSON.stringify(
2469
- val,
2470
- null,
2471
- 2
2472
- )}
2473
- `
2474
- });
2475
- }
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
+ });
2476
2503
  }
2477
- if (val.uid) {
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
+ });
2510
+ }
2511
+ if (val.uid && val.list) {
2478
2512
  if (val.list) {
2479
2513
  ctx.addIssue({
2480
2514
  code: z.z.ZodIssueCode.custom,
2481
- message: `Can not have \`list: true\` when using \`uid\`. Error in value
2482
- ${JSON.stringify(
2483
- val,
2484
- null,
2485
- 2
2486
- )}
2487
- `
2488
- });
2489
- }
2490
- if (!val.required) {
2491
- ctx.addIssue({
2492
- code: z.z.ZodIssueCode.custom,
2493
- message: `Must have { required: true } when using \`uid\` Error in value
2494
- ${JSON.stringify(
2495
- val,
2496
- null,
2497
- 2
2498
- )}
2499
- `
2515
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2516
+ ${stringifiedField}`
2500
2517
  });
2501
2518
  }
2502
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
+ }
2503
2527
  }
2504
2528
  if (val.type === "object") {
2505
- const message = "Must provide one of templates or fields in your collection";
2506
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2507
- if (!isValid) {
2529
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2508
2530
  ctx.addIssue({
2509
2531
  code: z.z.ZodIssueCode.custom,
2510
- 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."
2511
2540
  });
2512
2541
  return false;
2513
- } else {
2514
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2515
- if (!isValid) {
2516
- ctx.addIssue({
2517
- code: z.z.ZodIssueCode.custom,
2518
- message
2519
- });
2520
- }
2521
- return isValid;
2522
2542
  }
2523
2543
  }
2524
2544
  return true;
@@ -2562,8 +2582,8 @@ ${JSON.stringify(
2562
2582
  ];
2563
2583
  const Template = z.z.object({
2564
2584
  label: z.z.string({
2565
- invalid_type_error: "label must be a string",
2566
- 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."
2567
2587
  }),
2568
2588
  name,
2569
2589
  fields: z.z.array(TinaFieldZod)
@@ -2573,7 +2593,7 @@ ${JSON.stringify(
2573
2593
  if (dups) {
2574
2594
  ctx.addIssue({
2575
2595
  code: z.z.ZodIssueCode.custom,
2576
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2596
+ message: duplicateFieldErrorMessage(dups)
2577
2597
  });
2578
2598
  }
2579
2599
  });
@@ -2583,7 +2603,7 @@ ${JSON.stringify(
2583
2603
  if (val === "relativePath") {
2584
2604
  ctx.addIssue({
2585
2605
  code: z.z.ZodIssueCode.custom,
2586
- 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."
2587
2607
  });
2588
2608
  }
2589
2609
  }),
@@ -2591,7 +2611,7 @@ ${JSON.stringify(
2591
2611
  if (val === ".") {
2592
2612
  ctx.addIssue({
2593
2613
  code: z.z.ZodIssueCode.custom,
2594
- message: `path cannot be '.'. Please use '/' or '' instead. `
2614
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2595
2615
  });
2596
2616
  }
2597
2617
  }),
@@ -2600,63 +2620,64 @@ ${JSON.stringify(
2600
2620
  isDetached: z.z.boolean().optional()
2601
2621
  });
2602
2622
  const TinaCloudCollection = CollectionBaseSchema.extend({
2603
- 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) => {
2604
2624
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2605
2625
  if (dups) {
2606
2626
  ctx.addIssue({
2607
2627
  code: z.z.ZodIssueCode.custom,
2608
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2628
+ message: duplicateFieldErrorMessage(dups)
2609
2629
  });
2610
2630
  }
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 === "string" && x.isTitle)) || [];
2615
- return arr.length < 2;
2616
- },
2617
- {
2618
- message: "Fields can only have one use of `isTitle`"
2619
- }
2620
- ).refine(
2621
- // It is valid if it is 0 or 1
2622
- (val) => {
2623
- const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2624
- return arr.length < 2;
2625
- },
2626
- {
2627
- message: "Fields can only have one use of `uid`"
2628
- }
2629
- ).refine(
2630
- // It is valid if it is 0 or 1
2631
- (val) => {
2632
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2633
- return arr.length < 2;
2634
- },
2635
- {
2636
- 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
+ });
2637
2638
  }
2638
- ),
2639
- templates: z.z.array(Template).min(1).optional().superRefine((val, ctx) => {
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
+ });
2646
+ }
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) => {
2640
2657
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2641
2658
  if (dups) {
2642
2659
  ctx.addIssue({
2643
2660
  code: z.z.ZodIssueCode.custom,
2644
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2661
+ message: duplicateFieldErrorMessage(dups)
2645
2662
  });
2646
2663
  }
2647
2664
  })
2648
- }).refine(
2649
- (val) => {
2650
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2651
- if (!isValid) {
2652
- return false;
2653
- } else {
2654
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2655
- return isValid;
2656
- }
2657
- },
2658
- { message: "Must provide one of templates or fields in your collection" }
2659
- );
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
+ });
2660
2681
  const TinaCloudSchemaZod = z.z.object({
2661
2682
  collections: z.z.array(TinaCloudCollection),
2662
2683
  config: tinaConfigZod.optional()
@@ -2666,14 +2687,14 @@ ${JSON.stringify(
2666
2687
  if (dups) {
2667
2688
  ctx.addIssue({
2668
2689
  code: z.z.ZodIssueCode.custom,
2669
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2690
+ message: duplicateCollectionErrorMessage(dups),
2670
2691
  fatal: true
2671
2692
  });
2672
2693
  }
2673
2694
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2674
2695
  ctx.addIssue({
2675
2696
  code: z.z.ZodIssueCode.custom,
2676
- message: `Only one collection can be marked as isAuthCollection`,
2697
+ message: "Only one collection can be marked as `isAuthCollection`.",
2677
2698
  fatal: true
2678
2699
  });
2679
2700
  }
@@ -2681,7 +2702,7 @@ ${JSON.stringify(
2681
2702
  if (media && media.tina && media.loadCustomStore) {
2682
2703
  ctx.addIssue({
2683
2704
  code: z.z.ZodIssueCode.custom,
2684
- 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.",
2685
2706
  fatal: true,
2686
2707
  path: ["config", "media"]
2687
2708
  });
@@ -2690,7 +2711,7 @@ ${JSON.stringify(
2690
2711
  if (search && search.tina && search.searchClient) {
2691
2712
  ctx.addIssue({
2692
2713
  code: z.z.ZodIssueCode.custom,
2693
- 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.",
2694
2715
  fatal: true,
2695
2716
  path: ["config", "search"]
2696
2717
  });
@@ -2708,7 +2729,7 @@ ${JSON.stringify(
2708
2729
  } catch (e) {
2709
2730
  if (e instanceof z.ZodError) {
2710
2731
  const errors = parseZodError({ zodError: e });
2711
- throw new TinaSchemaValidationError(errors.join(", \n"));
2732
+ throw new TinaSchemaValidationError(errors.join("\n"));
2712
2733
  }
2713
2734
  throw new Error(e);
2714
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,20 +1970,67 @@ class TinaSchema {
1986
1970
  field.uid = field.uid || false;
1987
1971
  });
1988
1972
  }
1989
- findReferences(name2) {
1973
+ findReferencesFromCollection(name2) {
1990
1974
  const result = {};
1991
1975
  this.walkFields(({ field, collection: c, path }) => {
1976
+ if (c.name !== name2) {
1977
+ return;
1978
+ }
1992
1979
  if (field.type === "reference") {
1993
- if (field.collections.includes(name2)) {
1994
- if (result[c.name] === void 0) {
1995
- result[c.name] = [];
1980
+ field.collections.forEach((name22) => {
1981
+ if (result[name22] === void 0) {
1982
+ result[name22] = [];
1996
1983
  }
1997
- result[c.name].push({ path, field });
1998
- }
1984
+ result[name22].push(path);
1985
+ });
1999
1986
  }
2000
1987
  });
2001
1988
  return result;
2002
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
+ }
2003
2034
  /**
2004
2035
  * This function returns an array of glob matches for a given collection.
2005
2036
  *
@@ -2222,9 +2253,12 @@ const parseZodError = ({ zodError }) => {
2222
2253
  moreInfo.push(parseZodError({ zodError: unionError }));
2223
2254
  });
2224
2255
  }
2225
- 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(
2226
2259
  "."
2227
- )}`;
2260
+ )}
2261
+ `;
2228
2262
  const errorMessages = [errorMessage, ...moreInfo];
2229
2263
  return {
2230
2264
  errors: errorMessages
@@ -2259,6 +2293,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2259
2293
  });
2260
2294
  }
2261
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}]`;
2262
2299
  const TypeName = [
2263
2300
  "string",
2264
2301
  "boolean",
@@ -2269,10 +2306,11 @@ const TypeName = [
2269
2306
  "reference",
2270
2307
  "rich-text"
2271
2308
  ];
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
- )}`;
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}`;
2276
2314
  const Option = z.union(
2277
2315
  [
2278
2316
  z.string(),
@@ -2358,7 +2396,7 @@ const TinaFieldZod = z.lazy(() => {
2358
2396
  if (dups) {
2359
2397
  ctx.addIssue({
2360
2398
  code: z.ZodIssueCode.custom,
2361
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2399
+ message: duplicateFieldErrorMessage(dups)
2362
2400
  });
2363
2401
  }
2364
2402
  });
@@ -2368,21 +2406,21 @@ const TinaFieldZod = z.lazy(() => {
2368
2406
  invalid_type_error: typeTypeError,
2369
2407
  required_error: typeRequiredError
2370
2408
  }),
2371
- 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) => {
2372
2410
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2373
2411
  if (dups) {
2374
2412
  ctx.addIssue({
2375
2413
  code: z.ZodIssueCode.custom,
2376
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2414
+ message: duplicateFieldErrorMessage(dups)
2377
2415
  });
2378
2416
  }
2379
2417
  }),
2380
- 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) => {
2381
2419
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2382
2420
  if (dups) {
2383
2421
  ctx.addIssue({
2384
2422
  code: z.ZodIssueCode.custom,
2385
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2423
+ message: duplicateTemplateErrorMessage(dups)
2386
2424
  });
2387
2425
  }
2388
2426
  })
@@ -2397,7 +2435,7 @@ const TinaFieldZod = z.lazy(() => {
2397
2435
  if (dups) {
2398
2436
  ctx.addIssue({
2399
2437
  code: z.ZodIssueCode.custom,
2400
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2438
+ message: duplicateTemplateErrorMessage(dups)
2401
2439
  });
2402
2440
  }
2403
2441
  })
@@ -2417,10 +2455,17 @@ const TinaFieldZod = z.lazy(() => {
2417
2455
  ],
2418
2456
  {
2419
2457
  errorMap: (issue, ctx) => {
2420
- var _a;
2458
+ var _a, _b;
2421
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
+ }
2422
2466
  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(", ")}`
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}`
2424
2469
  };
2425
2470
  }
2426
2471
  return {
@@ -2430,77 +2475,52 @@ const TinaFieldZod = z.lazy(() => {
2430
2475
  }
2431
2476
  ).superRefine((val, ctx) => {
2432
2477
  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
- }
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
+ });
2458
2485
  }
2459
- if (val.uid) {
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
+ });
2492
+ }
2493
+ if (val.uid && val.list) {
2460
2494
  if (val.list) {
2461
2495
  ctx.addIssue({
2462
2496
  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
- `
2497
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2498
+ ${stringifiedField}`
2482
2499
  });
2483
2500
  }
2484
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
+ }
2485
2509
  }
2486
2510
  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) {
2511
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2490
2512
  ctx.addIssue({
2491
2513
  code: z.ZodIssueCode.custom,
2492
- 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."
2493
2522
  });
2494
2523
  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
2524
  }
2505
2525
  }
2506
2526
  return true;
@@ -2544,8 +2564,8 @@ const FORMATS = [
2544
2564
  ];
2545
2565
  const Template = z.object({
2546
2566
  label: z.string({
2547
- invalid_type_error: "label must be a string",
2548
- 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."
2549
2569
  }),
2550
2570
  name,
2551
2571
  fields: z.array(TinaFieldZod)
@@ -2555,7 +2575,7 @@ const Template = z.object({
2555
2575
  if (dups) {
2556
2576
  ctx.addIssue({
2557
2577
  code: z.ZodIssueCode.custom,
2558
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2578
+ message: duplicateFieldErrorMessage(dups)
2559
2579
  });
2560
2580
  }
2561
2581
  });
@@ -2565,7 +2585,7 @@ const CollectionBaseSchema = z.object({
2565
2585
  if (val === "relativePath") {
2566
2586
  ctx.addIssue({
2567
2587
  code: z.ZodIssueCode.custom,
2568
- 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."
2569
2589
  });
2570
2590
  }
2571
2591
  }),
@@ -2573,7 +2593,7 @@ const CollectionBaseSchema = z.object({
2573
2593
  if (val === ".") {
2574
2594
  ctx.addIssue({
2575
2595
  code: z.ZodIssueCode.custom,
2576
- message: `path cannot be '.'. Please use '/' or '' instead. `
2596
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2577
2597
  });
2578
2598
  }
2579
2599
  }),
@@ -2582,63 +2602,64 @@ const CollectionBaseSchema = z.object({
2582
2602
  isDetached: z.boolean().optional()
2583
2603
  });
2584
2604
  const TinaCloudCollection = CollectionBaseSchema.extend({
2585
- 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) => {
2586
2606
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2587
2607
  if (dups) {
2588
2608
  ctx.addIssue({
2589
2609
  code: z.ZodIssueCode.custom,
2590
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2610
+ message: duplicateFieldErrorMessage(dups)
2591
2611
  });
2592
2612
  }
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"
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
+ });
2619
2620
  }
2620
- ),
2621
- templates: z.array(Template).min(1).optional().superRefine((val, ctx) => {
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
+ });
2628
+ }
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) => {
2622
2639
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2623
2640
  if (dups) {
2624
2641
  ctx.addIssue({
2625
2642
  code: z.ZodIssueCode.custom,
2626
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2643
+ message: duplicateFieldErrorMessage(dups)
2627
2644
  });
2628
2645
  }
2629
2646
  })
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
- );
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
+ });
2642
2663
  const TinaCloudSchemaZod = z.object({
2643
2664
  collections: z.array(TinaCloudCollection),
2644
2665
  config: tinaConfigZod.optional()
@@ -2648,14 +2669,14 @@ const TinaCloudSchemaZod = z.object({
2648
2669
  if (dups) {
2649
2670
  ctx.addIssue({
2650
2671
  code: z.ZodIssueCode.custom,
2651
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2672
+ message: duplicateCollectionErrorMessage(dups),
2652
2673
  fatal: true
2653
2674
  });
2654
2675
  }
2655
2676
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2656
2677
  ctx.addIssue({
2657
2678
  code: z.ZodIssueCode.custom,
2658
- message: `Only one collection can be marked as isAuthCollection`,
2679
+ message: "Only one collection can be marked as `isAuthCollection`.",
2659
2680
  fatal: true
2660
2681
  });
2661
2682
  }
@@ -2663,7 +2684,7 @@ const TinaCloudSchemaZod = z.object({
2663
2684
  if (media && media.tina && media.loadCustomStore) {
2664
2685
  ctx.addIssue({
2665
2686
  code: z.ZodIssueCode.custom,
2666
- 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.",
2667
2688
  fatal: true,
2668
2689
  path: ["config", "media"]
2669
2690
  });
@@ -2672,7 +2693,7 @@ const TinaCloudSchemaZod = z.object({
2672
2693
  if (search && search.tina && search.searchClient) {
2673
2694
  ctx.addIssue({
2674
2695
  code: z.ZodIssueCode.custom,
2675
- 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.",
2676
2697
  fatal: true,
2677
2698
  path: ["config", "search"]
2678
2699
  });
@@ -2690,7 +2711,7 @@ const validateSchema = ({ schema }) => {
2690
2711
  } catch (e) {
2691
2712
  if (e instanceof ZodError) {
2692
2713
  const errors = parseZodError({ zodError: e });
2693
- throw new TinaSchemaValidationError(errors.join(", \n"));
2714
+ throw new TinaSchemaValidationError(errors.join("\n"));
2694
2715
  }
2695
2716
  throw new Error(e);
2696
2717
  }
@@ -28,10 +28,7 @@ export declare class TinaSchema {
28
28
  } & Schema);
29
29
  getIsTitleFieldName: (collection: string) => string;
30
30
  getCollectionsByName: (collectionNames: string[]) => Collection<true>[];
31
- findReferences(name: string): Record<string, {
32
- path: string[];
33
- field: TinaField;
34
- }[]>;
31
+ findReferencesFromCollection(name: string): Record<string, string[]>;
35
32
  getCollection: (collectionName: string) => Collection<true>;
36
33
  getCollections: () => Collection<true>[];
37
34
  getCollectionByFullPath: (filepath: string) => Collection<true>;
@@ -63,7 +60,26 @@ export declare class TinaSchema {
63
60
  *
64
61
  */
65
62
  getTemplatesForCollectable: (collection: Collectable) => CollectionTemplateable;
66
- 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: {
67
83
  field: TinaField;
68
84
  collection: Collection;
69
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 {};
@@ -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
+ /**@deprecated use overrides.toolbar */
201
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`).
@@ -394,7 +400,7 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
394
400
  token?: string | null;
395
401
  ui?: {
396
402
  /**
397
- * When using Tina Cloud's branching feature, provide the URL for your given branch
403
+ * When using TinaCloud's branching feature, provide the URL for your given branch
398
404
  *
399
405
  * Eg. If you're deplying to Vercel, and your repo name is 'my-app',
400
406
  * Vercel's preview URL would be based on the branch:
@@ -521,7 +527,7 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
521
527
  } | {
522
528
  searchClient?: never;
523
529
  /**
524
- * Use the Tina Cloud search index
530
+ * Use the TinaCloud search index
525
531
  */
526
532
  tina: {
527
533
  /**
@@ -548,7 +554,7 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
548
554
  maxSearchIndexFieldLength?: number;
549
555
  };
550
556
  /**
551
- * Used to override the default Tina Cloud API URL
557
+ * Used to override the default TinaCloud API URL
552
558
  *
553
559
  * [mostly for internal use only]
554
560
  */
@@ -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-f90ef4d-20241119072342",
3
+ "version": "0.0.0-fd664d8-20250407054012",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "exports": {
@@ -25,14 +25,14 @@
25
25
  "devDependencies": {
26
26
  "@types/jest": "^29.5.14",
27
27
  "@types/micromatch": "^4.0.9",
28
- "@types/react": "^18.3.12",
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.3",
33
+ "typescript": "^5.7.3",
34
34
  "yup": "^0.32.11",
35
- "@tinacms/scripts": "0.0.0-f90ef4d-20241119072342"
35
+ "@tinacms/scripts": "0.0.0-fd664d8-20250407054012"
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",