@valbuild/core 0.125.0 → 0.126.0

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/declarations/src/index.d.ts +3 -1
  3. package/dist/declarations/src/initSchema.d.ts +46 -15
  4. package/dist/declarations/src/initVal.d.ts +108 -0
  5. package/dist/declarations/src/render.d.ts +5 -4
  6. package/dist/declarations/src/schema/array.d.ts +65 -0
  7. package/dist/declarations/src/schema/boolean.d.ts +44 -0
  8. package/dist/declarations/src/schema/code.d.ts +63 -0
  9. package/dist/declarations/src/schema/color.d.ts +54 -0
  10. package/dist/declarations/src/schema/date.d.ts +64 -2
  11. package/dist/declarations/src/schema/datetime.d.ts +87 -2
  12. package/dist/declarations/src/schema/discriminatedUnion.d.ts +166 -0
  13. package/dist/declarations/src/schema/enum.d.ts +122 -0
  14. package/dist/declarations/src/schema/file.d.ts +77 -0
  15. package/dist/declarations/src/schema/image.d.ts +102 -0
  16. package/dist/declarations/src/schema/index.d.ts +42 -2
  17. package/dist/declarations/src/schema/keyOf.d.ts +50 -0
  18. package/dist/declarations/src/schema/literal.d.ts +46 -0
  19. package/dist/declarations/src/schema/number.d.ts +58 -0
  20. package/dist/declarations/src/schema/object.d.ts +63 -0
  21. package/dist/declarations/src/schema/record.d.ts +136 -0
  22. package/dist/declarations/src/schema/richtext.d.ts +78 -0
  23. package/dist/declarations/src/schema/route.d.ts +44 -0
  24. package/dist/declarations/src/schema/string.d.ts +99 -0
  25. package/dist/declarations/src/schema/union.d.ts +54 -91
  26. package/dist/{index-a3846321.cjs.prod.js → index-6db78f3c.cjs.prod.js} +1953 -449
  27. package/dist/{index-daa122af.cjs.dev.js → index-b41b638f.cjs.dev.js} +1953 -449
  28. package/dist/{index-915dd675.esm.js → index-cd25c2dc.esm.js} +1952 -449
  29. package/dist/valbuild-core.cjs.dev.js +3 -2
  30. package/dist/valbuild-core.cjs.prod.js +3 -2
  31. package/dist/valbuild-core.esm.js +1 -1
  32. package/package.json +1 -1
  33. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  34. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  35. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -464,6 +464,23 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
464
464
  _this.isMultiline = isMultiline;
465
465
  return _this;
466
466
  }
467
+
468
+ /**
469
+ * Describe this field.
470
+ *
471
+ * The description is shown next to the field's label in the Val editor, so
472
+ * it is where you say what an editor needs to know but the field name cannot
473
+ * carry. It also travels in the serialized schema, which is what the AI
474
+ * assistant and the MCP tools read.
475
+ *
476
+ * Pass `null` to clear a description set earlier.
477
+ *
478
+ * @example
479
+ * const schema = s
480
+ * .string()
481
+ * .describe("Shown in the browser tab and in search results");
482
+ * export default c.define("/example.val.ts", schema, "Hello");
483
+ */
467
484
  _inherits(StringSchema, _Schema);
468
485
  return _createClass(StringSchema, [{
469
486
  key: "describe",
@@ -473,12 +490,24 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
473
490
 
474
491
  /**
475
492
  * @deprecated Use `minLength` instead
493
+ *
494
+ * @example
495
+ * const schema = s.string().minLength(2);
496
+ * export default c.define("/example.val.ts", schema, "Hi");
476
497
  */
477
498
  }, {
478
499
  key: "min",
479
500
  value: function min(minLength) {
480
501
  return this.minLength(minLength);
481
502
  }
503
+
504
+ /**
505
+ * Validate that the string is at least `minLength` characters long.
506
+ *
507
+ * @example
508
+ * const schema = s.string().minLength(2);
509
+ * export default c.define("/example.val.ts", schema, "Hi");
510
+ */
482
511
  }, {
483
512
  key: "minLength",
484
513
  value: function minLength(_minLength) {
@@ -489,12 +518,24 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
489
518
 
490
519
  /**
491
520
  * @deprecated Use `maxLength` instead
521
+ *
522
+ * @example
523
+ * const schema = s.string().maxLength(60);
524
+ * export default c.define("/example.val.ts", schema, "A title that fits");
492
525
  */
493
526
  }, {
494
527
  key: "max",
495
528
  value: function max(maxLength) {
496
529
  return this.maxLength(maxLength);
497
530
  }
531
+
532
+ /**
533
+ * Validate that the string is at most `maxLength` characters long.
534
+ *
535
+ * @example
536
+ * const schema = s.string().maxLength(60);
537
+ * export default c.define("/example.val.ts", schema, "A title that fits");
538
+ */
498
539
  }, {
499
540
  key: "maxLength",
500
541
  value: function maxLength(_maxLength) {
@@ -502,6 +543,19 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
502
543
  maxLength: _maxLength
503
544
  }), this.opt, this.isRaw, this.customValidateFunctions, this.renderInput, this.isReadonly, this.isHidden, this.description, this.previewInput, this.isMultiline);
504
545
  }
546
+
547
+ /**
548
+ * Validate that the string matches `regexp`.
549
+ *
550
+ * `message` is what the editor is shown when it does not match. Without one
551
+ * the error names the pattern, which is rarely what a non-developer needs.
552
+ *
553
+ * @example
554
+ * const schema = s
555
+ * .string()
556
+ * .regexp(/^[a-z0-9-]+$/, "Lower case letters, digits and dashes only");
557
+ * export default c.define("/example.val.ts", schema, "my-slug");
558
+ */
505
559
  }, {
506
560
  key: "regexp",
507
561
  value: function regexp(_regexp, message) {
@@ -510,6 +564,27 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
510
564
  regExpMessage: message
511
565
  }), this.opt, this.isRaw, this.customValidateFunctions, this.renderInput, this.isReadonly, this.isHidden, this.description, this.previewInput, this.isMultiline);
512
566
  }
567
+
568
+ /**
569
+ * Add a custom validation rule to this field.
570
+ *
571
+ * The function is called with the field's value and returns `false` when the
572
+ * value is fine, or a STRING with the message to show when it is not. Call it
573
+ * more than once to add more rules — they all run, and every message is
574
+ * reported.
575
+ *
576
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
577
+ * when the value is fine, and `true` is not one of the two answers.
578
+ *
579
+ * Validation runs in the Studio as you type, in `npx val validate` and
580
+ * before a publish.
581
+ *
582
+ * @example
583
+ * const schema = s.string().validate((val) =>
584
+ * val.trim() === val ? false : "Must not start or end with a space",
585
+ * );
586
+ * export default c.define("/example.val.ts", schema, "Hello");
587
+ */
513
588
  }, {
514
589
  key: "validate",
515
590
  value: function validate(validationFunction) {
@@ -600,6 +675,26 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
600
675
  var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
601
676
  return new StringSchema(this.options, this.opt, this.isRaw, this.customValidateFunctions, this.renderInput, this.isReadonly, isHidden, this.description, this.previewInput, this.isMultiline);
602
677
  }
678
+
679
+ /**
680
+ * Do not stega encode this string.
681
+ *
682
+ * Val normally encodes an invisible pointer back to the source into every
683
+ * string it hands your app, which is what makes click-to-edit work. A raw
684
+ * string is handed over exactly as written — use it wherever the value is
685
+ * not rendered as visible text: a slug, a class name, an `id`, a URL,
686
+ * anything parsed or compared.
687
+ *
688
+ * @example
689
+ * const schema = s.object({
690
+ * title: s.string(),
691
+ * slug: s.string().raw(),
692
+ * });
693
+ * export default c.define("/example.val.ts", schema, {
694
+ * title: "Hello",
695
+ * slug: "hello",
696
+ * });
697
+ */
603
698
  }, {
604
699
  key: "raw",
605
700
  value: function raw() {
@@ -665,6 +760,10 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
665
760
  *
666
761
  * Static configuration, not a callback — see `render.ts`. What a CONTAINER
667
762
  * shows for its items is a `preview`, which is a different thing entirely.
763
+ *
764
+ * @example
765
+ * const schema = s.array(s.string().render({ as: "inline" }));
766
+ * export default c.define("/example.val.ts", schema, ["First", "Second"]);
668
767
  */
669
768
  }, {
670
769
  key: "render",
@@ -676,6 +775,12 @@ var StringSchema = /*#__PURE__*/function (_Schema) {
676
775
  * How this VALUE is shown where a preview of it is needed — a row in a
677
776
  * sortable list, a reference dropdown, a search hit. Never how the field
678
777
  * itself is edited (that is `render`). See `preview.ts`.
778
+ *
779
+ * @example
780
+ * const schema = s.array(
781
+ * s.string().preview(({ val }) => ({ title: val })),
782
+ * );
783
+ * export default c.define("/example.val.ts", schema, ["First", "Second"]);
679
784
  */
680
785
  }, {
681
786
  key: "preview",
@@ -736,12 +841,59 @@ var ObjectSchema = /*#__PURE__*/function (_Schema) {
736
841
  _this.previewInput = previewInput;
737
842
  return _this;
738
843
  }
844
+
845
+ /**
846
+ * Describe this field.
847
+ *
848
+ * The description is shown next to the field's label in the Val editor, so
849
+ * it is where you say what an editor needs to know but the field name cannot
850
+ * carry. It also travels in the serialized schema, which is what the AI
851
+ * assistant and the MCP tools read.
852
+ *
853
+ * Pass `null` to clear a description set earlier.
854
+ *
855
+ * @example
856
+ * const schema = s
857
+ * .object({ street: s.string(), city: s.string() })
858
+ * .describe("Where the office is — shown on the contact page");
859
+ * export default c.define("/example.val.ts", schema, {
860
+ * street: "Torggata 1",
861
+ * city: "Oslo",
862
+ * });
863
+ */
739
864
  _inherits(ObjectSchema, _Schema);
740
865
  return _createClass(ObjectSchema, [{
741
866
  key: "describe",
742
867
  value: function describe(description) {
743
868
  return new ObjectSchema(this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
744
869
  }
870
+
871
+ /**
872
+ * Add a custom validation rule to this field.
873
+ *
874
+ * The function is called with the field's value and returns `false` when the
875
+ * value is fine, or a STRING with the message to show when it is not. Call it
876
+ * more than once to add more rules — they all run, and every message is
877
+ * reported.
878
+ *
879
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
880
+ * when the value is fine, and `true` is not one of the two answers.
881
+ *
882
+ * Validation runs in the Studio as you type, in `npx val validate` and
883
+ * before a publish.
884
+ *
885
+ * An object validator is the place for a rule that spans FIELDS — one field
886
+ * validated on its own belongs on that field's schema, where the error lands
887
+ * on the field itself.
888
+ *
889
+ * @example
890
+ * const schema = s
891
+ * .object({ from: s.number(), to: s.number() })
892
+ * .validate((val) =>
893
+ * val.from <= val.to ? false : "'from' must not be after 'to'",
894
+ * );
895
+ * export default c.define("/example.val.ts", schema, { from: 1, to: 4 });
896
+ */
745
897
  }, {
746
898
  key: "validate",
747
899
  value: function validate(validationFunction) {
@@ -894,6 +1046,16 @@ var ObjectSchema = /*#__PURE__*/function (_Schema) {
894
1046
  * instead of a preview row that navigates to it.
895
1047
  *
896
1048
  * Static configuration, not a callback — see `render.ts`.
1049
+ *
1050
+ * @example
1051
+ * // A page builder: each row edits the object in place, instead of
1052
+ * // navigating to it.
1053
+ * const block = s
1054
+ * .object({ heading: s.string(), body: s.string() })
1055
+ * .render({ as: "inline" });
1056
+ * export default c.define("/example.val.ts", s.array(block), [
1057
+ * { heading: "Hello", body: "World" },
1058
+ * ]);
897
1059
  */
898
1060
  }, {
899
1061
  key: "render",
@@ -905,6 +1067,14 @@ var ObjectSchema = /*#__PURE__*/function (_Schema) {
905
1067
  * How this VALUE is shown where a preview of it is needed — a row in a
906
1068
  * sortable list, a reference dropdown, a search hit. Never how the field
907
1069
  * itself is edited (that is `render`). See `preview.ts`.
1070
+ *
1071
+ * @example
1072
+ * const author = s
1073
+ * .object({ name: s.string(), role: s.string() })
1074
+ * .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
1075
+ * export default c.define("/example.val.ts", s.array(author), [
1076
+ * { name: "Ada", role: "Engineer" },
1077
+ * ]);
908
1078
  */
909
1079
  }, {
910
1080
  key: "preview",
@@ -1007,12 +1177,54 @@ var ArraySchema = /*#__PURE__*/function (_Schema) {
1007
1177
  _this.renderInput = renderInput;
1008
1178
  return _this;
1009
1179
  }
1180
+
1181
+ /**
1182
+ * Describe this field.
1183
+ *
1184
+ * The description is shown next to the field's label in the Val editor, so
1185
+ * it is where you say what an editor needs to know but the field name cannot
1186
+ * carry. It also travels in the serialized schema, which is what the AI
1187
+ * assistant and the MCP tools read.
1188
+ *
1189
+ * Pass `null` to clear a description set earlier.
1190
+ *
1191
+ * @example
1192
+ * const schema = s
1193
+ * .array(s.string())
1194
+ * .describe("Bullet points shown under the heading");
1195
+ * export default c.define("/example.val.ts", schema, ["First", "Second"]);
1196
+ */
1010
1197
  _inherits(ArraySchema, _Schema);
1011
1198
  return _createClass(ArraySchema, [{
1012
1199
  key: "describe",
1013
1200
  value: function describe(description) {
1014
1201
  return new ArraySchema(this.item, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.previewInput, this.renderInput);
1015
1202
  }
1203
+
1204
+ /**
1205
+ * Add a custom validation rule to this field.
1206
+ *
1207
+ * The function is called with the field's value and returns `false` when the
1208
+ * value is fine, or a STRING with the message to show when it is not. Call it
1209
+ * more than once to add more rules — they all run, and every message is
1210
+ * reported.
1211
+ *
1212
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
1213
+ * when the value is fine, and `true` is not one of the two answers.
1214
+ *
1215
+ * Validation runs in the Studio as you type, in `npx val validate` and
1216
+ * before a publish.
1217
+ *
1218
+ * The function is given the whole array. To validate one item, put a
1219
+ * `.validate(...)` on the ITEM schema instead — that reports the error on
1220
+ * the item's own row.
1221
+ *
1222
+ * @example
1223
+ * const schema = s.array(s.string()).validate((val) =>
1224
+ * val.length <= 3 ? false : "At most 3 items",
1225
+ * );
1226
+ * export default c.define("/example.val.ts", schema, ["First", "Second"]);
1227
+ */
1016
1228
  }, {
1017
1229
  key: "validate",
1018
1230
  value: function validate(validationFunction) {
@@ -1220,6 +1432,25 @@ var ArraySchema = /*#__PURE__*/function (_Schema) {
1220
1432
  * is the item of another container, in search, in references. What its rows
1221
1433
  * show is the ITEM schema's `preview`, not this. Never how the field is
1222
1434
  * edited (that is `render`). See `preview.ts`.
1435
+ *
1436
+ * @example
1437
+ * // The preview of the ARRAY, for where it is an item of something else.
1438
+ * // What its own rows show is the ITEM schema's preview — see below.
1439
+ * const tags = s.array(s.string()).preview(({ val }) => ({
1440
+ * title: `${val.length} tags`,
1441
+ * }));
1442
+ * export default c.define("/example.val.ts", s.record(tags), {
1443
+ * "a-post": ["news", "release"],
1444
+ * });
1445
+ *
1446
+ * @example
1447
+ * // Rows of an array come from the ITEM's preview, not the array's:
1448
+ * const author = s
1449
+ * .object({ name: s.string(), role: s.string() })
1450
+ * .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
1451
+ * export default c.define("/example.val.ts", s.array(author), [
1452
+ * { name: "Ada", role: "Engineer" },
1453
+ * ]);
1223
1454
  */
1224
1455
  }, {
1225
1456
  key: "preview",
@@ -1233,6 +1464,12 @@ var ArraySchema = /*#__PURE__*/function (_Schema) {
1233
1464
  * instead of a preview row that navigates to it.
1234
1465
  *
1235
1466
  * Static configuration, not a callback — see `render.ts`.
1467
+ *
1468
+ * @example
1469
+ * const schema = s.record(s.array(s.string()).render({ as: "inline" }));
1470
+ * export default c.define("/example.val.ts", schema, {
1471
+ * "a-post": ["news", "release"],
1472
+ * });
1236
1473
  */
1237
1474
  }, {
1238
1475
  key: "render",
@@ -1267,12 +1504,50 @@ var LiteralSchema = /*#__PURE__*/function (_Schema) {
1267
1504
  _this.previewInput = previewInput;
1268
1505
  return _this;
1269
1506
  }
1507
+
1508
+ /**
1509
+ * Describe this field.
1510
+ *
1511
+ * The description is shown next to the field's label in the Val editor, so
1512
+ * it is where you say what an editor needs to know but the field name cannot
1513
+ * carry. It also travels in the serialized schema, which is what the AI
1514
+ * assistant and the MCP tools read.
1515
+ *
1516
+ * Pass `null` to clear a description set earlier.
1517
+ *
1518
+ * @example
1519
+ * const schema = s
1520
+ * .literal("hero")
1521
+ * .describe("The block type — it identifies the variant");
1522
+ * export default c.define("/example.val.ts", schema, "hero");
1523
+ */
1270
1524
  _inherits(LiteralSchema, _Schema);
1271
1525
  return _createClass(LiteralSchema, [{
1272
1526
  key: "describe",
1273
1527
  value: function describe(description) {
1274
1528
  return new LiteralSchema(this.value, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
1275
1529
  }
1530
+
1531
+ /**
1532
+ * Add a custom validation rule to this field.
1533
+ *
1534
+ * The function is called with the field's value and returns `false` when the
1535
+ * value is fine, or a STRING with the message to show when it is not. Call it
1536
+ * more than once to add more rules — they all run, and every message is
1537
+ * reported.
1538
+ *
1539
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
1540
+ * when the value is fine, and `true` is not one of the two answers.
1541
+ *
1542
+ * Validation runs in the Studio as you type, in `npx val validate` and
1543
+ * before a publish.
1544
+ *
1545
+ * @example
1546
+ * const schema = s.literal("hero").validate((val) =>
1547
+ * val === "hero" ? false : "Unknown block type",
1548
+ * );
1549
+ * export default c.define("/example.val.ts", schema, "hero");
1550
+ */
1276
1551
  }, {
1277
1552
  key: "validate",
1278
1553
  value: function validate(validationFunction) {
@@ -1367,6 +1642,10 @@ var LiteralSchema = /*#__PURE__*/function (_Schema) {
1367
1642
  * instead of a preview row that navigates to it.
1368
1643
  *
1369
1644
  * Static configuration, not a callback — see `render.ts`.
1645
+ *
1646
+ * @example
1647
+ * const schema = s.array(s.literal("hero").render({ as: "inline" }));
1648
+ * export default c.define("/example.val.ts", schema, ["hero"]);
1370
1649
  */
1371
1650
  }, {
1372
1651
  key: "render",
@@ -1378,6 +1657,12 @@ var LiteralSchema = /*#__PURE__*/function (_Schema) {
1378
1657
  * How this VALUE is shown where a preview of it is needed — a row in a
1379
1658
  * sortable list, a reference dropdown, a search hit. Never how the field
1380
1659
  * itself is edited (that is `render`). See `preview.ts`.
1660
+ *
1661
+ * @example
1662
+ * const schema = s.array(
1663
+ * s.literal("hero").preview(({ val }) => ({ title: val })),
1664
+ * );
1665
+ * export default c.define("/example.val.ts", schema, ["hero"]);
1381
1666
  */
1382
1667
  }, {
1383
1668
  key: "preview",
@@ -1426,8 +1711,30 @@ var literal = function literal(value) {
1426
1711
  return new LiteralSchema(value);
1427
1712
  };
1428
1713
 
1429
- var UnionSchema = /*#__PURE__*/function (_Schema) {
1430
- function UnionSchema(key, items) {
1714
+ /** The variant objects a discriminated union on `Key` may be built from. */
1715
+
1716
+ /**
1717
+ * At least one variant, so a union with none cannot be written.
1718
+ *
1719
+ * Everything downstream reads the first one where it needs any — `emptyOf`
1720
+ * takes `items[0]` for the value a new entry starts as — and a union with
1721
+ * nothing to select is not a thing an author means. `s.enum` says the same
1722
+ * about its values.
1723
+ */
1724
+
1725
+ /** The source type a discriminated union over `T` accepts. */
1726
+
1727
+ /**
1728
+ * One of several object shapes, told apart by a tag field.
1729
+ *
1730
+ * Every variant is an object with `key` set to a distinct `s.literal(...)`, and
1731
+ * the value of that field decides which variant a value is. Unlike
1732
+ * {@link EnumSchema} this is a CONTAINER: the variant's fields are the fields
1733
+ * being edited, and everything that walks the schema tree has to descend
1734
+ * through the matching variant.
1735
+ */
1736
+ var DiscriminatedUnionSchema = /*#__PURE__*/function (_Schema) {
1737
+ function DiscriminatedUnionSchema(key, items) {
1431
1738
  var _this;
1432
1739
  var opt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
1433
1740
  var customValidateFunctions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
@@ -1436,8 +1743,8 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1436
1743
  var description = arguments.length > 6 ? arguments[6] : undefined;
1437
1744
  var renderInput = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
1438
1745
  var previewInput = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
1439
- _classCallCheck(this, UnionSchema);
1440
- _this = _callSuper(this, UnionSchema);
1746
+ _classCallCheck(this, DiscriminatedUnionSchema);
1747
+ _this = _callSuper(this, DiscriminatedUnionSchema);
1441
1748
  _this.key = key;
1442
1749
  _this.items = items;
1443
1750
  _this.opt = opt;
@@ -1449,16 +1756,66 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1449
1756
  _this.previewInput = previewInput;
1450
1757
  return _this;
1451
1758
  }
1452
- _inherits(UnionSchema, _Schema);
1453
- return _createClass(UnionSchema, [{
1759
+
1760
+ /**
1761
+ * Describe this field.
1762
+ *
1763
+ * The description is shown next to the field's label in the Val editor, so it is
1764
+ * where you say what an editor needs to know but the field name cannot carry. It
1765
+ * also travels in the serialized schema, which is what the AI assistant and the
1766
+ * MCP tools read.
1767
+ *
1768
+ * Pass `null` to clear a description set earlier.
1769
+ *
1770
+ * @example
1771
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
1772
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
1773
+ * const schema = s
1774
+ * .discriminatedUnion("type", hero, text)
1775
+ * .describe("What this section of the page is");
1776
+ * export default c.define("/example.val.ts", schema, {
1777
+ * type: "hero",
1778
+ * title: "Hello",
1779
+ * });
1780
+ */
1781
+ _inherits(DiscriminatedUnionSchema, _Schema);
1782
+ return _createClass(DiscriminatedUnionSchema, [{
1454
1783
  key: "describe",
1455
1784
  value: function describe(description) {
1456
- return new UnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
1785
+ return new DiscriminatedUnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
1457
1786
  }
1787
+
1788
+ /**
1789
+ * Add a custom validation rule to this field.
1790
+ *
1791
+ * The function is called with the field's value and returns `false` when the
1792
+ * value is fine, or a STRING with the message to show when it is not. Call it
1793
+ * more than once to add more rules — they all run, and every message is
1794
+ * reported.
1795
+ *
1796
+ * Validation runs in the Studio as you type, in `npx val validate` and before a
1797
+ * publish.
1798
+ *
1799
+ * The function is given the whole value, whichever variant it took. A rule
1800
+ * that only concerns one variant belongs on that variant's object schema.
1801
+ *
1802
+ * @example
1803
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
1804
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
1805
+ * const schema = s
1806
+ * .discriminatedUnion("type", hero, text)
1807
+ * .validate((val) =>
1808
+ * val.type === "hero" && !val.title ? "A hero needs a title" : false,
1809
+ * );
1810
+ * export default c.define("/example.val.ts", schema, {
1811
+ * type: "hero",
1812
+ * title: "Hello",
1813
+ * });
1814
+ */
1458
1815
  }, {
1459
1816
  key: "validate",
1460
1817
  value: function validate(validationFunction) {
1461
- return new UnionSchema(this.key, this.items, this.opt, this.customValidateFunctions.concat(validationFunction), this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
1818
+ return new DiscriminatedUnionSchema(this.key, this.items, this.opt, this.customValidateFunctions.concat(validationFunction), this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
1462
1819
  }
1463
1820
  }, {
1464
1821
  key: "executeValidate",
@@ -1466,142 +1823,135 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1466
1823
  var customValidationErrors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
1467
1824
  path: path
1468
1825
  });
1826
+ /**
1827
+ * A structural error at this path, unless the custom validators already
1828
+ * reported — in which case theirs are the answer and this one is dropped.
1829
+ * Faithful to what `s.union` did; changing it is a separate decision.
1830
+ */
1831
+ var structural = function structural(error) {
1832
+ return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [error]);
1833
+ };
1469
1834
  var unknownSrc = src;
1470
1835
  if (this.opt && unknownSrc === null) {
1471
1836
  return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : false;
1472
1837
  }
1473
1838
  if (!this.key) {
1474
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1475
- message: "Missing required first argument in union",
1839
+ return structural({
1840
+ message: "Missing required first argument in discriminated union",
1476
1841
  schemaError: true
1477
- }]));
1842
+ });
1478
1843
  }
1479
1844
  var key = this.key;
1480
1845
  if (!Array.isArray(this.items)) {
1481
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1482
- message: "A union schema must take more than 1 schema arguments",
1846
+ return structural({
1847
+ message: "A discriminated union schema must take more than 1 schema arguments",
1483
1848
  schemaError: true
1484
- }]));
1849
+ });
1485
1850
  }
1486
- if (typeof key === "string") {
1487
- // tagged union
1488
- if (this.items.some(function (item) {
1489
- return !(item instanceof ObjectSchema);
1490
- })) {
1491
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1492
- message: "Key is a string, so all schema items must be objects",
1493
- schemaError: true
1494
- }]));
1495
- }
1496
- var objectSchemas = this.items;
1497
- var serializedSchemas = objectSchemas.map(function (schema) {
1498
- return schema["executeSerialize"]();
1851
+ if (typeof key !== "string") {
1852
+ return structural({
1853
+ message: "The key of a discriminated union must be a string",
1854
+ schemaError: true
1499
1855
  });
1500
- var illegalSchemas = serializedSchemas.filter(function (schema) {
1501
- return !(schema.type === "object") || !(schema.items[key].type === "literal");
1856
+ }
1857
+ if (this.items.some(function (item) {
1858
+ return !(item instanceof ObjectSchema);
1859
+ })) {
1860
+ return structural({
1861
+ message: "All schema items of a discriminated union must be objects",
1862
+ schemaError: true
1502
1863
  });
1503
- if (illegalSchemas.length > 0) {
1504
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1505
- message: "All schema items must be objects with a key: ".concat(key, " that is a literal schema. Found: ").concat(JSON.stringify(illegalSchemas, null, 2)),
1506
- schemaError: true
1507
- }]));
1508
- }
1509
- var serializedObjectSchemas = serializedSchemas;
1510
- var optionalLiterals = serializedObjectSchemas.filter(function (schema) {
1511
- return schema.items[key].opt;
1864
+ }
1865
+ var objectSchemas = this.items;
1866
+ var serializedSchemas = objectSchemas.map(function (schema) {
1867
+ return schema["executeSerialize"]();
1868
+ });
1869
+ // `items[key]?` — a variant that OMITS the discriminator is exactly what
1870
+ // this check exists to report, so it must not throw while finding it.
1871
+ var illegalSchemas = serializedSchemas.filter(function (schema) {
1872
+ var _schema$items$key;
1873
+ return !(schema.type === "object") || ((_schema$items$key = schema.items[key]) === null || _schema$items$key === void 0 ? void 0 : _schema$items$key.type) !== "literal";
1874
+ });
1875
+ if (illegalSchemas.length > 0) {
1876
+ return structural({
1877
+ message: "All schema items must be objects with a key: ".concat(key, " that is a literal schema. Found: ").concat(JSON.stringify(illegalSchemas, null, 2)),
1878
+ schemaError: true
1512
1879
  });
1513
- if (optionalLiterals.length > 1) {
1514
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1515
- message: "Schema cannot have an optional keys: ".concat(key),
1516
- schemaError: true
1517
- }]));
1518
- }
1519
- if (_typeof(unknownSrc) !== "object") {
1520
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1521
- message: "Expected an object",
1522
- typeError: true
1523
- }]));
1524
- }
1525
- var objectSrc = unknownSrc;
1526
- if (objectSrc[key] === undefined) {
1527
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1528
- message: "Missing required key: ".concat(key),
1529
- typeError: true
1530
- }]));
1531
- }
1532
- var foundSchemaLiterals = [];
1533
- var _iterator = result._createForOfIteratorHelper(serializedObjectSchemas),
1534
- _step;
1535
- try {
1536
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
1537
- var schema = _step.value;
1538
- var schemaKey = schema.items[key];
1539
- if (schemaKey.type === "literal") {
1540
- if (!foundSchemaLiterals.includes(schemaKey.value)) {
1541
- foundSchemaLiterals.push(schemaKey.value);
1542
- } else {
1543
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1544
- message: "Found duplicate key in schema: ".concat(schemaKey.value),
1545
- schemaError: true
1546
- }]));
1547
- }
1548
- }
1549
- }
1550
- } catch (err) {
1551
- _iterator.e(err);
1552
- } finally {
1553
- _iterator.f();
1554
- }
1555
- var objectSchemaAtKey = objectSchemas.find(function (schema) {
1556
- return !schema["items"][key]["executeValidate"](path, objectSrc[key]);
1880
+ }
1881
+ var serializedObjectSchemas = serializedSchemas;
1882
+ var optionalLiterals = serializedObjectSchemas.filter(function (schema) {
1883
+ return schema.items[key].opt;
1884
+ });
1885
+ if (optionalLiterals.length > 1) {
1886
+ return structural({
1887
+ message: "Schema cannot have an optional keys: ".concat(key),
1888
+ schemaError: true
1557
1889
  });
1558
- if (!objectSchemaAtKey) {
1559
- var keyPath = createValPathOfItem(path, key);
1560
- if (!keyPath) {
1561
- throw new Error("Internal error: could not create path at ".concat(!path && typeof path === "string" ? "<empty string>" : path, " at key ").concat(key));
1562
- }
1563
- return _defineProperty({}, keyPath, [{
1564
- message: "Invalid key: \"".concat(key, "\". Value was: \"").concat(objectSrc[key], "\". Valid values: ").concat(serializedObjectSchemas.map(function (schema) {
1565
- var keySchema = schema.items[key];
1566
- if (keySchema.type === "literal" && keySchema.value) {
1567
- return "\"".concat(keySchema.value, "\"");
1568
- } else {
1569
- // should not happen here, we already checked this
1570
- throw new Error("Expected literal schema, got ".concat(JSON.stringify(keySchema, null, 2)));
1571
- }
1572
- }).join(", "))
1573
- }]);
1574
- }
1575
- var error = objectSchemaAtKey["executeValidate"](path, objectSrc);
1576
- if (error) {
1577
- return error;
1578
- }
1579
- } else if (key instanceof LiteralSchema) {
1580
- if (this.items.some(function (item) {
1581
- return !(item instanceof LiteralSchema);
1582
- })) {
1583
- return _defineProperty({}, path, [{
1584
- message: "Key is a literal schema, so all schema items must be literals",
1585
- typeError: true
1586
- }]);
1587
- }
1588
- var literalItems = [key].concat(_toConsumableArray(this.items));
1589
- if (typeof unknownSrc === "string") {
1590
- var isMatch = literalItems.some(function (item) {
1591
- return !item["executeValidate"](path, unknownSrc);
1592
- });
1593
- if (!isMatch) {
1594
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1595
- message: "Union must match one of the following: ".concat(literalItems.map(function (item) {
1596
- return "\"".concat(item["value"], "\"");
1597
- }).join(", "))
1598
- }]));
1890
+ }
1891
+
1892
+ // `typeof null === "object"`, so null has to be named separately or it
1893
+ // reaches `objectSrc[key]` below and throws instead of reporting.
1894
+ if (unknownSrc === null || _typeof(unknownSrc) !== "object") {
1895
+ return structural({
1896
+ message: "Expected an object, got ".concat(unknownSrc === null ? "null" : _typeof(unknownSrc)),
1897
+ typeError: true
1898
+ });
1899
+ }
1900
+ var objectSrc = unknownSrc;
1901
+ if (objectSrc[key] === undefined) {
1902
+ return structural({
1903
+ message: "Missing required key: ".concat(key),
1904
+ typeError: true
1905
+ });
1906
+ }
1907
+ var foundSchemaLiterals = [];
1908
+ var _iterator = result._createForOfIteratorHelper(serializedObjectSchemas),
1909
+ _step;
1910
+ try {
1911
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
1912
+ var schema = _step.value;
1913
+ var schemaKey = schema.items[key];
1914
+ if (schemaKey.type === "literal") {
1915
+ if (!foundSchemaLiterals.includes(schemaKey.value)) {
1916
+ foundSchemaLiterals.push(schemaKey.value);
1917
+ } else {
1918
+ return structural({
1919
+ message: "Found duplicate key in schema: ".concat(schemaKey.value),
1920
+ schemaError: true
1921
+ });
1922
+ }
1599
1923
  }
1600
1924
  }
1601
- } else {
1602
- return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
1603
- message: "Expected a string or literal"
1604
- }]));
1925
+ } catch (err) {
1926
+ _iterator.e(err);
1927
+ } finally {
1928
+ _iterator.f();
1929
+ }
1930
+ var objectSchemaAtKey = objectSchemas.find(function (schema) {
1931
+ return !schema["items"][key]["executeValidate"](path, objectSrc[key]);
1932
+ });
1933
+ if (!objectSchemaAtKey) {
1934
+ var keyPath = createValPathOfItem(path, key);
1935
+ if (!keyPath) {
1936
+ throw new Error("Internal error: could not create path at ".concat(!path && typeof path === "string" ? "<empty string>" : path, " at key ").concat(key));
1937
+ }
1938
+ return _defineProperty({}, keyPath, [{
1939
+ message: "Invalid key: \"".concat(key, "\". Value was: \"").concat(objectSrc[key], "\". Valid values: ").concat(serializedObjectSchemas.map(function (schema) {
1940
+ var keySchema = schema.items[key];
1941
+ // The TYPE is the whole test: `s.literal("")` is a valid
1942
+ // discriminator, and a truthiness check threw on it here.
1943
+ if (keySchema.type === "literal") {
1944
+ return "\"".concat(keySchema.value, "\"");
1945
+ } else {
1946
+ // should not happen here, we already checked this
1947
+ throw new Error("Expected literal schema, got ".concat(JSON.stringify(keySchema, null, 2)));
1948
+ }
1949
+ }).join(", "))
1950
+ }]);
1951
+ }
1952
+ var error = objectSchemaAtKey["executeValidate"](path, objectSrc);
1953
+ if (error) {
1954
+ return error;
1605
1955
  }
1606
1956
  return customValidationErrors.length > 0 ? _defineProperty({}, path, customValidationErrors) : false;
1607
1957
  }
@@ -1623,11 +1973,11 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1623
1973
  }])
1624
1974
  };
1625
1975
  }
1626
- if (!this.key) {
1976
+ if (typeof this.key !== "string" || !this.key) {
1627
1977
  return {
1628
1978
  success: false,
1629
1979
  errors: _defineProperty({}, path, [{
1630
- message: "Missing required first argument in union schema",
1980
+ message: "Missing required first argument in discriminated union schema",
1631
1981
  schemaError: true
1632
1982
  }])
1633
1983
  };
@@ -1636,135 +1986,68 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1636
1986
  return {
1637
1987
  success: false,
1638
1988
  errors: _defineProperty({}, path, [{
1639
- message: "The schema of this value is wrong. Schema is neither a union of literals nor a tagged union (of objects)",
1989
+ message: "The schema of this value is wrong. A discriminated union must have an array of object schemas",
1640
1990
  schemaError: true
1641
1991
  }])
1642
1992
  };
1643
1993
  }
1644
- if (this.key instanceof LiteralSchema) {
1645
- var _ref23;
1646
- var success = false;
1647
- var errors = {};
1648
- var _iterator2 = result._createForOfIteratorHelper((_ref23 = [this.key]).concat.apply(_ref23, _toConsumableArray(this.items))),
1649
- _step2;
1650
- try {
1651
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
1652
- var itemSchema = _step2.value;
1653
- if (!(itemSchema instanceof LiteralSchema)) {
1654
- return {
1655
- success: false,
1656
- errors: _defineProperty({}, path, [{
1657
- message: "Schema of value is a union of string, so all schema items must be literals",
1658
- schemaError: true
1659
- }])
1660
- };
1661
- }
1662
- if (typeof src !== "string") {
1663
- errors[path] = [{
1664
- message: "Expected 'string', got '".concat(_typeof(src), "'"),
1665
- typeError: true
1666
- }];
1667
- continue;
1668
- }
1669
- var res = itemSchema["executeAssert"](path, src);
1670
- if (res.success) {
1671
- success = true;
1672
- break;
1673
- } else {
1674
- for (var _i = 0, _Object$entries = Object.entries(res.errors); _i < _Object$entries.length; _i++) {
1675
- var _errors5;
1676
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
1677
- _key = _Object$entries$_i[0],
1678
- value = _Object$entries$_i[1];
1679
- if (!errors[_key]) {
1680
- errors[_key] = [];
1681
- }
1682
- (_errors5 = errors[_key]).push.apply(_errors5, _toConsumableArray(value));
1683
- }
1684
- }
1685
- }
1686
- } catch (err) {
1687
- _iterator2.e(err);
1688
- } finally {
1689
- _iterator2.f();
1690
- }
1691
- if (!success) {
1692
- return {
1693
- success: false,
1694
- errors: errors
1695
- };
1696
- }
1697
- return {
1698
- success: true,
1699
- data: src
1700
- };
1701
- } else if (typeof this.key === "string") {
1702
- var _success = false;
1703
- var _errors6 = {};
1704
- var _iterator3 = result._createForOfIteratorHelper(this.items),
1705
- _step3;
1706
- try {
1707
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
1708
- var _itemSchema = _step3.value;
1709
- var _res = _itemSchema["executeAssert"](path, src);
1710
- if (_res.success) {
1711
- _success = true;
1712
- break;
1713
- } else {
1714
- for (var _i2 = 0, _Object$entries2 = Object.entries(_res.errors); _i2 < _Object$entries2.length; _i2++) {
1715
- var _errors7;
1716
- var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
1717
- _key2 = _Object$entries2$_i[0],
1718
- _value = _Object$entries2$_i[1];
1719
- if (!_errors6[_key2]) {
1720
- _errors6[_key2] = [];
1721
- }
1722
- (_errors7 = _errors6[_key2]).push.apply(_errors7, _toConsumableArray(_value)); // by appending all type errors, we most likely get a lot of duplicate errors. Currently we believe this is correct though, but should probably be handled in when showing the errors to users
1994
+ var success = false;
1995
+ var errors = {};
1996
+ var _iterator2 = result._createForOfIteratorHelper(this.items),
1997
+ _step2;
1998
+ try {
1999
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
2000
+ var itemSchema = _step2.value;
2001
+ var res = itemSchema["executeAssert"](path, src);
2002
+ if (res.success) {
2003
+ success = true;
2004
+ break;
2005
+ } else {
2006
+ for (var _i = 0, _Object$entries = Object.entries(res.errors); _i < _Object$entries.length; _i++) {
2007
+ var _errors4;
2008
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
2009
+ _key = _Object$entries$_i[0],
2010
+ value = _Object$entries$_i[1];
2011
+ if (!errors[_key]) {
2012
+ errors[_key] = [];
1723
2013
  }
2014
+ (_errors4 = errors[_key]).push.apply(_errors4, _toConsumableArray(value)); // by appending all type errors, we most likely get a lot of duplicate errors. Currently we believe this is correct though, but should probably be handled in when showing the errors to users
1724
2015
  }
1725
2016
  }
1726
- } catch (err) {
1727
- _iterator3.e(err);
1728
- } finally {
1729
- _iterator3.f();
1730
- }
1731
- if (!_success) {
1732
- return {
1733
- success: false,
1734
- errors: _errors6
1735
- };
1736
2017
  }
1737
- return {
1738
- success: true,
1739
- data: src
1740
- };
1741
- } else {
2018
+ } catch (err) {
2019
+ _iterator2.e(err);
2020
+ } finally {
2021
+ _iterator2.f();
2022
+ }
2023
+ if (!success) {
1742
2024
  return {
1743
2025
  success: false,
1744
- errors: _defineProperty({}, path, [{
1745
- message: "The schema of this value is wrong. Schema is neither a union of literals nor a tagged union (of objects)",
1746
- schemaError: true
1747
- }])
2026
+ errors: errors
1748
2027
  };
1749
2028
  }
2029
+ return {
2030
+ success: true,
2031
+ data: src
2032
+ };
1750
2033
  }
1751
2034
  }, {
1752
2035
  key: "nullable",
1753
2036
  value: function nullable() {
1754
2037
  // Explicit type args: `previewInput` would otherwise pin inference to `Src`.
1755
- return new UnionSchema(this.key, this.items, true, [], this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
2038
+ return new DiscriminatedUnionSchema(this.key, this.items, true, [], this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
1756
2039
  }
1757
2040
  }, {
1758
2041
  key: "readonly",
1759
2042
  value: function readonly() {
1760
2043
  var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
1761
- return new UnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
2044
+ return new DiscriminatedUnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
1762
2045
  }
1763
2046
  }, {
1764
2047
  key: "hidden",
1765
2048
  value: function hidden() {
1766
2049
  var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
1767
- return new UnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.renderInput, this.previewInput);
2050
+ return new DiscriminatedUnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.renderInput, this.previewInput);
1768
2051
  }
1769
2052
  }, {
1770
2053
  key: "executeCustomValidateAt",
@@ -1772,10 +2055,10 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1772
2055
  var errors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
1773
2056
  path: path
1774
2057
  });
1775
- // A tagged union's variants share the union's path, so `Internal.resolvePath`
1776
- // stops here and a variant's OWN validator would never be reached. Dispatch
1777
- // into the variant this value takes — the union is the only node that knows
1778
- // which one that is.
2058
+ // A discriminated union's variants share the union's path, so
2059
+ // `Internal.resolvePath` stops here and a variant's OWN validator would
2060
+ // never be reached. Dispatch into the variant this value takes — the union
2061
+ // is the only node that knows which one that is.
1779
2062
  var matched = this.matchedVariant(src);
1780
2063
  if (matched) {
1781
2064
  errors.push.apply(errors, _toConsumableArray(matched["executeCustomValidateAt"](path, src)));
@@ -1784,10 +2067,8 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1784
2067
  }
1785
2068
 
1786
2069
  /**
1787
- * The variant schema a value takes, for a TAGGED union (string `key`). `null`
1788
- * for a literal union (its variants are leaves with nothing to descend into) or
1789
- * when the value matches none of them — a structural error `executeValidate`
1790
- * already reports.
2070
+ * The variant schema a value takes. `null` when the value matches none of
2071
+ * them — a structural error `executeValidate` already reports.
1791
2072
  */
1792
2073
  }, {
1793
2074
  key: "matchedVariant",
@@ -1804,11 +2085,11 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1804
2085
  return null;
1805
2086
  }
1806
2087
  var objectSchemas = this.items;
1807
- var _iterator4 = result._createForOfIteratorHelper(objectSchemas),
1808
- _step4;
2088
+ var _iterator3 = result._createForOfIteratorHelper(objectSchemas),
2089
+ _step3;
1809
2090
  try {
1810
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
1811
- var item = _step4.value;
2091
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
2092
+ var item = _step3.value;
1812
2093
  if (!(item instanceof ObjectSchema)) {
1813
2094
  continue;
1814
2095
  }
@@ -1818,9 +2099,9 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1818
2099
  }
1819
2100
  }
1820
2101
  } catch (err) {
1821
- _iterator4.e(err);
2102
+ _iterator3.e(err);
1822
2103
  } finally {
1823
- _iterator4.f();
2104
+ _iterator3.f();
1824
2105
  }
1825
2106
  return null;
1826
2107
  }
@@ -1831,11 +2112,21 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1831
2112
  * instead of a preview row that navigates to it.
1832
2113
  *
1833
2114
  * Static configuration, not a callback — see `render.ts`.
2115
+ *
2116
+ * @example
2117
+ * // A page builder: the row draws the tag selector and the matched
2118
+ * // variant's fields.
2119
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
2120
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
2121
+ * const block = s.discriminatedUnion("type", hero, text).render({ as: "inline" });
2122
+ * export default c.define("/example.val.ts", s.array(block), [
2123
+ * { type: "hero", title: "Hello" },
2124
+ * ]);
1834
2125
  */
1835
2126
  }, {
1836
2127
  key: "render",
1837
2128
  value: function render(input) {
1838
- return new UnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, input, this.previewInput);
2129
+ return new DiscriminatedUnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, input, this.previewInput);
1839
2130
  }
1840
2131
 
1841
2132
  /**
@@ -1843,13 +2134,24 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1843
2134
  * sortable list, a reference dropdown, a search hit. Never how the field
1844
2135
  * itself is edited (that is `render`). See `preview.ts`.
1845
2136
  *
1846
- * Without one of its own, a tagged union previews as the VARIANT the value
1847
- * takes — declare `preview` on the member objects and the union dispatches.
2137
+ * Without one of its own, a discriminated union previews as the VARIANT the
2138
+ * value takes — declare `preview` on the member objects and the union
2139
+ * dispatches.
2140
+ *
2141
+ * @example
2142
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
2143
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
2144
+ * const block = s
2145
+ * .discriminatedUnion("type", hero, text)
2146
+ * .preview(({ val }) => ({ title: val.type }));
2147
+ * export default c.define("/example.val.ts", s.array(block), [
2148
+ * { type: "hero", title: "Hello" },
2149
+ * ]);
1848
2150
  */
1849
2151
  }, {
1850
2152
  key: "preview",
1851
2153
  value: function preview(select) {
1852
- return new UnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, select);
2154
+ return new DiscriminatedUnionSchema(this.key, this.items, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, select);
1853
2155
  }
1854
2156
  }, {
1855
2157
  key: "executePreviewItem",
@@ -1880,34 +2182,19 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1880
2182
  }, {
1881
2183
  key: "executeSerialize",
1882
2184
  value: function executeSerialize() {
1883
- var _this$renderInput2, _this$customValidateF2;
1884
- if (typeof this.key === "string") {
1885
- var _this$renderInput, _this$customValidateF;
1886
- return {
1887
- type: "union",
1888
- render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
1889
- preview: this.previewInput ? true : undefined,
1890
- key: this.key,
1891
- items: this.items.map(function (o) {
1892
- return o["executeSerialize"]();
1893
- }),
1894
- opt: this.opt,
1895
- customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
1896
- readonly: this.isReadonly,
1897
- hidden: this.isHidden,
1898
- description: this.description
1899
- };
1900
- }
2185
+ var _this$renderInput, _this$customValidateF;
1901
2186
  return {
1902
- type: "union",
1903
- render: (_this$renderInput2 = this.renderInput) !== null && _this$renderInput2 !== void 0 ? _this$renderInput2 : undefined,
2187
+ type: "discriminated-union",
2188
+ render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
1904
2189
  preview: this.previewInput ? true : undefined,
1905
- key: this.key["executeSerialize"](),
2190
+ key: this.key,
2191
+ // Every item is an ObjectSchema — `executeValidate` reports a schema
2192
+ // error for anything else, but the type of `T` cannot say so.
1906
2193
  items: this.items.map(function (o) {
1907
2194
  return o["executeSerialize"]();
1908
2195
  }),
1909
2196
  opt: this.opt,
1910
- customValidate: this.customValidateFunctions && ((_this$customValidateF2 = this.customValidateFunctions) === null || _this$customValidateF2 === void 0 ? void 0 : _this$customValidateF2.length) > 0,
2197
+ customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
1911
2198
  readonly: this.isReadonly,
1912
2199
  hidden: this.isHidden,
1913
2200
  description: this.description
@@ -1920,48 +2207,46 @@ var UnionSchema = /*#__PURE__*/function (_Schema) {
1920
2207
  if (src === null) {
1921
2208
  return res;
1922
2209
  }
1923
- if (this.key instanceof LiteralSchema) {
1924
- return res;
1925
- }
1926
2210
  var unionKey = this.key;
1927
- if (typeof unionKey === "string") {
1928
- var thisSchema = this.items.find(
1929
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1930
- function (item) {
1931
- if (item instanceof ObjectSchema) {
1932
- var itemKey = item["items"][unionKey];
1933
- if (itemKey instanceof LiteralSchema) {
1934
- return _typeof(src) === "object" && unionKey in src && itemKey["value"] === src[unionKey];
1935
- }
1936
- }
1937
- return false;
1938
- });
1939
- if (thisSchema) {
1940
- var itemResult = thisSchema["executePreview"](sourcePath, src, scope);
1941
- for (var keyS in itemResult) {
1942
- var _key3 = keyS;
1943
- res[_key3] = itemResult[_key3];
1944
- }
1945
- return res;
1946
- }
2211
+ if (typeof unionKey !== "string") {
1947
2212
  res[sourcePath] = {
1948
2213
  status: "error",
1949
- message: "Could not find a matching (object) schema for the union key: ".concat(unionKey)
2214
+ message: "The schema of this value is wrong. Expected the key of a discriminated union to be a string. Got: '".concat(JSON.stringify(unionKey, null, 2), "'")
1950
2215
  };
2216
+ return res;
2217
+ }
2218
+ var thisSchema = this.items.find(
2219
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2220
+ function (item) {
2221
+ if (item instanceof ObjectSchema) {
2222
+ var itemKey = item["items"][unionKey];
2223
+ if (itemKey instanceof LiteralSchema) {
2224
+ return _typeof(src) === "object" && unionKey in src && itemKey["value"] === src[unionKey];
2225
+ }
2226
+ }
2227
+ return false;
2228
+ });
2229
+ if (thisSchema) {
2230
+ var itemResult = thisSchema["executePreview"](sourcePath, src, scope);
2231
+ for (var keyS in itemResult) {
2232
+ var _key2 = keyS;
2233
+ res[_key2] = itemResult[_key2];
2234
+ }
2235
+ return res;
1951
2236
  }
1952
2237
  res[sourcePath] = {
1953
2238
  status: "error",
1954
- message: "The schema of this value is wrong. Expected a object union schema, but union key is not a string. Got: '".concat(JSON.stringify(unionKey, null, 2), "'")
2239
+ message: "Could not find a matching (object) schema for the discriminated union key: ".concat(unionKey)
1955
2240
  };
1956
2241
  return res;
1957
2242
  }
1958
2243
  }]);
1959
2244
  }(Schema);
1960
- var union = function union(key) {
1961
- for (var _len = arguments.length, objects = new Array(_len > 1 ? _len - 1 : 0), _key4 = 1; _key4 < _len; _key4++) {
1962
- objects[_key4 - 1] = arguments[_key4];
2245
+ var discriminatedUnion = function discriminatedUnion(key) {
2246
+ for (var _len = arguments.length, objects = new Array(_len > 1 ? _len - 1 : 0), _key3 = 1; _key3 < _len; _key3++) {
2247
+ objects[_key3 - 1] = arguments[_key3];
1963
2248
  }
1964
- return new UnionSchema(key, objects);
2249
+ return new DiscriminatedUnionSchema(key, objects);
1965
2250
  };
1966
2251
 
1967
2252
  var RegEx$1 = /^(https?:\/\/[^/]+)\/file\/p\/([^/]+)\/b\/([^/]+)\/v\/([^/]+)\/h\/([^/]+)\/f\/([^/]+)\/p\/(.+)$/;
@@ -2932,17 +3217,94 @@ var ImageSchema = /*#__PURE__*/function (_Schema) {
2932
3217
  _this.previewInput = previewInput;
2933
3218
  return _this;
2934
3219
  }
3220
+
3221
+ /**
3222
+ * Describe this field.
3223
+ *
3224
+ * The description is shown next to the field's label in the Val editor, so
3225
+ * it is where you say what an editor needs to know but the field name cannot
3226
+ * carry. It also travels in the serialized schema, which is what the AI
3227
+ * assistant and the MCP tools read.
3228
+ *
3229
+ * Pass `null` to clear a description set earlier.
3230
+ *
3231
+ * @example
3232
+ * const schema = s
3233
+ * .image()
3234
+ * .describe("Shown at the top of the page, 16:9 works best");
3235
+ * export default c.define("/example.val.ts", schema, {
3236
+ * path: "/public/val/example.png",
3237
+ * width: 100,
3238
+ * height: 100,
3239
+ * mimeType: "image/png",
3240
+ * });
3241
+ */
2935
3242
  _inherits(ImageSchema, _Schema);
2936
3243
  return _createClass(ImageSchema, [{
2937
3244
  key: "describe",
2938
3245
  value: function describe(description) {
2939
3246
  return new ImageSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
2940
3247
  }
3248
+
3249
+ /**
3250
+ * Store the image on Val's remote content host instead of in your repository.
3251
+ *
3252
+ * The bytes still go into the patch store when the image is uploaded — the
3253
+ * push to the remote host happens at publish. What changes is where the
3254
+ * published image lives: `path` becomes a remote URL rather than a path
3255
+ * under `/public`, so the repository does not grow with every upload.
3256
+ *
3257
+ * The `path` of a remote image is a URL on the content host, and it is not
3258
+ * something to write by hand: upload the image in the Studio, or write a
3259
+ * local path and let `npx val validate --fix` upload it and rewrite the
3260
+ * `path` to the ref below.
3261
+ *
3262
+ * @example
3263
+ * const schema = s
3264
+ * .image({ accept: "image/webp", directory: "/public/val/images" })
3265
+ * .remote();
3266
+ * export default c.define("/example.val.ts", schema, {
3267
+ * path: "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/images/example.webp",
3268
+ * width: 100,
3269
+ * height: 100,
3270
+ * mimeType: "image/webp",
3271
+ * });
3272
+ */
2941
3273
  }, {
2942
3274
  key: "remote",
2943
3275
  value: function remote() {
2944
3276
  return new ImageSchema(this.options, this.opt, true, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
2945
3277
  }
3278
+
3279
+ /**
3280
+ * Add a custom validation rule to this field.
3281
+ *
3282
+ * The function is called with the field's value and returns `false` when the
3283
+ * value is fine, or a STRING with the message to show when it is not. Call it
3284
+ * more than once to add more rules — they all run, and every message is
3285
+ * reported.
3286
+ *
3287
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
3288
+ * when the value is fine, and `true` is not one of the two answers.
3289
+ *
3290
+ * Validation runs in the Studio as you type, in `npx val validate` and
3291
+ * before a publish.
3292
+ *
3293
+ * The second argument carries the `path` of the field being validated, for
3294
+ * when the message needs to say where the problem is.
3295
+ *
3296
+ * @example
3297
+ * const schema = s.image().validate((val) =>
3298
+ * val.alt ? false : "Every image needs alt text",
3299
+ * );
3300
+ * export default c.define("/example.val.ts", schema, {
3301
+ * path: "/public/val/example.png",
3302
+ * width: 100,
3303
+ * height: 100,
3304
+ * mimeType: "image/png",
3305
+ * alt: "An example",
3306
+ * });
3307
+ */
2946
3308
  }, {
2947
3309
  key: "validate",
2948
3310
  value: function validate(validationFunction) {
@@ -3160,6 +3522,17 @@ var ImageSchema = /*#__PURE__*/function (_Schema) {
3160
3522
  * instead of a preview row that navigates to it.
3161
3523
  *
3162
3524
  * Static configuration, not a callback — see `render.ts`.
3525
+ *
3526
+ * @example
3527
+ * const schema = s.array(s.image().render({ as: "inline" }));
3528
+ * export default c.define("/example.val.ts", schema, [
3529
+ * {
3530
+ * path: "/public/val/example.png",
3531
+ * width: 100,
3532
+ * height: 100,
3533
+ * mimeType: "image/png",
3534
+ * },
3535
+ * ]);
3163
3536
  */
3164
3537
  }, {
3165
3538
  key: "render",
@@ -3171,6 +3544,23 @@ var ImageSchema = /*#__PURE__*/function (_Schema) {
3171
3544
  * How this VALUE is shown where a preview of it is needed — a row in a
3172
3545
  * sortable list, a reference dropdown, a search hit. Never how the field
3173
3546
  * itself is edited (that is `render`). See `preview.ts`.
3547
+ *
3548
+ * @example
3549
+ * const schema = s.array(
3550
+ * s.image().preview(({ val }) => ({
3551
+ * title: val.alt ?? "Image",
3552
+ * image: val,
3553
+ * })),
3554
+ * );
3555
+ * export default c.define("/example.val.ts", schema, [
3556
+ * {
3557
+ * path: "/public/val/example.png",
3558
+ * width: 100,
3559
+ * height: 100,
3560
+ * mimeType: "image/png",
3561
+ * alt: "An example",
3562
+ * },
3563
+ * ]);
3174
3564
  */
3175
3565
  }, {
3176
3566
  key: "preview",
@@ -3267,6 +3657,21 @@ var RouteSchema = /*#__PURE__*/function (_Schema) {
3267
3657
  _this.previewInput = previewInput;
3268
3658
  return _this;
3269
3659
  }
3660
+
3661
+ /**
3662
+ * Describe this field.
3663
+ *
3664
+ * The description is shown next to the field's label in the Val editor, so
3665
+ * it is where you say what an editor needs to know but the field name cannot
3666
+ * carry. It also travels in the serialized schema, which is what the AI
3667
+ * assistant and the MCP tools read.
3668
+ *
3669
+ * Pass `null` to clear a description set earlier.
3670
+ *
3671
+ * @example
3672
+ * const schema = s.route().describe("Where the button goes");
3673
+ * export default c.define("/example.val.ts", schema, "/a-page-slug");
3674
+ */
3270
3675
  _inherits(RouteSchema, _Schema);
3271
3676
  return _createClass(RouteSchema, [{
3272
3677
  key: "describe",
@@ -3313,6 +3718,27 @@ var RouteSchema = /*#__PURE__*/function (_Schema) {
3313
3718
  exclude: pattern
3314
3719
  }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
3315
3720
  }
3721
+
3722
+ /**
3723
+ * Add a custom validation rule to this field.
3724
+ *
3725
+ * The function is called with the field's value and returns `false` when the
3726
+ * value is fine, or a STRING with the message to show when it is not. Call it
3727
+ * more than once to add more rules — they all run, and every message is
3728
+ * reported.
3729
+ *
3730
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
3731
+ * when the value is fine, and `true` is not one of the two answers.
3732
+ *
3733
+ * Validation runs in the Studio as you type, in `npx val validate` and
3734
+ * before a publish.
3735
+ *
3736
+ * @example
3737
+ * const schema = s.route().validate((val) =>
3738
+ * val.endsWith("/") ? "Leave off the trailing slash" : false,
3739
+ * );
3740
+ * export default c.define("/example.val.ts", schema, "/a-page-slug");
3741
+ */
3316
3742
  }, {
3317
3743
  key: "validate",
3318
3744
  value: function validate(validationFunction) {
@@ -3399,6 +3825,10 @@ var RouteSchema = /*#__PURE__*/function (_Schema) {
3399
3825
  * instead of a preview row that navigates to it.
3400
3826
  *
3401
3827
  * Static configuration, not a callback — see `render.ts`.
3828
+ *
3829
+ * @example
3830
+ * const schema = s.array(s.route().render({ as: "inline" }));
3831
+ * export default c.define("/example.val.ts", schema, ["/a-page-slug"]);
3402
3832
  */
3403
3833
  }, {
3404
3834
  key: "render",
@@ -3410,6 +3840,12 @@ var RouteSchema = /*#__PURE__*/function (_Schema) {
3410
3840
  * How this VALUE is shown where a preview of it is needed — a row in a
3411
3841
  * sortable list, a reference dropdown, a search hit. Never how the field
3412
3842
  * itself is edited (that is `render`). See `preview.ts`.
3843
+ *
3844
+ * @example
3845
+ * const schema = s.array(
3846
+ * s.route().preview(({ val }) => ({ title: val })),
3847
+ * );
3848
+ * export default c.define("/example.val.ts", schema, ["/a-page-slug"]);
3413
3849
  */
3414
3850
  }, {
3415
3851
  key: "preview",
@@ -3490,12 +3926,42 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
3490
3926
  _this.previewInput = previewInput;
3491
3927
  return _this;
3492
3928
  }
3929
+
3930
+ /**
3931
+ * Describe this field.
3932
+ *
3933
+ * The description is shown next to the field's label in the Val editor, so
3934
+ * it is where you say what an editor needs to know but the field name cannot
3935
+ * carry. It also travels in the serialized schema, which is what the AI
3936
+ * assistant and the MCP tools read.
3937
+ *
3938
+ * Pass `null` to clear a description set earlier.
3939
+ *
3940
+ * @example
3941
+ * const schema = s.richtext().describe("The body of the article");
3942
+ * export default c.define("/example.val.ts", schema, [
3943
+ * { tag: "p", children: ["Hello"] },
3944
+ * ]);
3945
+ */
3493
3946
  _inherits(RichTextSchema, _Schema);
3494
3947
  return _createClass(RichTextSchema, [{
3495
3948
  key: "describe",
3496
3949
  value: function describe(description) {
3497
3950
  return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
3498
3951
  }
3952
+
3953
+ /**
3954
+ * Validate that the rich text holds at most `max` characters.
3955
+ *
3956
+ * Counted over the TEXT, not the markup: tags, attributes and the structure
3957
+ * of the document do not add to it.
3958
+ *
3959
+ * @example
3960
+ * const schema = s.richtext().maxLength(280);
3961
+ * export default c.define("/example.val.ts", schema, [
3962
+ * { tag: "p", children: ["Hello"] },
3963
+ * ]);
3964
+ */
3499
3965
  }, {
3500
3966
  key: "maxLength",
3501
3967
  value: function maxLength(max) {
@@ -3503,6 +3969,19 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
3503
3969
  maxLength: max
3504
3970
  }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
3505
3971
  }
3972
+
3973
+ /**
3974
+ * Validate that the rich text holds at least `min` characters.
3975
+ *
3976
+ * Counted over the TEXT, not the markup: tags, attributes and the structure
3977
+ * of the document do not add to it.
3978
+ *
3979
+ * @example
3980
+ * const schema = s.richtext().minLength(20).maxLength(280);
3981
+ * export default c.define("/example.val.ts", schema, [
3982
+ * { tag: "p", children: ["Long enough to clear the minimum length."] },
3983
+ * ]);
3984
+ */
3506
3985
  }, {
3507
3986
  key: "minLength",
3508
3987
  value: function minLength(min) {
@@ -3510,6 +3989,29 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
3510
3989
  minLength: min
3511
3990
  }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
3512
3991
  }
3992
+
3993
+ /**
3994
+ * Add a custom validation rule to this field.
3995
+ *
3996
+ * The function is called with the field's value and returns `false` when the
3997
+ * value is fine, or a STRING with the message to show when it is not. Call it
3998
+ * more than once to add more rules — they all run, and every message is
3999
+ * reported.
4000
+ *
4001
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
4002
+ * when the value is fine, and `true` is not one of the two answers.
4003
+ *
4004
+ * Validation runs in the Studio as you type, in `npx val validate` and
4005
+ * before a publish.
4006
+ *
4007
+ * @example
4008
+ * const schema = s.richtext().validate((val) =>
4009
+ * val.length > 0 ? false : "Write something",
4010
+ * );
4011
+ * export default c.define("/example.val.ts", schema, [
4012
+ * { tag: "p", children: ["Hello"] },
4013
+ * ]);
4014
+ */
3513
4015
  }, {
3514
4016
  key: "validate",
3515
4017
  value: function validate(validationFunction) {
@@ -3985,6 +4487,12 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
3985
4487
  * instead of a preview row that navigates to it.
3986
4488
  *
3987
4489
  * Static configuration, not a callback — see `render.ts`.
4490
+ *
4491
+ * @example
4492
+ * const schema = s.array(s.richtext().render({ as: "inline" }));
4493
+ * export default c.define("/example.val.ts", schema, [
4494
+ * [{ tag: "p", children: ["Hello"] }],
4495
+ * ]);
3988
4496
  */
3989
4497
  }, {
3990
4498
  key: "render",
@@ -3996,6 +4504,16 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
3996
4504
  * How this VALUE is shown where a preview of it is needed — a row in a
3997
4505
  * sortable list, a reference dropdown, a search hit. Never how the field
3998
4506
  * itself is edited (that is `render`). See `preview.ts`.
4507
+ *
4508
+ * @example
4509
+ * const schema = s.array(
4510
+ * s.richtext().preview(({ val }) => ({
4511
+ * title: val.length > 0 ? "Rich text" : "Empty",
4512
+ * })),
4513
+ * );
4514
+ * export default c.define("/example.val.ts", schema, [
4515
+ * [{ tag: "p", children: ["Hello"] }],
4516
+ * ]);
3999
4517
  */
4000
4518
  }, {
4001
4519
  key: "preview",
@@ -4504,12 +5022,55 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
4504
5022
  _this.externalLabel = externalLabel;
4505
5023
  return _this;
4506
5024
  }
5025
+
5026
+ /**
5027
+ * Describe this field.
5028
+ *
5029
+ * The description is shown next to the field's label in the Val editor, so
5030
+ * it is where you say what an editor needs to know but the field name cannot
5031
+ * carry. It also travels in the serialized schema, which is what the AI
5032
+ * assistant and the MCP tools read.
5033
+ *
5034
+ * Pass `null` to clear a description set earlier.
5035
+ *
5036
+ * @example
5037
+ * const schema = s
5038
+ * .record(s.string())
5039
+ * .describe("Button labels, keyed by locale");
5040
+ * export default c.define("/example.val.ts", schema, { en: "Read more" });
5041
+ */
4507
5042
  _inherits(RecordSchema, _Schema);
4508
5043
  return _createClass(RecordSchema, [{
4509
5044
  key: "describe",
4510
5045
  value: function describe(description) {
4511
5046
  return new RecordSchema(this.item, this.opt, this.customValidateFunctions, this.currentRouter, this.keySchema, this.mediaOptions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.isJsonValues, this.previewInput, this.renderInput, this.externalLabel);
4512
5047
  }
5048
+
5049
+ /**
5050
+ * Add a custom validation rule to this field.
5051
+ *
5052
+ * The function is called with the field's value and returns `false` when the
5053
+ * value is fine, or a STRING with the message to show when it is not. Call it
5054
+ * more than once to add more rules — they all run, and every message is
5055
+ * reported.
5056
+ *
5057
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
5058
+ * when the value is fine, and `true` is not one of the two answers.
5059
+ *
5060
+ * Validation runs in the Studio as you type, in `npx val validate` and
5061
+ * before a publish.
5062
+ *
5063
+ * The function is given the whole record. To validate one entry, put a
5064
+ * `.validate(...)` on the ITEM schema instead — that reports the error on
5065
+ * the entry's own row, and on a `.jsonValues()` record it does not force
5066
+ * every entry to be loaded.
5067
+ *
5068
+ * @example
5069
+ * const schema = s.record(s.string()).validate((val) =>
5070
+ * "en" in val ? false : "There has to be an 'en' entry",
5071
+ * );
5072
+ * export default c.define("/example.val.ts", schema, { en: "Read more" });
5073
+ */
4513
5074
  }, {
4514
5075
  key: "validate",
4515
5076
  value: function validate(validationFunction) {
@@ -4871,6 +5432,10 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
4871
5432
  * must come BEFORE `.external()`: afterwards the value is a `Schema`, and
4872
5433
  * there is no `.readonly()` left to call — which is the error the compiler
4873
5434
  * gives, and a clearer one than a runtime throw.
5435
+ *
5436
+ * @example
5437
+ * const schema = s.record(s.string()).readonly();
5438
+ * export default c.define("/example.val.ts", schema, { en: "Read more" });
4874
5439
  */
4875
5440
  }, {
4876
5441
  key: "readonly",
@@ -4884,11 +5449,54 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
4884
5449
  var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
4885
5450
  return new RecordSchema(this.item, this.opt, this.customValidateFunctions, this.currentRouter, this.keySchema, this.mediaOptions, this.isReadonly, isHidden, this.description, this.isJsonValues, this.previewInput, this.renderInput, this.externalLabel);
4886
5451
  }
5452
+
5453
+ /**
5454
+ * Turn this record into a page router: its keys are route paths of your
5455
+ * application, and they are validated against the router's conventions.
5456
+ *
5457
+ * `s.router(router, item)` builds the same thing in one call and is what
5458
+ * most code should use — this is for adding a router to a record that
5459
+ * already exists.
5460
+ *
5461
+ * @example
5462
+ * import { nextAppRouter } from "../val.config";
5463
+ * const schema = s
5464
+ * .record(s.object({ title: s.string() }))
5465
+ * .router(nextAppRouter);
5466
+ * export default c.define("/app/[slug]/page.val.ts", schema, {
5467
+ * "/a-page-slug": { title: "First Page" },
5468
+ * });
5469
+ */
4887
5470
  }, {
4888
5471
  key: "router",
4889
5472
  value: function router(_router) {
4890
5473
  return new RecordSchema(this.item, this.opt, this.customValidateFunctions, _router, this.keySchema, this.mediaOptions, this.isReadonly, this.isHidden, this.description, this.isJsonValues, this.previewInput, this.renderInput, this.externalLabel);
4891
5474
  }
5475
+
5476
+ /**
5477
+ * Allow the files in this gallery to be stored on Val's remote content host
5478
+ * instead of in your repository.
5479
+ *
5480
+ * For `s.images()` and `s.files()`, which are records of media. Remote is off
5481
+ * until this is called.
5482
+ *
5483
+ * Each entry is then keyed by its URL on the content host rather than by a
5484
+ * path under `/public`. That key is not something to write by hand: upload
5485
+ * in the Studio, or add a local path and let `npx val validate --fix`
5486
+ * upload it and rewrite the key.
5487
+ *
5488
+ * @example
5489
+ * const schema = s.images({ directory: "/public/val/images" }).remote();
5490
+ * export default c.define("/content/images.val.ts", schema, {
5491
+ * "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/images/hero.webp":
5492
+ * {
5493
+ * width: 1920,
5494
+ * height: 1080,
5495
+ * mimeType: "image/webp",
5496
+ * alt: "Hero image",
5497
+ * },
5498
+ * });
5499
+ */
4892
5500
  }, {
4893
5501
  key: "remote",
4894
5502
  value: function remote() {
@@ -4910,6 +5518,20 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
4910
5518
  * nested inside an object/array/record is rejected at startup with a module
4911
5519
  * error, because the single-entry fetch endpoint, the Studio's content
4912
5520
  * substitution and content validation are all root-only.
5521
+ *
5522
+ * Must come BEFORE `.validate()` and `.preview()`: those are typed against
5523
+ * the un-lazy source shape, so a validator or preview declared first cannot
5524
+ * be carried over, and declaring one first throws rather than silently
5525
+ * dropping it.
5526
+ *
5527
+ * @example
5528
+ * import { nextAppRouter } from "../val.config";
5529
+ * const schema = s
5530
+ * .router(nextAppRouter, s.object({ title: s.string(), body: s.string() }))
5531
+ * .jsonValues();
5532
+ * export default c.define("/app/support/[slug]/page.val.ts", schema, {
5533
+ * "/support/faq": c.json(() => import("./content/faq.val.json")),
5534
+ * });
4913
5535
  */
4914
5536
  }, {
4915
5537
  key: "jsonValues",
@@ -4957,6 +5579,19 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
4957
5579
  * nested external record would leave a hole in the middle of a module's
4958
5580
  * source, and the Studio, patching and validation all assume a module's
4959
5581
  * source is complete apart from its entries.
5582
+ *
5583
+ * @example
5584
+ * const schema = s.record(s.object({ title: s.string() })).external("posts");
5585
+ * export default c.define("/content/posts.val.ts", schema, c.external());
5586
+ *
5587
+ * @example
5588
+ * // A read-only external record: `.readonly()` must come FIRST, because
5589
+ * // afterwards there is no `.readonly()` left on the type to call.
5590
+ * const schema = s
5591
+ * .record(s.object({ title: s.string() }))
5592
+ * .readonly()
5593
+ * .external("posts");
5594
+ * export default c.define("/content/posts.val.ts", schema, c.external());
4960
5595
  */
4961
5596
  }, {
4962
5597
  key: "external",
@@ -5216,6 +5851,25 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5216
5851
  * is the item of another container, in search, in references. What its
5217
5852
  * entries show is the ITEM schema's `preview`, not this. Never how the field
5218
5853
  * is edited (that is `render`). See `preview.ts`.
5854
+ *
5855
+ * @example
5856
+ * // The preview of the RECORD, for where it is an item of something else.
5857
+ * // What its own rows show is the ITEM schema's preview — see below.
5858
+ * const labels = s.record(s.string()).preview(({ val }) => ({
5859
+ * title: `${Object.keys(val).length} locales`,
5860
+ * }));
5861
+ * export default c.define("/example.val.ts", s.array(labels), [
5862
+ * { en: "Read more" },
5863
+ * ]);
5864
+ *
5865
+ * @example
5866
+ * // Rows of a record come from the ITEM's preview, not the record's:
5867
+ * const author = s
5868
+ * .object({ name: s.string(), role: s.string() })
5869
+ * .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
5870
+ * export default c.define("/example.val.ts", s.record(author), {
5871
+ * ada: { name: "Ada", role: "Engineer" },
5872
+ * });
5219
5873
  */
5220
5874
  }, {
5221
5875
  key: "preview",
@@ -5229,6 +5883,10 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5229
5883
  * instead of a preview row that navigates to it.
5230
5884
  *
5231
5885
  * Static configuration, not a callback — see `render.ts`.
5886
+ *
5887
+ * @example
5888
+ * const schema = s.array(s.record(s.string()).render({ as: "inline" }));
5889
+ * export default c.define("/example.val.ts", schema, [{ en: "Read more" }]);
5232
5890
  */
5233
5891
  }, {
5234
5892
  key: "render",
@@ -5357,12 +6015,48 @@ var BooleanSchema = /*#__PURE__*/function (_Schema) {
5357
6015
  _this.previewInput = previewInput;
5358
6016
  return _this;
5359
6017
  }
6018
+
6019
+ /**
6020
+ * Describe this field.
6021
+ *
6022
+ * The description is shown next to the field's label in the Val editor, so
6023
+ * it is where you say what an editor needs to know but the field name cannot
6024
+ * carry. It also travels in the serialized schema, which is what the AI
6025
+ * assistant and the MCP tools read.
6026
+ *
6027
+ * Pass `null` to clear a description set earlier.
6028
+ *
6029
+ * @example
6030
+ * const schema = s.boolean().describe("Show this item in the main menu");
6031
+ * export default c.define("/example.val.ts", schema, true);
6032
+ */
5360
6033
  _inherits(BooleanSchema, _Schema);
5361
6034
  return _createClass(BooleanSchema, [{
5362
6035
  key: "describe",
5363
6036
  value: function describe(description) {
5364
6037
  return new BooleanSchema(this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
5365
6038
  }
6039
+
6040
+ /**
6041
+ * Add a custom validation rule to this field.
6042
+ *
6043
+ * The function is called with the field's value and returns `false` when the
6044
+ * value is fine, or a STRING with the message to show when it is not. Call it
6045
+ * more than once to add more rules — they all run, and every message is
6046
+ * reported.
6047
+ *
6048
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
6049
+ * when the value is fine, and `true` is not one of the two answers.
6050
+ *
6051
+ * Validation runs in the Studio as you type, in `npx val validate` and
6052
+ * before a publish.
6053
+ *
6054
+ * @example
6055
+ * const schema = s.boolean().validate((val) =>
6056
+ * val === true ? false : "This has to be on for now",
6057
+ * );
6058
+ * export default c.define("/example.val.ts", schema, true);
6059
+ */
5366
6060
  }, {
5367
6061
  key: "validate",
5368
6062
  value: function validate(validationFunction) {
@@ -5445,6 +6139,10 @@ var BooleanSchema = /*#__PURE__*/function (_Schema) {
5445
6139
  * instead of a preview row that navigates to it.
5446
6140
  *
5447
6141
  * Static configuration, not a callback — see `render.ts`.
6142
+ *
6143
+ * @example
6144
+ * const schema = s.array(s.boolean().render({ as: "inline" }));
6145
+ * export default c.define("/example.val.ts", schema, [true, false]);
5448
6146
  */
5449
6147
  }, {
5450
6148
  key: "render",
@@ -5456,6 +6154,12 @@ var BooleanSchema = /*#__PURE__*/function (_Schema) {
5456
6154
  * How this VALUE is shown where a preview of it is needed — a row in a
5457
6155
  * sortable list, a reference dropdown, a search hit. Never how the field
5458
6156
  * itself is edited (that is `render`). See `preview.ts`.
6157
+ *
6158
+ * @example
6159
+ * const schema = s.array(
6160
+ * s.boolean().preview(({ val }) => ({ title: val ? "On" : "Off" })),
6161
+ * );
6162
+ * export default c.define("/example.val.ts", schema, [true, false]);
5459
6163
  */
5460
6164
  }, {
5461
6165
  key: "preview",
@@ -5814,10 +6518,8 @@ function isArraySchema(schema) {
5814
6518
  // );
5815
6519
  // }
5816
6520
 
5817
- function isUnionSchema(schema
5818
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5819
- ) {
5820
- return schema instanceof UnionSchema || _typeof(schema) === "object" && "type" in schema && schema.type === "union";
6521
+ function isDiscriminatedUnionSchema(schema) {
6522
+ return schema instanceof DiscriminatedUnionSchema || _typeof(schema) === "object" && "type" in schema && schema.type === "discriminated-union";
5821
6523
  }
5822
6524
  function isRichTextSchema(schema) {
5823
6525
  return schema instanceof RichTextSchema || _typeof(schema) === "object" && "type" in schema && schema.type === "richtext";
@@ -5923,27 +6625,18 @@ function resolvePath(path, valModule, schema) {
5923
6625
  source: resolvedSource
5924
6626
  }
5925
6627
  };
5926
- } else if (isUnionSchema(resolvedSchema)) {
5927
- var _key = resolvedSchema.key;
5928
- if (typeof _key !== "string") {
5929
- return {
5930
- v: {
5931
- path: origParts.map(function (p) {
5932
- if (!Number.isNaN(Number(p))) {
5933
- return p;
5934
- } else {
5935
- return JSON.stringify(p);
5936
- }
5937
- }).join("."),
5938
- // TODO: create a function generate path from parts (not sure if this always works)
5939
- schema: resolvedSchema,
5940
- source: resolvedSource
5941
- }
5942
- };
6628
+ } else if (isDiscriminatedUnionSchema(resolvedSchema)) {
6629
+ var _key = resolvedSchema instanceof DiscriminatedUnionSchema ? resolvedSchema["key"] : resolvedSchema.key;
6630
+ // A nullable union holding `null` has no variant to descend into, and
6631
+ // `null[key]` throws before any of the reporting below can run.
6632
+ if (resolvedSource === null || _typeof(resolvedSource) !== "object") {
6633
+ throw Error("Schema type error: expected discriminated union source to be an object, but got ".concat(resolvedSource === null ? "null" : _typeof(resolvedSource), " in path: ").concat(path));
5943
6634
  }
5944
6635
  var keyValue = resolvedSource[_key];
5945
- if (!keyValue) {
5946
- throw Error("Invalid path: union source ".concat(resolvedSchema, " did not have required key ").concat(_key, " in path: ").concat(path));
6636
+ // `undefined`, not falsy: `s.literal("")` is a legal tag, and a variant
6637
+ // carrying it was reported as a missing key.
6638
+ if (keyValue === undefined) {
6639
+ throw Error("Invalid path: discriminated union source ".concat(resolvedSchema, " did not have required key ").concat(_key, " in path: ").concat(path));
5947
6640
  }
5948
6641
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5949
6642
  var schemaOfUnionKey = resolvedSchema.items.find(
@@ -5953,7 +6646,7 @@ function resolvePath(path, valModule, schema) {
5953
6646
  return (child === null || child === void 0 || (_child$items = child.items) === null || _child$items === void 0 || (_child$items = _child$items[_key]) === null || _child$items === void 0 ? void 0 : _child$items.value) === keyValue;
5954
6647
  });
5955
6648
  if (!schemaOfUnionKey) {
5956
- throw Error("Invalid path: union schema ".concat(resolvedSchema, " did not have a child object with ").concat(_key, " of value ").concat(keyValue, " in path: ").concat(path));
6649
+ throw Error("Invalid path: discriminated union schema ".concat(resolvedSchema, " did not have a child object with ").concat(_key, " of value ").concat(keyValue, " in path: ").concat(path));
5957
6650
  }
5958
6651
  resolvedSchema = schemaOfUnionKey.items[part];
5959
6652
  resolvedSource = resolvedSource[part];
@@ -6173,31 +6866,25 @@ function safeResolvePath(path, valModule, schema) {
6173
6866
  source: resolvedSource
6174
6867
  }
6175
6868
  };
6176
- } else if (isUnionSchema(resolvedSchema)) {
6177
- var _key2 = resolvedSchema.key;
6178
- if (typeof _key2 !== "string") {
6869
+ } else if (isDiscriminatedUnionSchema(resolvedSchema)) {
6870
+ var _key2 = resolvedSchema instanceof DiscriminatedUnionSchema ? resolvedSchema["key"] : resolvedSchema.key;
6871
+ // See the note in `resolvePath`: a null union has no variant to descend
6872
+ // into, and this API promises a structured error rather than a throw.
6873
+ if (resolvedSource === null || _typeof(resolvedSource) !== "object") {
6179
6874
  return {
6180
6875
  v: {
6181
- status: "ok",
6182
- path: origParts.map(function (p) {
6183
- if (!Number.isNaN(Number(p))) {
6184
- return p;
6185
- } else {
6186
- return JSON.stringify(p);
6187
- }
6188
- }).join("."),
6189
- // TODO: create a function generate path from parts (not sure if this always works)
6190
- schema: resolvedSchema,
6191
- source: resolvedSource
6876
+ status: "error",
6877
+ message: "Schema type error: expected discriminated union source to be an object, but got ".concat(resolvedSource === null ? "null" : _typeof(resolvedSource), " in path: ").concat(path)
6192
6878
  }
6193
6879
  };
6194
6880
  }
6195
6881
  var keyValue = resolvedSource[_key2];
6196
- if (!keyValue) {
6882
+ // See the note in `resolvePath`: an empty-string tag is a present key.
6883
+ if (keyValue === undefined) {
6197
6884
  return {
6198
6885
  v: {
6199
6886
  status: "error",
6200
- message: "Invalid path: union source ".concat(resolvedSchema, " did not have required key ").concat(_key2, " in path: ").concat(path)
6887
+ message: "Invalid path: discriminated union source ".concat(resolvedSchema, " did not have required key ").concat(_key2, " in path: ").concat(path)
6201
6888
  }
6202
6889
  };
6203
6890
  }
@@ -6212,7 +6899,7 @@ function safeResolvePath(path, valModule, schema) {
6212
6899
  return {
6213
6900
  v: {
6214
6901
  status: "error",
6215
- message: "Invalid path: union schema ".concat(resolvedSchema, " did not have a child object with ").concat(_key2, " of value ").concat(keyValue, " in path: ").concat(path)
6902
+ message: "Invalid path: discriminated union schema ".concat(resolvedSchema, " did not have a child object with ").concat(_key2, " of value ").concat(keyValue, " in path: ").concat(path)
6216
6903
  }
6217
6904
  };
6218
6905
  }
@@ -6349,12 +7036,48 @@ var NumberSchema = /*#__PURE__*/function (_Schema) {
6349
7036
  _this.previewInput = previewInput;
6350
7037
  return _this;
6351
7038
  }
7039
+
7040
+ /**
7041
+ * Describe this field.
7042
+ *
7043
+ * The description is shown next to the field's label in the Val editor, so
7044
+ * it is where you say what an editor needs to know but the field name cannot
7045
+ * carry. It also travels in the serialized schema, which is what the AI
7046
+ * assistant and the MCP tools read.
7047
+ *
7048
+ * Pass `null` to clear a description set earlier.
7049
+ *
7050
+ * @example
7051
+ * const schema = s.number().describe("How many columns the grid uses");
7052
+ * export default c.define("/example.val.ts", schema, 3);
7053
+ */
6352
7054
  _inherits(NumberSchema, _Schema);
6353
7055
  return _createClass(NumberSchema, [{
6354
7056
  key: "describe",
6355
7057
  value: function describe(description) {
6356
7058
  return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
6357
7059
  }
7060
+
7061
+ /**
7062
+ * Add a custom validation rule to this field.
7063
+ *
7064
+ * The function is called with the field's value and returns `false` when the
7065
+ * value is fine, or a STRING with the message to show when it is not. Call it
7066
+ * more than once to add more rules — they all run, and every message is
7067
+ * reported.
7068
+ *
7069
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
7070
+ * when the value is fine, and `true` is not one of the two answers.
7071
+ *
7072
+ * Validation runs in the Studio as you type, in `npx val validate` and
7073
+ * before a publish.
7074
+ *
7075
+ * @example
7076
+ * const schema = s.number().validate((val) =>
7077
+ * Number.isInteger(val) ? false : "Must be a whole number",
7078
+ * );
7079
+ * export default c.define("/example.val.ts", schema, 3);
7080
+ */
6358
7081
  }, {
6359
7082
  key: "validate",
6360
7083
  value: function validate(validationFunction) {
@@ -6382,16 +7105,290 @@ var NumberSchema = /*#__PURE__*/function (_Schema) {
6382
7105
  value: src
6383
7106
  }]));
6384
7107
  }
6385
- if ((_this$options2 = this.options) !== null && _this$options2 !== void 0 && _this$options2.min && src < this.options.min) {
6386
- return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
6387
- message: "Expected 'number' greater than ".concat(this.options.min),
7108
+ if ((_this$options2 = this.options) !== null && _this$options2 !== void 0 && _this$options2.min && src < this.options.min) {
7109
+ return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
7110
+ message: "Expected 'number' greater than ".concat(this.options.min),
7111
+ value: src
7112
+ }]));
7113
+ }
7114
+ if (customValidationErrors.length > 0) {
7115
+ return _defineProperty({}, path, customValidationErrors);
7116
+ }
7117
+ return false;
7118
+ }
7119
+ }, {
7120
+ key: "executeAssert",
7121
+ value: function executeAssert(path, src) {
7122
+ if (this.opt && src === null) {
7123
+ return {
7124
+ success: true,
7125
+ data: src
7126
+ };
7127
+ }
7128
+ if (src === null) {
7129
+ return {
7130
+ success: false,
7131
+ errors: _defineProperty({}, path, [{
7132
+ message: "Expected 'number', got 'null'",
7133
+ typeError: true
7134
+ }])
7135
+ };
7136
+ }
7137
+ if (typeof src === "number") {
7138
+ return {
7139
+ success: true,
7140
+ data: src
7141
+ };
7142
+ }
7143
+ return {
7144
+ success: false,
7145
+ errors: _defineProperty({}, path, [{
7146
+ message: "Expected 'number', got '".concat(_typeof(src), "'"),
7147
+ typeError: true
7148
+ }])
7149
+ };
7150
+ }
7151
+ }, {
7152
+ key: "nullable",
7153
+ value: function nullable() {
7154
+ return new NumberSchema(this.options, true, [], this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7155
+ }
7156
+ }, {
7157
+ key: "readonly",
7158
+ value: function readonly() {
7159
+ var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
7160
+ return new NumberSchema(this.options, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7161
+ }
7162
+ }, {
7163
+ key: "hidden",
7164
+ value: function hidden() {
7165
+ var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
7166
+ return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.renderInput, this.previewInput);
7167
+ }
7168
+
7169
+ /**
7170
+ * Validate that the number is at most `max` (inclusive).
7171
+ *
7172
+ * @example
7173
+ * const schema = s.number().max(4);
7174
+ * export default c.define("/example.val.ts", schema, 3);
7175
+ */
7176
+ }, {
7177
+ key: "max",
7178
+ value: function max(_max) {
7179
+ return new NumberSchema(_objectSpread2(_objectSpread2({}, this.options), {}, {
7180
+ max: _max
7181
+ }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7182
+ }
7183
+
7184
+ /**
7185
+ * Validate that the number is at least `min` (inclusive).
7186
+ *
7187
+ * @example
7188
+ * const schema = s.number().min(1).max(4);
7189
+ * export default c.define("/example.val.ts", schema, 3);
7190
+ */
7191
+ }, {
7192
+ key: "min",
7193
+ value: function min(_min) {
7194
+ return new NumberSchema(_objectSpread2(_objectSpread2({}, this.options), {}, {
7195
+ min: _min
7196
+ }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7197
+ }
7198
+ }, {
7199
+ key: "executeCustomValidateAt",
7200
+ value: function executeCustomValidateAt(path, src) {
7201
+ return this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
7202
+ path: path
7203
+ });
7204
+ }
7205
+
7206
+ /**
7207
+ * How this field is laid out in the editor when it is the item of an array
7208
+ * or record: `{ as: "inline" }` renders the field itself inside each row,
7209
+ * instead of a preview row that navigates to it.
7210
+ *
7211
+ * Static configuration, not a callback — see `render.ts`.
7212
+ *
7213
+ * @example
7214
+ * const schema = s.array(s.number().render({ as: "inline" }));
7215
+ * export default c.define("/example.val.ts", schema, [1, 2]);
7216
+ */
7217
+ }, {
7218
+ key: "render",
7219
+ value: function render(input) {
7220
+ return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, input, this.previewInput);
7221
+ }
7222
+
7223
+ /**
7224
+ * How this VALUE is shown where a preview of it is needed — a row in a
7225
+ * sortable list, a reference dropdown, a search hit. Never how the field
7226
+ * itself is edited (that is `render`). See `preview.ts`.
7227
+ *
7228
+ * @example
7229
+ * const schema = s.array(
7230
+ * s.number().preview(({ val }) => ({ title: String(val) })),
7231
+ * );
7232
+ * export default c.define("/example.val.ts", schema, [1, 2]);
7233
+ */
7234
+ }, {
7235
+ key: "preview",
7236
+ value: function preview(select) {
7237
+ return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, select);
7238
+ }
7239
+ }, {
7240
+ key: "executePreviewItem",
7241
+ value: function executePreviewItem(src) {
7242
+ if (this.previewInput === null) {
7243
+ return null;
7244
+ }
7245
+ return this.previewInput({
7246
+ val: src
7247
+ });
7248
+ }
7249
+ }, {
7250
+ key: "declaresItemPreview",
7251
+ value: function declaresItemPreview() {
7252
+ return this.previewInput !== null;
7253
+ }
7254
+ }, {
7255
+ key: "executeSerialize",
7256
+ value: function executeSerialize() {
7257
+ var _this$renderInput, _this$customValidateF;
7258
+ return {
7259
+ type: "number",
7260
+ render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
7261
+ preview: this.previewInput ? true : undefined,
7262
+ options: this.options,
7263
+ opt: this.opt,
7264
+ customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
7265
+ readonly: this.isReadonly,
7266
+ hidden: this.isHidden,
7267
+ description: this.description
7268
+ };
7269
+ }
7270
+ }, {
7271
+ key: "executePreview",
7272
+ value: function executePreview() {
7273
+ return {};
7274
+ }
7275
+ }]);
7276
+ }(Schema);
7277
+ var number = function number(options) {
7278
+ return new NumberSchema(options);
7279
+ };
7280
+
7281
+ /**
7282
+ * One of a fixed set of strings.
7283
+ *
7284
+ * The values are the schema — there are no member schemas to descend into, so
7285
+ * this is a LEAF, like `s.string()` with a closed domain. That is the whole
7286
+ * difference from {@link DiscriminatedUnionSchema}, and the reason the two are
7287
+ * separate: everything that walks a schema tree has to recurse into a
7288
+ * discriminated union's variants and must not try to recurse into these.
7289
+ */
7290
+ var EnumSchema = /*#__PURE__*/function (_Schema) {
7291
+ function EnumSchema(values) {
7292
+ var _this;
7293
+ var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
7294
+ var customValidateFunctions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
7295
+ var isReadonly = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
7296
+ var isHidden = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
7297
+ var description = arguments.length > 5 ? arguments[5] : undefined;
7298
+ var renderInput = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;
7299
+ var previewInput = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
7300
+ _classCallCheck(this, EnumSchema);
7301
+ _this = _callSuper(this, EnumSchema);
7302
+ _this.values = values;
7303
+ _this.opt = opt;
7304
+ _this.customValidateFunctions = customValidateFunctions;
7305
+ _this.isReadonly = isReadonly;
7306
+ _this.isHidden = isHidden;
7307
+ _this.description = description;
7308
+ _this.renderInput = renderInput;
7309
+ _this.previewInput = previewInput;
7310
+ return _this;
7311
+ }
7312
+
7313
+ /**
7314
+ * Describe this field.
7315
+ *
7316
+ * The description is shown next to the field's label in the Val editor, so it is
7317
+ * where you say what an editor needs to know but the field name cannot carry. It
7318
+ * also travels in the serialized schema, which is what the AI assistant and the
7319
+ * MCP tools read.
7320
+ *
7321
+ * Pass `null` to clear a description set earlier.
7322
+ *
7323
+ * @example
7324
+ * const schema = s
7325
+ * .enum("draft", "published")
7326
+ * .describe("Only published pages are built");
7327
+ * export default c.define("/example.val.ts", schema, "draft");
7328
+ */
7329
+ _inherits(EnumSchema, _Schema);
7330
+ return _createClass(EnumSchema, [{
7331
+ key: "describe",
7332
+ value: function describe(description) {
7333
+ return new EnumSchema(this.values, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
7334
+ }
7335
+
7336
+ /**
7337
+ * Add a custom validation rule to this field.
7338
+ *
7339
+ * The function is called with the field's value and returns `false` when the
7340
+ * value is fine, or a STRING with the message to show when it is not. Call it
7341
+ * more than once to add more rules — they all run, and every message is
7342
+ * reported.
7343
+ *
7344
+ * Validation runs in the Studio as you type, in `npx val validate` and before a
7345
+ * publish.
7346
+ *
7347
+ * @example
7348
+ * const schema = s
7349
+ * .enum("draft", "published")
7350
+ * .validate((val) =>
7351
+ * val === "published" ? "Publishing is frozen this week" : false,
7352
+ * );
7353
+ * export default c.define("/example.val.ts", schema, "draft");
7354
+ */
7355
+ }, {
7356
+ key: "validate",
7357
+ value: function validate(validationFunction) {
7358
+ return new EnumSchema(this.values, this.opt, this.customValidateFunctions.concat(validationFunction), this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7359
+ }
7360
+ }, {
7361
+ key: "executeValidate",
7362
+ value: function executeValidate(path, src) {
7363
+ var customValidationErrors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
7364
+ path: path
7365
+ });
7366
+ // The custom validators always report, whatever the structural answer is.
7367
+ var errors = customValidationErrors;
7368
+ var unknownSrc = src;
7369
+ if (this.opt && (unknownSrc === null || unknownSrc === undefined)) {
7370
+ return errors.length > 0 ? _defineProperty({}, path, errors) : false;
7371
+ }
7372
+ if (!Array.isArray(this.values) || this.values.length === 0) {
7373
+ errors.push({
7374
+ message: "An enum schema must have at least one value",
7375
+ schemaError: true
7376
+ });
7377
+ } else if (typeof unknownSrc !== "string") {
7378
+ errors.push({
7379
+ message: "Expected 'string', got '".concat(unknownSrc === null ? "null" : _typeof(unknownSrc), "'"),
7380
+ value: src,
7381
+ typeError: true
7382
+ });
7383
+ } else if (!this.values.includes(unknownSrc)) {
7384
+ errors.push({
7385
+ message: "Value must be one of the following: ".concat(this.values.map(function (value) {
7386
+ return "\"".concat(value, "\"");
7387
+ }).join(", ")),
6388
7388
  value: src
6389
- }]));
6390
- }
6391
- if (customValidationErrors.length > 0) {
6392
- return _defineProperty({}, path, customValidationErrors);
7389
+ });
6393
7390
  }
6394
- return false;
7391
+ return errors.length > 0 ? _defineProperty({}, path, errors) : false;
6395
7392
  }
6396
7393
  }, {
6397
7394
  key: "executeAssert",
@@ -6402,59 +7399,61 @@ var NumberSchema = /*#__PURE__*/function (_Schema) {
6402
7399
  data: src
6403
7400
  };
6404
7401
  }
6405
- if (src === null) {
7402
+ var errors = {};
7403
+ if (typeof src !== "string") {
7404
+ errors[path] = [{
7405
+ message: "Expected 'string', got '".concat(src === null ? "null" : _typeof(src), "'"),
7406
+ typeError: true
7407
+ }];
6406
7408
  return {
6407
7409
  success: false,
6408
- errors: _defineProperty({}, path, [{
6409
- message: "Expected 'number', got 'null'",
6410
- typeError: true
6411
- }])
7410
+ errors: errors
6412
7411
  };
6413
7412
  }
6414
- if (typeof src === "number") {
7413
+ if (!Array.isArray(this.values) || this.values.length === 0) {
7414
+ errors[path] = [{
7415
+ message: "An enum schema must have at least one value",
7416
+ schemaError: true
7417
+ }];
6415
7418
  return {
6416
- success: true,
6417
- data: src
7419
+ success: false,
7420
+ errors: errors
6418
7421
  };
6419
7422
  }
6420
- return {
6421
- success: false,
6422
- errors: _defineProperty({}, path, [{
6423
- message: "Expected 'number', got '".concat(_typeof(src), "'"),
7423
+ if (!this.values.includes(src)) {
7424
+ errors[path] = [{
7425
+ message: "Expected one of ".concat(this.values.map(function (value) {
7426
+ return "'".concat(value, "'");
7427
+ }).join(", "), ", got '").concat(src, "'"),
6424
7428
  typeError: true
6425
- }])
7429
+ }];
7430
+ return {
7431
+ success: false,
7432
+ errors: errors
7433
+ };
7434
+ }
7435
+ return {
7436
+ success: true,
7437
+ data: src
6426
7438
  };
6427
7439
  }
6428
7440
  }, {
6429
7441
  key: "nullable",
6430
7442
  value: function nullable() {
6431
- return new NumberSchema(this.options, true, [], this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7443
+ // Explicit type args: `previewInput` would otherwise pin inference to `Src`.
7444
+ return new EnumSchema(this.values, true, [], this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
6432
7445
  }
6433
7446
  }, {
6434
7447
  key: "readonly",
6435
7448
  value: function readonly() {
6436
7449
  var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
6437
- return new NumberSchema(this.options, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7450
+ return new EnumSchema(this.values, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
6438
7451
  }
6439
7452
  }, {
6440
7453
  key: "hidden",
6441
7454
  value: function hidden() {
6442
7455
  var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
6443
- return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.renderInput, this.previewInput);
6444
- }
6445
- }, {
6446
- key: "max",
6447
- value: function max(_max) {
6448
- return new NumberSchema(_objectSpread2(_objectSpread2({}, this.options), {}, {
6449
- max: _max
6450
- }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
6451
- }
6452
- }, {
6453
- key: "min",
6454
- value: function min(_min) {
6455
- return new NumberSchema(_objectSpread2(_objectSpread2({}, this.options), {}, {
6456
- min: _min
6457
- }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7456
+ return new EnumSchema(this.values, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.renderInput, this.previewInput);
6458
7457
  }
6459
7458
  }, {
6460
7459
  key: "executeCustomValidateAt",
@@ -6470,22 +7469,34 @@ var NumberSchema = /*#__PURE__*/function (_Schema) {
6470
7469
  * instead of a preview row that navigates to it.
6471
7470
  *
6472
7471
  * Static configuration, not a callback — see `render.ts`.
7472
+ *
7473
+ * @example
7474
+ * const schema = s.array(
7475
+ * s.enum("draft", "published").render({ as: "inline" }),
7476
+ * );
7477
+ * export default c.define("/example.val.ts", schema, ["draft"]);
6473
7478
  */
6474
7479
  }, {
6475
7480
  key: "render",
6476
7481
  value: function render(input) {
6477
- return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, input, this.previewInput);
7482
+ return new EnumSchema(this.values, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, input, this.previewInput);
6478
7483
  }
6479
7484
 
6480
7485
  /**
6481
7486
  * How this VALUE is shown where a preview of it is needed — a row in a
6482
7487
  * sortable list, a reference dropdown, a search hit. Never how the field
6483
7488
  * itself is edited (that is `render`). See `preview.ts`.
7489
+ *
7490
+ * @example
7491
+ * const schema = s.array(
7492
+ * s.enum("draft", "published").preview(({ val }) => ({ title: val })),
7493
+ * );
7494
+ * export default c.define("/example.val.ts", schema, ["draft"]);
6484
7495
  */
6485
7496
  }, {
6486
7497
  key: "preview",
6487
7498
  value: function preview(select) {
6488
- return new NumberSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, select);
7499
+ return new EnumSchema(this.values, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, select);
6489
7500
  }
6490
7501
  }, {
6491
7502
  key: "executePreviewItem",
@@ -6507,10 +7518,10 @@ var NumberSchema = /*#__PURE__*/function (_Schema) {
6507
7518
  value: function executeSerialize() {
6508
7519
  var _this$renderInput, _this$customValidateF;
6509
7520
  return {
6510
- type: "number",
7521
+ type: "enum",
6511
7522
  render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
6512
7523
  preview: this.previewInput ? true : undefined,
6513
- options: this.options,
7524
+ values: _toConsumableArray(this.values),
6514
7525
  opt: this.opt,
6515
7526
  customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
6516
7527
  readonly: this.isReadonly,
@@ -6521,14 +7532,104 @@ var NumberSchema = /*#__PURE__*/function (_Schema) {
6521
7532
  }, {
6522
7533
  key: "executePreview",
6523
7534
  value: function executePreview() {
7535
+ // A leaf: nothing below it to preview.
6524
7536
  return {};
6525
7537
  }
6526
7538
  }]);
6527
7539
  }(Schema);
6528
- var number = function number(options) {
6529
- return new NumberSchema(options);
7540
+
7541
+ /**
7542
+ * Define a string that must be one of a fixed set of values.
7543
+ *
7544
+ * Named `enumSchema` rather than `enum` because `enum` is a reserved word and
7545
+ * cannot be a binding name; it is exposed as `s.enum`.
7546
+ */
7547
+ var enumSchema = function enumSchema() {
7548
+ for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
7549
+ values[_key] = arguments[_key];
7550
+ }
7551
+ return new EnumSchema(values);
6530
7552
  };
6531
7553
 
7554
+ /**
7555
+ * @deprecated Use {@link SerializedDiscriminatedUnionSchema} or
7556
+ * {@link SerializedEnumSchema}. A serialized union is now one or the other,
7557
+ * distinguished by `type` rather than by the shape of `key`.
7558
+ */
7559
+
7560
+ /** @deprecated Use {@link SerializedEnumSchema}. */
7561
+
7562
+ /** @deprecated Use {@link SerializedDiscriminatedUnionSchema}. */
7563
+
7564
+ /**
7565
+ * @deprecated There is no single union schema class any more: name
7566
+ * {@link DiscriminatedUnionSchema} or {@link EnumSchema}. The type parameters
7567
+ * are kept so existing annotations still compile, but only `Src` is used.
7568
+ */
7569
+
7570
+ /**
7571
+ * The value type of a list of literal schemas — how `s.union` typed a union of
7572
+ * literals. Inferred from `Schema<infer S>` rather than `LiteralSchema<infer S>`
7573
+ * because a `LiteralSchema` is invariant in its source (its custom validate
7574
+ * functions put it in a parameter position), so `LiteralSchema<"a">` is not
7575
+ * assignable to `LiteralSchema<"a" | "b">`.
7576
+ */
7577
+
7578
+ /**
7579
+ * @deprecated Use `s.discriminatedUnion(key, ...objects)` for a tagged union of
7580
+ * objects, or `s.enum("a", "b")` for one of a fixed set of strings.
7581
+ *
7582
+ * One thing does NOT survive the literal form: an enum's values are strings,
7583
+ * so a member literal's own `.validate()` and `.describe()` are dropped rather
7584
+ * than carried over. `s.union(s.literal("a").validate(...), ...)` ran that
7585
+ * validator as part of deciding whether the value matched; `s.enum` has no
7586
+ * member to run. A validator on a single-valued literal could only accept or
7587
+ * reject that one value, which the enum's own value list already decides — but
7588
+ * if you rely on one, keep it by validating the enum itself.
7589
+ *
7590
+ * @example
7591
+ * // was: s.union("type", s.object({ type: s.literal("a"), a: s.string() }))
7592
+ * const schema = s.discriminatedUnion(
7593
+ * "type",
7594
+ * s.object({ type: s.literal("a"), a: s.string() }),
7595
+ * );
7596
+ * export default c.define("/example.val.ts", schema, { type: "a", a: "one" });
7597
+ *
7598
+ * @example
7599
+ * // was: s.union(s.literal("a"), s.literal("b"))
7600
+ * const schema = s.enum("a", "b");
7601
+ * export default c.define("/example.val.ts", schema, "a");
7602
+ */
7603
+
7604
+ function union(keyOrFirstLiteral) {
7605
+ for (var _len = arguments.length, rest = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
7606
+ rest[_key - 1] = arguments[_key];
7607
+ }
7608
+ if (typeof keyOrFirstLiteral === "string") {
7609
+ return new DiscriminatedUnionSchema(keyOrFirstLiteral, rest);
7610
+ }
7611
+ // A literal union is an enum: the values ARE the schema, so the literals are
7612
+ // flattened into them. `key` was only ever the first member.
7613
+ var literals = [keyOrFirstLiteral].concat(rest);
7614
+ var values = [];
7615
+ var _iterator = result._createForOfIteratorHelper(literals),
7616
+ _step;
7617
+ try {
7618
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
7619
+ var literal = _step.value;
7620
+ if (!(literal instanceof LiteralSchema)) {
7621
+ throw new Error("s.union: expected either a string key followed by object schemas, or literal schemas. Got: ".concat(JSON.stringify(literal)));
7622
+ }
7623
+ values.push(literal["value"]);
7624
+ }
7625
+ } catch (err) {
7626
+ _iterator.e(err);
7627
+ } finally {
7628
+ _iterator.f();
7629
+ }
7630
+ return new EnumSchema(values);
7631
+ }
7632
+
6532
7633
  var KeyOfSchema = /*#__PURE__*/function (_Schema) {
6533
7634
  function KeyOfSchema(schema, sourcePath) {
6534
7635
  var _this;
@@ -6552,12 +7653,52 @@ var KeyOfSchema = /*#__PURE__*/function (_Schema) {
6552
7653
  _this.previewInput = previewInput;
6553
7654
  return _this;
6554
7655
  }
7656
+
7657
+ /**
7658
+ * Describe this field.
7659
+ *
7660
+ * The description is shown next to the field's label in the Val editor, so
7661
+ * it is where you say what an editor needs to know but the field name cannot
7662
+ * carry. It also travels in the serialized schema, which is what the AI
7663
+ * assistant and the MCP tools read.
7664
+ *
7665
+ * Pass `null` to clear a description set earlier.
7666
+ *
7667
+ * @example
7668
+ * import authorsVal from "./authors.val"; // a record module
7669
+ * const schema = s.keyOf(authorsVal).describe("Who wrote this post");
7670
+ * export default c.define("/example.val.ts", schema, "ada");
7671
+ */
6555
7672
  _inherits(KeyOfSchema, _Schema);
6556
7673
  return _createClass(KeyOfSchema, [{
6557
7674
  key: "describe",
6558
7675
  value: function describe(description) {
6559
7676
  return new KeyOfSchema(this.schema, this.sourcePath, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
6560
7677
  }
7678
+
7679
+ /**
7680
+ * Add a custom validation rule to this field.
7681
+ *
7682
+ * The function is called with the field's value and returns `false` when the
7683
+ * value is fine, or a STRING with the message to show when it is not. Call it
7684
+ * more than once to add more rules — they all run, and every message is
7685
+ * reported.
7686
+ *
7687
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
7688
+ * when the value is fine, and `true` is not one of the two answers.
7689
+ *
7690
+ * Validation runs in the Studio as you type, in `npx val validate` and
7691
+ * before a publish.
7692
+ *
7693
+ * @example
7694
+ * import authorsVal from "./authors.val"; // a record module
7695
+ * const schema = s
7696
+ * .keyOf(authorsVal)
7697
+ * .validate((val) =>
7698
+ * val === "placeholder" ? "Pick a real author" : false,
7699
+ * );
7700
+ * export default c.define("/example.val.ts", schema, "ada");
7701
+ */
6561
7702
  }, {
6562
7703
  key: "validate",
6563
7704
  value: function validate(validationFunction) {
@@ -6719,6 +7860,11 @@ var KeyOfSchema = /*#__PURE__*/function (_Schema) {
6719
7860
  * instead of a preview row that navigates to it.
6720
7861
  *
6721
7862
  * Static configuration, not a callback — see `render.ts`.
7863
+ *
7864
+ * @example
7865
+ * import authorsVal from "./authors.val"; // a record module
7866
+ * const schema = s.array(s.keyOf(authorsVal).render({ as: "inline" }));
7867
+ * export default c.define("/example.val.ts", schema, ["ada"]);
6722
7868
  */
6723
7869
  }, {
6724
7870
  key: "render",
@@ -6730,6 +7876,13 @@ var KeyOfSchema = /*#__PURE__*/function (_Schema) {
6730
7876
  * How this VALUE is shown where a preview of it is needed — a row in a
6731
7877
  * sortable list, a reference dropdown, a search hit. Never how the field
6732
7878
  * itself is edited (that is `render`). See `preview.ts`.
7879
+ *
7880
+ * @example
7881
+ * import authorsVal from "./authors.val"; // a record module
7882
+ * const schema = s.array(
7883
+ * s.keyOf(authorsVal).preview(({ val }) => ({ title: val })),
7884
+ * );
7885
+ * export default c.define("/example.val.ts", schema, ["ada"]);
6733
7886
  */
6734
7887
  }, {
6735
7888
  key: "preview",
@@ -6846,17 +7999,83 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
6846
7999
  _this.previewInput = previewInput;
6847
8000
  return _this;
6848
8001
  }
8002
+
8003
+ /**
8004
+ * Describe this field.
8005
+ *
8006
+ * The description is shown next to the field's label in the Val editor, so
8007
+ * it is where you say what an editor needs to know but the field name cannot
8008
+ * carry. It also travels in the serialized schema, which is what the AI
8009
+ * assistant and the MCP tools read.
8010
+ *
8011
+ * Pass `null` to clear a description set earlier.
8012
+ *
8013
+ * @example
8014
+ * const schema = s.file().describe("The PDF offered for download");
8015
+ * export default c.define("/example.val.ts", schema, {
8016
+ * path: "/public/val/example.pdf",
8017
+ * mimeType: "application/pdf",
8018
+ * });
8019
+ */
6849
8020
  _inherits(FileSchema, _Schema);
6850
8021
  return _createClass(FileSchema, [{
6851
8022
  key: "describe",
6852
8023
  value: function describe(description) {
6853
8024
  return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
6854
8025
  }
8026
+
8027
+ /**
8028
+ * Store the file on Val's remote content host instead of in your repository.
8029
+ *
8030
+ * The bytes still go into the patch store when the file is uploaded — the
8031
+ * push to the remote host happens at publish. What changes is where the
8032
+ * published file lives: `path` becomes a remote URL rather than a path under
8033
+ * `/public`, so the repository does not grow with every upload.
8034
+ *
8035
+ * The `path` of a remote file is a URL on the content host, and it is not
8036
+ * something to write by hand: upload the file in the Studio, or write a
8037
+ * local path and let `npx val validate --fix` upload it and rewrite the
8038
+ * `path` to the ref below.
8039
+ *
8040
+ * @example
8041
+ * const schema = s.file({ accept: "application/pdf" }).remote();
8042
+ * export default c.define("/example.val.ts", schema, {
8043
+ * path: "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/example.pdf",
8044
+ * mimeType: "application/pdf",
8045
+ * });
8046
+ */
6855
8047
  }, {
6856
8048
  key: "remote",
6857
8049
  value: function remote() {
6858
8050
  return new FileSchema(this.options, this.opt, true, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
6859
8051
  }
8052
+
8053
+ /**
8054
+ * Add a custom validation rule to this field.
8055
+ *
8056
+ * The function is called with the field's value and returns `false` when the
8057
+ * value is fine, or a STRING with the message to show when it is not. Call it
8058
+ * more than once to add more rules — they all run, and every message is
8059
+ * reported.
8060
+ *
8061
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
8062
+ * when the value is fine, and `true` is not one of the two answers.
8063
+ *
8064
+ * Validation runs in the Studio as you type, in `npx val validate` and
8065
+ * before a publish.
8066
+ *
8067
+ * The second argument carries the `path` of the field being validated, for
8068
+ * when the message needs to say where the problem is.
8069
+ *
8070
+ * @example
8071
+ * const schema = s.file().validate((val) =>
8072
+ * val.path.endsWith(".pdf") ? false : "Must be a PDF",
8073
+ * );
8074
+ * export default c.define("/example.val.ts", schema, {
8075
+ * path: "/public/val/example.pdf",
8076
+ * mimeType: "application/pdf",
8077
+ * });
8078
+ */
6860
8079
  }, {
6861
8080
  key: "validate",
6862
8081
  value: function validate(validationFunction) {
@@ -7063,6 +8282,12 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
7063
8282
  * instead of a preview row that navigates to it.
7064
8283
  *
7065
8284
  * Static configuration, not a callback — see `render.ts`.
8285
+ *
8286
+ * @example
8287
+ * const schema = s.array(s.file().render({ as: "inline" }));
8288
+ * export default c.define("/example.val.ts", schema, [
8289
+ * { path: "/public/val/example.pdf", mimeType: "application/pdf" },
8290
+ * ]);
7066
8291
  */
7067
8292
  }, {
7068
8293
  key: "render",
@@ -7074,6 +8299,14 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
7074
8299
  * How this VALUE is shown where a preview of it is needed — a row in a
7075
8300
  * sortable list, a reference dropdown, a search hit. Never how the field
7076
8301
  * itself is edited (that is `render`). See `preview.ts`.
8302
+ *
8303
+ * @example
8304
+ * const schema = s.array(
8305
+ * s.file().preview(({ val }) => ({ title: val.path })),
8306
+ * );
8307
+ * export default c.define("/example.val.ts", schema, [
8308
+ * { path: "/public/val/example.pdf", mimeType: "application/pdf" },
8309
+ * ]);
7077
8310
  */
7078
8311
  }, {
7079
8312
  key: "preview",
@@ -7207,12 +8440,48 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
7207
8440
  _this.previewInput = previewInput;
7208
8441
  return _this;
7209
8442
  }
8443
+
8444
+ /**
8445
+ * Describe this field.
8446
+ *
8447
+ * The description is shown next to the field's label in the Val editor, so
8448
+ * it is where you say what an editor needs to know but the field name cannot
8449
+ * carry. It also travels in the serialized schema, which is what the AI
8450
+ * assistant and the MCP tools read.
8451
+ *
8452
+ * Pass `null` to clear a description set earlier.
8453
+ *
8454
+ * @example
8455
+ * const schema = s.date().describe("First day the campaign is live");
8456
+ * export default c.define("/example.val.ts", schema, "2025-06-01");
8457
+ */
7210
8458
  _inherits(DateSchema, _Schema);
7211
8459
  return _createClass(DateSchema, [{
7212
8460
  key: "describe",
7213
8461
  value: function describe(description) {
7214
8462
  return new DateSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
7215
8463
  }
8464
+
8465
+ /**
8466
+ * Add a custom validation rule to this field.
8467
+ *
8468
+ * The function is called with the field's value and returns `false` when the
8469
+ * value is fine, or a STRING with the message to show when it is not. Call it
8470
+ * more than once to add more rules — they all run, and every message is
8471
+ * reported.
8472
+ *
8473
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
8474
+ * when the value is fine, and `true` is not one of the two answers.
8475
+ *
8476
+ * Validation runs in the Studio as you type, in `npx val validate` and
8477
+ * before a publish.
8478
+ *
8479
+ * @example
8480
+ * const schema = s.date().validate((val) =>
8481
+ * val >= "2025-01-01" ? false : "Must be in 2025 or later",
8482
+ * );
8483
+ * export default c.define("/example.val.ts", schema, "2025-06-01");
8484
+ */
7216
8485
  }, {
7217
8486
  key: "validate",
7218
8487
  value: function validate(validationFunction) {
@@ -7300,6 +8569,16 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
7300
8569
  data: src
7301
8570
  };
7302
8571
  }
8572
+
8573
+ /**
8574
+ * Validate that the date is this date or after (inclusive).
8575
+ *
8576
+ * Written as `YYYY-MM-DD`, the same shape the value has.
8577
+ *
8578
+ * @example
8579
+ * const schema = s.date().from("2025-01-01");
8580
+ * export default c.define("/example.val.ts", schema, "2025-06-01");
8581
+ */
7303
8582
  }, {
7304
8583
  key: "from",
7305
8584
  value: function from(_from) {
@@ -7307,6 +8586,16 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
7307
8586
  from: _from
7308
8587
  }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7309
8588
  }
8589
+
8590
+ /**
8591
+ * Validate that the date is this date or before (inclusive).
8592
+ *
8593
+ * Written as `YYYY-MM-DD`, the same shape the value has.
8594
+ *
8595
+ * @example
8596
+ * const schema = s.date().from("2025-01-01").to("2025-12-31");
8597
+ * export default c.define("/example.val.ts", schema, "2025-06-01");
8598
+ */
7310
8599
  }, {
7311
8600
  key: "to",
7312
8601
  value: function to(_to) {
@@ -7345,6 +8634,10 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
7345
8634
  * instead of a preview row that navigates to it.
7346
8635
  *
7347
8636
  * Static configuration, not a callback — see `render.ts`.
8637
+ *
8638
+ * @example
8639
+ * const schema = s.array(s.date().render({ as: "inline" }));
8640
+ * export default c.define("/example.val.ts", schema, ["2025-06-01"]);
7348
8641
  */
7349
8642
  }, {
7350
8643
  key: "render",
@@ -7356,6 +8649,12 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
7356
8649
  * How this VALUE is shown where a preview of it is needed — a row in a
7357
8650
  * sortable list, a reference dropdown, a search hit. Never how the field
7358
8651
  * itself is edited (that is `render`). See `preview.ts`.
8652
+ *
8653
+ * @example
8654
+ * const schema = s.array(
8655
+ * s.date().preview(({ val }) => ({ title: val })),
8656
+ * );
8657
+ * export default c.define("/example.val.ts", schema, ["2025-06-01"]);
7359
8658
  */
7360
8659
  }, {
7361
8660
  key: "preview",
@@ -7426,12 +8725,56 @@ var DateTimeSchema = /*#__PURE__*/function (_Schema) {
7426
8725
  _this.previewInput = previewInput;
7427
8726
  return _this;
7428
8727
  }
8728
+
8729
+ /**
8730
+ * Describe this field.
8731
+ *
8732
+ * The description is shown next to the field's label in the Val editor, so
8733
+ * it is where you say what an editor needs to know but the field name cannot
8734
+ * carry. It also travels in the serialized schema, which is what the AI
8735
+ * assistant and the MCP tools read.
8736
+ *
8737
+ * Pass `null` to clear a description set earlier.
8738
+ *
8739
+ * @example
8740
+ * const schema = s.datetime().describe("When the article goes live");
8741
+ * export default c.define(
8742
+ * "/example.val.ts",
8743
+ * schema,
8744
+ * "2025-06-01T09:00:00.000Z",
8745
+ * );
8746
+ */
7429
8747
  _inherits(DateTimeSchema, _Schema);
7430
8748
  return _createClass(DateTimeSchema, [{
7431
8749
  key: "describe",
7432
8750
  value: function describe(description) {
7433
8751
  return new DateTimeSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
7434
8752
  }
8753
+
8754
+ /**
8755
+ * Add a custom validation rule to this field.
8756
+ *
8757
+ * The function is called with the field's value and returns `false` when the
8758
+ * value is fine, or a STRING with the message to show when it is not. Call it
8759
+ * more than once to add more rules — they all run, and every message is
8760
+ * reported.
8761
+ *
8762
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
8763
+ * when the value is fine, and `true` is not one of the two answers.
8764
+ *
8765
+ * Validation runs in the Studio as you type, in `npx val validate` and
8766
+ * before a publish.
8767
+ *
8768
+ * @example
8769
+ * const schema = s.datetime().validate((val) =>
8770
+ * val.endsWith("Z") ? false : "Must be in UTC",
8771
+ * );
8772
+ * export default c.define(
8773
+ * "/example.val.ts",
8774
+ * schema,
8775
+ * "2025-06-01T09:00:00.000Z",
8776
+ * );
8777
+ */
7435
8778
  }, {
7436
8779
  key: "validate",
7437
8780
  value: function validate(validationFunction) {
@@ -7551,6 +8894,20 @@ var DateTimeSchema = /*#__PURE__*/function (_Schema) {
7551
8894
  data: src
7552
8895
  };
7553
8896
  }
8897
+
8898
+ /**
8899
+ * Validate that the datetime is this datetime or after (inclusive).
8900
+ *
8901
+ * Any ISO 8601 datetime string `Date.parse` understands.
8902
+ *
8903
+ * @example
8904
+ * const schema = s.datetime().from("2025-01-01T00:00:00Z");
8905
+ * export default c.define(
8906
+ * "/example.val.ts",
8907
+ * schema,
8908
+ * "2025-06-01T09:00:00.000Z",
8909
+ * );
8910
+ */
7554
8911
  }, {
7555
8912
  key: "from",
7556
8913
  value: function from(_from) {
@@ -7558,6 +8915,23 @@ var DateTimeSchema = /*#__PURE__*/function (_Schema) {
7558
8915
  from: _from
7559
8916
  }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput);
7560
8917
  }
8918
+
8919
+ /**
8920
+ * Validate that the datetime is this datetime or before (inclusive).
8921
+ *
8922
+ * Any ISO 8601 datetime string `Date.parse` understands.
8923
+ *
8924
+ * @example
8925
+ * const schema = s
8926
+ * .datetime()
8927
+ * .from("2025-01-01T00:00:00Z")
8928
+ * .to("2025-12-31T23:59:59Z");
8929
+ * export default c.define(
8930
+ * "/example.val.ts",
8931
+ * schema,
8932
+ * "2025-06-01T09:00:00.000Z",
8933
+ * );
8934
+ */
7561
8935
  }, {
7562
8936
  key: "to",
7563
8937
  value: function to(_to) {
@@ -7596,6 +8970,12 @@ var DateTimeSchema = /*#__PURE__*/function (_Schema) {
7596
8970
  * instead of a preview row that navigates to it.
7597
8971
  *
7598
8972
  * Static configuration, not a callback — see `render.ts`.
8973
+ *
8974
+ * @example
8975
+ * const schema = s.array(s.datetime().render({ as: "inline" }));
8976
+ * export default c.define("/example.val.ts", schema, [
8977
+ * "2025-06-01T09:00:00.000Z",
8978
+ * ]);
7599
8979
  */
7600
8980
  }, {
7601
8981
  key: "render",
@@ -7607,6 +8987,14 @@ var DateTimeSchema = /*#__PURE__*/function (_Schema) {
7607
8987
  * How this VALUE is shown where a preview of it is needed — a row in a
7608
8988
  * sortable list, a reference dropdown, a search hit. Never how the field
7609
8989
  * itself is edited (that is `render`). See `preview.ts`.
8990
+ *
8991
+ * @example
8992
+ * const schema = s.array(
8993
+ * s.datetime().preview(({ val }) => ({ title: val })),
8994
+ * );
8995
+ * export default c.define("/example.val.ts", schema, [
8996
+ * "2025-06-01T09:00:00.000Z",
8997
+ * ]);
7610
8998
  */
7611
8999
  }, {
7612
9000
  key: "preview",
@@ -7691,12 +9079,59 @@ var CodeSchema = /*#__PURE__*/function (_Schema) {
7691
9079
  _this.previewInput = previewInput;
7692
9080
  return _this;
7693
9081
  }
9082
+
9083
+ /**
9084
+ * Describe this field.
9085
+ *
9086
+ * The description is shown next to the field's label in the Val editor, so
9087
+ * it is where you say what an editor needs to know but the field name cannot
9088
+ * carry. It also travels in the serialized schema, which is what the AI
9089
+ * assistant and the MCP tools read.
9090
+ *
9091
+ * Pass `null` to clear a description set earlier.
9092
+ *
9093
+ * @example
9094
+ * const schema = s
9095
+ * .code({ language: "css" })
9096
+ * .describe("Injected into the page head — keep it small");
9097
+ * export default c.define(
9098
+ * "/example.val.ts",
9099
+ * schema,
9100
+ * ".hero { color: red; }",
9101
+ * );
9102
+ */
7694
9103
  _inherits(CodeSchema, _Schema);
7695
9104
  return _createClass(CodeSchema, [{
7696
9105
  key: "describe",
7697
9106
  value: function describe(description) {
7698
9107
  return new CodeSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
7699
9108
  }
9109
+
9110
+ /**
9111
+ * Add a custom validation rule to this field.
9112
+ *
9113
+ * The function is called with the field's value and returns `false` when the
9114
+ * value is fine, or a STRING with the message to show when it is not. Call it
9115
+ * more than once to add more rules — they all run, and every message is
9116
+ * reported.
9117
+ *
9118
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
9119
+ * when the value is fine, and `true` is not one of the two answers.
9120
+ *
9121
+ * Validation runs in the Studio as you type, in `npx val validate` and
9122
+ * before a publish.
9123
+ *
9124
+ * @example
9125
+ * const schema = s.code({ language: "json" }).validate((val) => {
9126
+ * try {
9127
+ * JSON.parse(val);
9128
+ * return false;
9129
+ * } catch {
9130
+ * return "Must be valid JSON";
9131
+ * }
9132
+ * });
9133
+ * export default c.define("/example.val.ts", schema, '{ "a": 1 }');
9134
+ */
7700
9135
  }, {
7701
9136
  key: "validate",
7702
9137
  value: function validate(validationFunction) {
@@ -7776,6 +9211,14 @@ var CodeSchema = /*#__PURE__*/function (_Schema) {
7776
9211
  * instead of a preview row that navigates to it.
7777
9212
  *
7778
9213
  * Static configuration, not a callback — see `render.ts`.
9214
+ *
9215
+ * @example
9216
+ * const schema = s.array(
9217
+ * s.code({ language: "css" }).render({ as: "inline" }),
9218
+ * );
9219
+ * export default c.define("/example.val.ts", schema, [
9220
+ * ".hero { color: red; }",
9221
+ * ]);
7779
9222
  */
7780
9223
  }, {
7781
9224
  key: "render",
@@ -7787,6 +9230,16 @@ var CodeSchema = /*#__PURE__*/function (_Schema) {
7787
9230
  * How this VALUE is shown where a preview of it is needed — a row in a
7788
9231
  * sortable list, a reference dropdown, a search hit. Never how the field
7789
9232
  * itself is edited (that is `render`). See `preview.ts`.
9233
+ *
9234
+ * @example
9235
+ * const schema = s.array(
9236
+ * s.code({ language: "css" }).preview(({ val }) => ({
9237
+ * title: val.split("\n")[0],
9238
+ * })),
9239
+ * );
9240
+ * export default c.define("/example.val.ts", schema, [
9241
+ * ".hero { color: red; }",
9242
+ * ]);
7790
9243
  */
7791
9244
  }, {
7792
9245
  key: "preview",
@@ -8337,12 +9790,54 @@ var ColorSchema = /*#__PURE__*/function (_Schema) {
8337
9790
  _this.previewInput = previewInput;
8338
9791
  return _this;
8339
9792
  }
9793
+
9794
+ /**
9795
+ * Describe this field.
9796
+ *
9797
+ * The description is shown next to the field's label in the Val editor, so
9798
+ * it is where you say what an editor needs to know but the field name cannot
9799
+ * carry. It also travels in the serialized schema, which is what the AI
9800
+ * assistant and the MCP tools read.
9801
+ *
9802
+ * Pass `null` to clear a description set earlier.
9803
+ *
9804
+ * @example
9805
+ * const schema = s.color().describe("Background of the hero section");
9806
+ * export default c.define(
9807
+ * "/example.val.ts",
9808
+ * schema,
9809
+ * "hsl(217.22 91.22% 59.8%)",
9810
+ * );
9811
+ */
8340
9812
  _inherits(ColorSchema, _Schema);
8341
9813
  return _createClass(ColorSchema, [{
8342
9814
  key: "describe",
8343
9815
  value: function describe(description) {
8344
9816
  return new ColorSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput);
8345
9817
  }
9818
+
9819
+ /**
9820
+ * Add a custom validation rule to this field.
9821
+ *
9822
+ * The function is called with the field's value and returns `false` when the
9823
+ * value is fine, or a STRING with the message to show when it is not. Call it
9824
+ * more than once to add more rules — they all run, and every message is
9825
+ * reported.
9826
+ *
9827
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
9828
+ * when the value is fine, and `true` is not one of the two answers.
9829
+ *
9830
+ * Validation runs in the Studio as you type, in `npx val validate` and
9831
+ * before a publish.
9832
+ *
9833
+ * @example
9834
+ * const schema = s
9835
+ * .color({ format: "hex" })
9836
+ * .validate((val) =>
9837
+ * val === val.toLowerCase() ? false : "Write hex in lower case",
9838
+ * );
9839
+ * export default c.define("/example.val.ts", schema, "#3b82f6");
9840
+ */
8346
9841
  }, {
8347
9842
  key: "validate",
8348
9843
  value: function validate(validationFunction) {
@@ -8464,6 +9959,12 @@ var ColorSchema = /*#__PURE__*/function (_Schema) {
8464
9959
  * instead of a preview row that navigates to it.
8465
9960
  *
8466
9961
  * Static configuration, not a callback — see `render.ts`.
9962
+ *
9963
+ * @example
9964
+ * const schema = s.array(s.color().render({ as: "inline" }));
9965
+ * export default c.define("/example.val.ts", schema, [
9966
+ * "hsl(217.22 91.22% 59.8%)",
9967
+ * ]);
8467
9968
  */
8468
9969
  }, {
8469
9970
  key: "render",
@@ -8475,6 +9976,14 @@ var ColorSchema = /*#__PURE__*/function (_Schema) {
8475
9976
  * How this VALUE is shown where a preview of it is needed — a row in a
8476
9977
  * sortable list, a reference dropdown, a search hit. Never how the field
8477
9978
  * itself is edited (that is `render`). See `preview.ts`.
9979
+ *
9980
+ * @example
9981
+ * const schema = s.array(
9982
+ * s.color().preview(({ val }) => ({ title: val })),
9983
+ * );
9984
+ * export default c.define("/example.val.ts", schema, [
9985
+ * "hsl(217.22 91.22% 59.8%)",
9986
+ * ]);
8478
9987
  */
8479
9988
  }, {
8480
9989
  key: "preview",
@@ -8603,6 +10112,8 @@ function initSchema() {
8603
10112
  array: array,
8604
10113
  object: object,
8605
10114
  number: number,
10115
+ discriminatedUnion: discriminatedUnion,
10116
+ "enum": enumSchema,
8606
10117
  union: union,
8607
10118
  // oneOf,
8608
10119
  richtext: richtext,
@@ -9521,32 +11032,26 @@ function hasRemoteFileSchema(schema) {
9521
11032
  }
9522
11033
  }
9523
11034
  return false;
9524
- } else if (schema.type === "union") {
9525
- var unionStringSchema = _typeof(schema.key) === "object" && schema.key.type === "literal" ? schema : undefined;
9526
- var unionObjectSchema = typeof schema.key === "string" ? schema : undefined;
9527
- if (unionStringSchema) {
9528
- // A string union's items are literals, so there is nothing to look inside.
9529
- return false;
9530
- }
9531
- if (unionObjectSchema) {
9532
- var _iterator = result._createForOfIteratorHelper(unionObjectSchema.items),
9533
- _step;
9534
- try {
9535
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
9536
- var item = _step.value;
9537
- var _hasRemoteFile = hasRemoteFileSchema(item);
9538
- if (_hasRemoteFile) {
9539
- return true;
9540
- }
11035
+ } else if (schema.type === "discriminated-union") {
11036
+ var _iterator = result._createForOfIteratorHelper(schema.items),
11037
+ _step;
11038
+ try {
11039
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
11040
+ var item = _step.value;
11041
+ var _hasRemoteFile = hasRemoteFileSchema(item);
11042
+ if (_hasRemoteFile) {
11043
+ return true;
9541
11044
  }
9542
- } catch (err) {
9543
- _iterator.e(err);
9544
- } finally {
9545
- _iterator.f();
9546
11045
  }
11046
+ } catch (err) {
11047
+ _iterator.e(err);
11048
+ } finally {
11049
+ _iterator.f();
9547
11050
  }
9548
11051
  return false;
9549
- } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route") {
11052
+ } else if (schema.type === "boolean" ||
11053
+ // An enum's values are strings, so there is nothing to look inside.
11054
+ schema.type === "enum" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route") {
9550
11055
  return false;
9551
11056
  } else {
9552
11057
  var exhaustiveCheck = schema;
@@ -9600,27 +11105,25 @@ function hasMediaSchema(schema) {
9600
11105
  }
9601
11106
  }
9602
11107
  return false;
9603
- } else if (schema.type === "union") {
9604
- var unionObjectSchema = typeof schema.key === "string" ? schema : undefined;
9605
- if (unionObjectSchema) {
9606
- var _iterator2 = result._createForOfIteratorHelper(unionObjectSchema.items),
9607
- _step2;
9608
- try {
9609
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9610
- var item = _step2.value;
9611
- if (hasMediaSchema(item)) {
9612
- return true;
9613
- }
11108
+ } else if (schema.type === "discriminated-union") {
11109
+ var _iterator2 = result._createForOfIteratorHelper(schema.items),
11110
+ _step2;
11111
+ try {
11112
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
11113
+ var item = _step2.value;
11114
+ if (hasMediaSchema(item)) {
11115
+ return true;
9614
11116
  }
9615
- } catch (err) {
9616
- _iterator2.e(err);
9617
- } finally {
9618
- _iterator2.f();
9619
11117
  }
11118
+ } catch (err) {
11119
+ _iterator2.e(err);
11120
+ } finally {
11121
+ _iterator2.f();
9620
11122
  }
9621
- // A string union's items are literals, so there is nothing to look inside.
9622
11123
  return false;
9623
- } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" ||
11124
+ } else if (schema.type === "boolean" ||
11125
+ // An enum's values are strings, so there is nothing to look inside.
11126
+ schema.type === "enum" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" ||
9624
11127
  // A settings module holds the assistant's configuration, not content, and
9625
11128
  // nothing in it is media.
9626
11129
  schema.type === "settings") {
@@ -9910,7 +11413,7 @@ function deserializeSchema(serialized) {
9910
11413
  return schema;
9911
11414
  }
9912
11415
  function deserializeSchemaImpl(serialized) {
9913
- var _serialized$options, _serialized$options2, _serialized$render, _serialized$multiline, _serialized$render2, _serialized$render3, _serialized$render4, _serialized$render5, _serialized$render6, _serialized$render7, _serialized$accept, _serialized$directory, _serialized$remote, _serialized$jsonValue, _serialized$render9, _serialized$external, _serialized$render0, _serialized$render10, _serialized$render11, _serialized$render12, _serialized$render13, _serialized$render14, _serialized$render15;
11416
+ var _serialized$options, _serialized$options2, _serialized$render, _serialized$multiline, _serialized$render2, _serialized$render3, _serialized$render4, _serialized$render5, _serialized$render6, _serialized$render7, _serialized$render8, _serialized$accept, _serialized$directory, _serialized$remote, _serialized$jsonValue, _serialized$render0, _serialized$external, _serialized$render1, _serialized$render11, _serialized$render12, _serialized$render13, _serialized$render14, _serialized$render15, _serialized$render16;
9914
11417
  switch (serialized.type) {
9915
11418
  case "string":
9916
11419
  return new StringSchema(_objectSpread2(_objectSpread2({}, serialized.options), {}, {
@@ -9944,23 +11447,22 @@ function deserializeSchemaImpl(serialized) {
9944
11447
  return new ArraySchema(deserializeSchema(serialized.item), serialized.opt, [], false, false, serialized.description,
9945
11448
  // The preview closure cannot survive serialization; the render can.
9946
11449
  null, (_serialized$render6 = serialized.render) !== null && _serialized$render6 !== void 0 ? _serialized$render6 : null);
9947
- case "union":
9948
- return new UnionSchema(typeof serialized.key === "string" ? serialized.key :
9949
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
9950
- deserializeSchema(serialized.key),
9951
- // TODO: we do not really need any here - right?
11450
+ case "discriminated-union":
11451
+ return new DiscriminatedUnionSchema(serialized.key,
9952
11452
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9953
11453
  serialized.items.map(deserializeSchema),
9954
11454
  // TODO: we do not really need any here - right?
9955
11455
  serialized.opt, [], false, false, serialized.description, (_serialized$render7 = serialized.render) !== null && _serialized$render7 !== void 0 ? _serialized$render7 : null);
11456
+ case "enum":
11457
+ return new EnumSchema(serialized.values, serialized.opt, [], false, false, serialized.description, (_serialized$render8 = serialized.render) !== null && _serialized$render8 !== void 0 ? _serialized$render8 : null);
9956
11458
  case "richtext":
9957
11459
  {
9958
- var _serialized$options3, _serialized$options4, _serialized$options5, _serialized$options6, _serialized$render8;
11460
+ var _serialized$options3, _serialized$options4, _serialized$options5, _serialized$options6, _serialized$render9;
9959
11461
  var deserializedOptions = _objectSpread2(_objectSpread2({}, serialized.options || {}), {}, {
9960
11462
  a: _typeof((_serialized$options3 = serialized.options) === null || _serialized$options3 === void 0 ? void 0 : _serialized$options3.a) === "object" ? deserializeSchema(serialized.options.a) : (_serialized$options4 = serialized.options) === null || _serialized$options4 === void 0 ? void 0 : _serialized$options4.a,
9961
11463
  img: _typeof((_serialized$options5 = serialized.options) === null || _serialized$options5 === void 0 ? void 0 : _serialized$options5.img) === "object" ? deserializeSchema(serialized.options.img) : (_serialized$options6 = serialized.options) === null || _serialized$options6 === void 0 ? void 0 : _serialized$options6.img
9962
11464
  });
9963
- return new RichTextSchema(deserializedOptions, serialized.opt, [], false, false, serialized.description, (_serialized$render8 = serialized.render) !== null && _serialized$render8 !== void 0 ? _serialized$render8 : null);
11465
+ return new RichTextSchema(deserializedOptions, serialized.opt, [], false, false, serialized.description, (_serialized$render9 = serialized.render) !== null && _serialized$render9 !== void 0 ? _serialized$render9 : null);
9964
11466
  }
9965
11467
  case "record":
9966
11468
  return new RecordSchema(deserializeSchema(serialized.item), serialized.opt, [], null, serialized.key ? deserializeSchema(serialized.key) : null, serialized.mediaType ? {
@@ -9971,33 +11473,33 @@ function deserializeSchemaImpl(serialized) {
9971
11473
  altSchema: serialized.alt ? deserializeSchema(serialized.alt) : undefined
9972
11474
  } : undefined, false, false, serialized.description, (_serialized$jsonValue = serialized.jsonValues) !== null && _serialized$jsonValue !== void 0 ? _serialized$jsonValue : false,
9973
11475
  // The preview closure cannot survive serialization; the render can.
9974
- null, (_serialized$render9 = serialized.render) !== null && _serialized$render9 !== void 0 ? _serialized$render9 : null, // The external label is static data, so it survives serialization and
11476
+ null, (_serialized$render0 = serialized.render) !== null && _serialized$render0 !== void 0 ? _serialized$render0 : null, // The external label is static data, so it survives serialization and
9975
11477
  // must be carried back: a deserialized schema that forgot it would walk
9976
11478
  // an external record as if its entries were local.
9977
11479
  (_serialized$external = serialized.external) !== null && _serialized$external !== void 0 ? _serialized$external : null);
9978
11480
  case "keyOf":
9979
- return new KeyOfSchema(serialized.schema, serialized.path, serialized.opt, [], false, false, serialized.description, (_serialized$render0 = serialized.render) !== null && _serialized$render0 !== void 0 ? _serialized$render0 : null);
11481
+ return new KeyOfSchema(serialized.schema, serialized.path, serialized.opt, [], false, false, serialized.description, (_serialized$render1 = serialized.render) !== null && _serialized$render1 !== void 0 ? _serialized$render1 : null);
9980
11482
  case "route":
9981
11483
  {
9982
- var _serialized$render1;
11484
+ var _serialized$render10;
9983
11485
  var routeOptions = serialized.options ? {
9984
11486
  include: serialized.options.include ? new RegExp(serialized.options.include.source, serialized.options.include.flags) : undefined,
9985
11487
  exclude: serialized.options.exclude ? new RegExp(serialized.options.exclude.source, serialized.options.exclude.flags) : undefined
9986
11488
  } : undefined;
9987
- return new RouteSchema(routeOptions, serialized.opt, [], false, false, serialized.description, (_serialized$render1 = serialized.render) !== null && _serialized$render1 !== void 0 ? _serialized$render1 : null);
11489
+ return new RouteSchema(routeOptions, serialized.opt, [], false, false, serialized.description, (_serialized$render10 = serialized.render) !== null && _serialized$render10 !== void 0 ? _serialized$render10 : null);
9988
11490
  }
9989
11491
  case "file":
9990
- return new FileSchema(serialized.options, serialized.opt, serialized.remote, [], {}, false, false, serialized.description, (_serialized$render10 = serialized.render) !== null && _serialized$render10 !== void 0 ? _serialized$render10 : null);
11492
+ return new FileSchema(serialized.options, serialized.opt, serialized.remote, [], {}, false, false, serialized.description, (_serialized$render11 = serialized.render) !== null && _serialized$render11 !== void 0 ? _serialized$render11 : null);
9991
11493
  case "image":
9992
- return new ImageSchema(serialized.options, serialized.opt, serialized.remote, [], {}, false, false, serialized.description, (_serialized$render11 = serialized.render) !== null && _serialized$render11 !== void 0 ? _serialized$render11 : null);
11494
+ return new ImageSchema(serialized.options, serialized.opt, serialized.remote, [], {}, false, false, serialized.description, (_serialized$render12 = serialized.render) !== null && _serialized$render12 !== void 0 ? _serialized$render12 : null);
9993
11495
  case "date":
9994
- return new DateSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render12 = serialized.render) !== null && _serialized$render12 !== void 0 ? _serialized$render12 : null);
11496
+ return new DateSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render13 = serialized.render) !== null && _serialized$render13 !== void 0 ? _serialized$render13 : null);
9995
11497
  case "dateTime":
9996
- return new DateTimeSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render13 = serialized.render) !== null && _serialized$render13 !== void 0 ? _serialized$render13 : null);
11498
+ return new DateTimeSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render14 = serialized.render) !== null && _serialized$render14 !== void 0 ? _serialized$render14 : null);
9997
11499
  case "color":
9998
- return new ColorSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render14 = serialized.render) !== null && _serialized$render14 !== void 0 ? _serialized$render14 : null);
11500
+ return new ColorSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render15 = serialized.render) !== null && _serialized$render15 !== void 0 ? _serialized$render15 : null);
9999
11501
  case "code":
10000
- return new CodeSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render15 = serialized.render) !== null && _serialized$render15 !== void 0 ? _serialized$render15 : null);
11502
+ return new CodeSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render16 = serialized.render) !== null && _serialized$render16 !== void 0 ? _serialized$render16 : null);
10001
11503
  default:
10002
11504
  {
10003
11505
  var exhaustiveCheck = serialized;
@@ -10206,10 +11708,11 @@ function previewScope(paths) {
10206
11708
  * question, and a disagreement between them is a row you can edit in place but
10207
11709
  * that "add" navigates away from.
10208
11710
  *
10209
- * A tagged union counts as inline when the union itself declares it OR when
10210
- * ANY of its variants does. A page-builder list is `s.array(s.union("type",
10211
- * block, block, ...))` and the natural place to write the render is on the
10212
- * blocks, one per block type — the union is a dispatch, not something the
11711
+ * A discriminated union counts as inline when the union itself declares it OR
11712
+ * when ANY of its variants does. A page-builder list is
11713
+ * `s.array(s.discriminatedUnion("type", block, block, ...))` and the natural
11714
+ * place to write the render is on the blocks, one per block type — the union
11715
+ * is a dispatch, not something the
10213
11716
  * author thinks of as the field. `some` rather than `every` because the row
10214
11717
  * draws the union's own editor (the tag selector plus the matched variant's
10215
11718
  * fields), which handles every variant either way: with `every`, adding one
@@ -10226,7 +11729,7 @@ function isInlineRender(schema) {
10226
11729
  if (((_schema$render = schema.render) === null || _schema$render === void 0 ? void 0 : _schema$render.as) === "inline") {
10227
11730
  return true;
10228
11731
  }
10229
- if (schema.type === "union") {
11732
+ if (schema.type === "discriminated-union") {
10230
11733
  return schema.items.some(function (item) {
10231
11734
  var _item$render;
10232
11735
  return ((_item$render = item.render) === null || _item$render === void 0 ? void 0 : _item$render.as) === "inline";
@@ -10663,7 +12166,7 @@ function isPageRouter(routerId) {
10663
12166
 
10664
12167
  var corePackageJson = {
10665
12168
  name: "@valbuild/core",
10666
- version: "0.125.0",
12169
+ version: "0.126.0",
10667
12170
  "private": false,
10668
12171
  description: "Val - supercharged hard-coded content",
10669
12172
  repository: {
@@ -10847,6 +12350,8 @@ exports.DEFAULT_CONTENT_HOST = DEFAULT_CONTENT_HOST;
10847
12350
  exports.DEFAULT_VAL_REMOTE_HOST = DEFAULT_VAL_REMOTE_HOST;
10848
12351
  exports.DateSchema = DateSchema;
10849
12352
  exports.DateTimeSchema = DateTimeSchema;
12353
+ exports.DiscriminatedUnionSchema = DiscriminatedUnionSchema;
12354
+ exports.EnumSchema = EnumSchema;
10850
12355
  exports.FATAL_ERROR_TYPES = FATAL_ERROR_TYPES;
10851
12356
  exports.FileSchema = FileSchema;
10852
12357
  exports.GenericSelector = GenericSelector;
@@ -10865,7 +12370,6 @@ exports.SETTINGS_MODULE_CONVENTION = SETTINGS_MODULE_CONVENTION;
10865
12370
  exports.Schema = Schema;
10866
12371
  exports.SettingsSchema = SettingsSchema;
10867
12372
  exports.StringSchema = StringSchema;
10868
- exports.UnionSchema = UnionSchema;
10869
12373
  exports.VAL_EXTENSION = VAL_EXTENSION;
10870
12374
  exports._classCallCheck = _classCallCheck;
10871
12375
  exports._createClass = _createClass;