@valbuild/next 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,127 @@
1
1
  # @valbuild/next
2
2
 
3
+ ## 0.126.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#652](https://github.com/valbuild/val/pull/652) [`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a) Thanks [@freekh](https://github.com/freekh)! - `s.union` is now `s.discriminatedUnion` and `s.enum`.
8
+
9
+ `s.union` did two unrelated jobs and worked out which one you meant from its
10
+ first argument: a string key meant a tagged union of objects, literal schemas
11
+ meant a set of allowed strings. Those are now two schemas with two names.
12
+
13
+ ```ts
14
+ // A fixed set of strings — presents as a dropdown
15
+ s.enum("primary", "secondary", "ghost"); // Schema<"primary" | "secondary" | "ghost">
16
+
17
+ // One of several object shapes, told apart by a tag field
18
+ s.discriminatedUnion(
19
+ "type",
20
+ s.object({ type: s.literal("hero"), heading: s.string() }),
21
+ s.object({ type: s.literal("quote"), text: s.string() }),
22
+ );
23
+ ```
24
+
25
+ `s.enum` takes the strings directly, so the `s.literal(...)` wrapper is gone.
26
+
27
+ **`s.union` still works** — it is deprecated, and it builds exactly the schema
28
+ above, so nothing has to change today:
29
+
30
+ ```ts
31
+ s.union(s.literal("one"), s.literal("two")); // → s.enum("one", "two")
32
+ s.union("type", pageA, pageB); // → s.discriminatedUnion("type", pageA, pageB)
33
+ ```
34
+
35
+ The two are different kinds of node, and that is the reason for the split. A
36
+ discriminated union is a container: the selected variant's fields are the fields
37
+ being edited, and everything that walks a schema descends through it. An enum is
38
+ a leaf — a string with a closed domain — so nothing recurses into it. Told apart
39
+ only by the shape of `key`, every consumer had to re-derive which one it was
40
+ holding; each now has its own serialized type (`"discriminated-union"` and
41
+ `"enum"`) and Val Studio has a field per kind rather than one field that
42
+ branches.
43
+
44
+ Two behaviour changes fall out of the split, both of them fixes:
45
+
46
+ - A value that is not a string at all now fails an enum's validation with a
47
+ type error. `s.union` of literals only ever checked the value against its
48
+ literals when the value WAS a string, so a number or an object where an enum
49
+ was declared validated clean.
50
+ - An enum field now shows its validation errors in Val Studio where the field
51
+ is opened on its own — the module editor and the canvas's fields column — and
52
+ gets the compact error layout inside an inline list row. It is a leaf now, so
53
+ it goes through the same error rendering as every other leaf field; the string
54
+ union bypassed it and showed nothing in those places.
55
+
56
+ Several latent crashes in the old `s.union` are fixed on the way past, all of
57
+ them cases where it threw a `TypeError` instead of reporting:
58
+
59
+ - A required discriminated union holding `null` now reports a type error rather
60
+ than throwing, and resolving a path underneath a nullable one that is `null`
61
+ gives the error the API promises instead of a crash.
62
+ - `s.literal("")` is a legal discriminator tag, and `s.enum("")` a legal value.
63
+ Both used to be treated as absent by a truthiness check — in path resolution,
64
+ in stega encoding, and in the message that lists a union's valid tags. The
65
+ editor's dropdowns handle them too: an empty value is reserved by the select
66
+ component and had to be mapped around.
67
+ - A variant that omits the discriminator entirely is now reported as the schema
68
+ error it is, instead of throwing while the check looked for it.
69
+ - An enum's value is now indexed for search, like every other string leaf. The
70
+ old string union was never indexed at all, so searching for one of its values
71
+ could not find the field.
72
+ - A nullable discriminated union set to `null` no longer renders a spinner that
73
+ never resolves.
74
+
75
+ `s.discriminatedUnion` also requires at least one variant, as `s.enum` requires
76
+ at least one value: a union with nothing to select is not a thing to write, and
77
+ everything downstream reads the first variant where it needs any.
78
+
79
+ If you read serialized schemas yourself, that is the breaking part: `type` is no
80
+ longer `"union"`, an enum carries `values: string[]` instead of a `key` plus
81
+ `items` of literal schemas, and `UnionSchema` is no longer a class.
82
+ `SerializedUnionSchema`, `SerializedStringUnionSchema`,
83
+ `SerializedObjectUnionSchema` and `UnionSchema` remain as deprecated type
84
+ aliases.
85
+
86
+ - [#661](https://github.com/valbuild/val/pull/661) [`171208a`](https://github.com/valbuild/val/commit/171208a20177e68ed5a8b1a6fdaabfe893a6aa5f) Thanks [@freekh](https://github.com/freekh)! - Every schema method now has a worked `@example` in its JSDoc, so hovering it in
87
+ your editor shows what to write.
88
+
89
+ That covers the whole builder surface — `.describe()`, `.validate()`,
90
+ `.nullable()`, `.readonly()`, `.hidden()`, `.preview()`, `.render()`, and the
91
+ per-type methods such as `.minLength()`, `.regexp()`, `.raw()`, `.multiline()`,
92
+ `.from()` / `.to()`, `.remote()`, `.jsonValues()` and `.external()` — as well as
93
+ `c.define()`, `c.json()`, `c.external()` and the `val` helpers (`val.attrs`,
94
+ `val.raw`, `val.unstable_getPath` and friends).
95
+
96
+ One of the examples corrected a real trap: a custom validator returns
97
+ `false | string`, so the natural-looking
98
+
99
+ ```ts
100
+ s.string().validate((val) => val.trim() === val || "No surrounding spaces");
101
+ ```
102
+
103
+ does not type check — `||` yields `true`, and `true` is not one of the two
104
+ answers. Write it as a ternary instead:
105
+
106
+ ```ts
107
+ s.string().validate((val) =>
108
+ val.trim() === val ? false : "No surrounding spaces",
109
+ );
110
+ ```
111
+
112
+ The examples are checked in CI, not just written: one test asks the TypeScript
113
+ checker for the doc each method actually resolves to and fails if it has no
114
+ `@example`, and another compiles every example it finds.
115
+
116
+ - Updated dependencies [[`719ad6b`](https://github.com/valbuild/val/commit/719ad6b607bcf136d0dbde9e90bf4b8a843561a4), [`9830277`](https://github.com/valbuild/val/commit/9830277e9aaca8da3030f629c2656ec58da47e45), [`64bfd0a`](https://github.com/valbuild/val/commit/64bfd0a6c85832ea5169b53e47087f22e193df36), [`7782979`](https://github.com/valbuild/val/commit/7782979e9b52f2015a6e72dc981e630d4f8c78e2), [`5bfd630`](https://github.com/valbuild/val/commit/5bfd630b63dee2189e238f20fe72ecc5537160f7), [`ccbcda6`](https://github.com/valbuild/val/commit/ccbcda60b3e3c465071229ae1ba28ac735483e63), [`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a), [`5c18c99`](https://github.com/valbuild/val/commit/5c18c99ecc84651f82123481fc042063db953833), [`755e1a3`](https://github.com/valbuild/val/commit/755e1a3953775cb8d2c2dce87d6810d3dc329640), [`c6b1ec8`](https://github.com/valbuild/val/commit/c6b1ec84f1883750a4cfe5f70470b177621e971f), [`656f680`](https://github.com/valbuild/val/commit/656f680043c640f678625a64e690389ab23a0a69), [`610a041`](https://github.com/valbuild/val/commit/610a0414b120b521f38a2eb1182b3778bf778b2b), [`171208a`](https://github.com/valbuild/val/commit/171208a20177e68ed5a8b1a6fdaabfe893a6aa5f)]:
117
+ - @valbuild/ui@0.126.0
118
+ - @valbuild/shared@0.126.0
119
+ - @valbuild/server@0.126.0
120
+ - @valbuild/core@0.126.0
121
+ - @valbuild/react@0.126.0
122
+ - @valbuild/language-server@0.126.0
123
+ - @valbuild/mcp@0.126.0
124
+
3
125
  ## 0.125.0
4
126
 
5
127
  ### Patch Changes
package/README.md CHANGED
@@ -571,7 +571,7 @@ const sectionsSchema = s.array(
571
571
  );
572
572
  ```
573
573
 
574
- A tagged union with no preview of its own previews as the VARIANT the value
574
+ A discriminated union with no preview of its own previews as the VARIANT the value
575
575
  takes, so a page-builder list previews each block by its own block type.
576
576
 
577
577
  Your function is run on demand, for the rows actually on screen, so it is fine
@@ -909,20 +909,16 @@ const image = useVal(imageVal);
909
909
  return <img src={image.url} />;
910
910
  ```
911
911
 
912
- ## Union
912
+ ## Discriminated Union
913
913
 
914
- The union schema can be used to create either "tagged unions" or a union of string literals.
914
+ A discriminated union is a union of objects which all have the same field (of the same type). This field determines (or "discriminates") which of the union's types a value is.
915
915
 
916
- ### Union Schema tagged unions
917
-
918
- A tagged union is a union of objects which all have the same field (of the same type). This field can be used to determine (or "discriminate") the exact type of one of the types of the union.
919
-
920
- It is useful when editors should be able to chose from a set of objects that are different.
916
+ It is useful when editors should be able to choose from a set of objects that are different.
921
917
 
922
918
  Example: let us say you have a page that can be one of the following: blog (page) or product (page). In this case your schema could look like this:
923
919
 
924
920
  ```ts
925
- s.union(
921
+ s.discriminatedUnion(
926
922
  "type", // the key of the "discriminator"
927
923
  s.object({
928
924
  type: s.literal("blogPage"), // <- each type must have a UNIQUE value
@@ -937,16 +933,23 @@ s.union(
937
933
  ); // <- Schema<{ type: "blogPage", author: string } | { type: "productPage", sku: number }>
938
934
  ```
939
935
 
940
- ## Union Schema: union of string literals
936
+ ## Enum
937
+
938
+ Use `s.enum` for a fixed set of strings. It gives you a type-safe way to describe the valid values an editor can choose from, and it presents as a dropdown in Val Studio.
939
+
940
+ ```ts
941
+ s.enum("one", "two"); // <- Schema<"one" | "two">
942
+ ```
943
+
944
+ ### `s.union` is deprecated
941
945
 
942
- You can also use a union to create a union of string literals. This is useful if you want a type-safe way to describe a set of valid strings that can be chosen by an editor.
946
+ `s.union` did both of these jobs, deciding which one you meant from its first
947
+ argument. It still works, and produces exactly the schemas above, but name the
948
+ one you mean instead:
943
949
 
944
950
  ```ts
945
- s.union(
946
- s.literal("one"),
947
- s.literal("two"),
948
- //...
949
- ); // <- Schema<"one" | "two">
951
+ s.union(s.literal("one"), s.literal("two")); // -> s.enum("one", "two")
952
+ s.union("type", pageA, pageB); // -> s.discriminatedUnion("type", pageA, pageB)
950
953
  ```
951
954
 
952
955
  ## KeyOf
@@ -26,6 +26,10 @@ export declare const initVal: (config?: ValConfig) => InitVal & {
26
26
  * cache for every route it covers, for all visitors. It is NOT needed for
27
27
  * the `suspend` prop on ValProvider (which detects the cookie client-side)
28
28
  * — reserve it for advanced server-side conditionals.
29
+ *
30
+ * @example
31
+ * // In a Server Component, a Server Action or a Route Handler:
32
+ * const enabled = await isValEnabled();
29
33
  */
30
34
  isValEnabled: typeof isValEnabled;
31
35
  val: ValConstructor & {
@@ -40,10 +44,22 @@ export declare const initVal: (config?: ValConfig) => InitVal & {
40
44
  *
41
45
  * This method is primarily intended for tooling and other advanced use cases
42
46
  * outside of the actual application.
47
+ *
48
+ * @example
49
+ * import pageVal from "./page.val";
50
+ * const page = val.unstable_getUnpatchedUnencodedVal(pageVal);
43
51
  */
44
52
  unstable_getUnpatchedUnencodedVal: typeof getUnpatchedUnencodedVal;
45
53
  /**
46
54
  * Convert any object that is encoded with Val stega encoding back to the original values
55
+ *
56
+ * Use it wherever an encoded string would break something: a `key`, a
57
+ * comparison, a URL, anything sent to an API.
58
+ *
59
+ * @example
60
+ * import pageVal from "./page.val";
61
+ * const page = useVal(pageVal);
62
+ * const slug = val.raw(page.slug);
47
63
  */
48
64
  raw: typeof raw;
49
65
  /**
@@ -52,12 +68,30 @@ export declare const initVal: (config?: ValConfig) => InitVal & {
52
68
  * This is typically used to manually set the data-val-path attribute for visual editing on any element.
53
69
  *
54
70
  * @example
55
- * const page = useVal(pageVal)
56
- * <a href={page.url.href} {...val.attrs(page)}>
57
- * {page.url.label}
58
- * </a>
71
+ * import pageVal from "./page.val";
72
+ * function PageLink() {
73
+ * const page = useVal(pageVal);
74
+ * return (
75
+ * <a href={page.url.href} {...val.attrs(page)}>
76
+ * {page.url.label}
77
+ * </a>
78
+ * );
79
+ * }
59
80
  */
60
81
  attrs: typeof attrs;
82
+ /**
83
+ * The Val paths encoded into a single stega encoded string, or `undefined`
84
+ * when there are none.
85
+ *
86
+ * `val.attrs` is what an element usually wants; this is the lower-level
87
+ * read, for when you need the paths themselves. Unstable: the shape of a
88
+ * path is not part of the public API yet.
89
+ *
90
+ * @example
91
+ * import pageVal from "./page.val";
92
+ * const page = useVal(pageVal);
93
+ * const paths = val.unstable_decodeValPathsOfString(page.title);
94
+ */
61
95
  unstable_decodeValPathsOfString: typeof decodeValPathsOfString;
62
96
  };
63
97
  /**
@@ -73,6 +107,21 @@ export declare const initVal: (config?: ValConfig) => InitVal & {
73
107
  * });
74
108
  */
75
109
  nextAppRouter: ValRouter;
110
+ /**
111
+ * A router for pages that are NOT in this application: the keys of the record
112
+ * are whole URLs, not route paths of your site.
113
+ *
114
+ * It is what `s.route()` links to when the destination is somewhere else -
115
+ * a campaign site, a docs host, a social profile.
116
+ *
117
+ * @example
118
+ * const links = s.record(s.object({ title: s.string() }));
119
+ * export default c.define(
120
+ * "/content/external.val.ts",
121
+ * links.router(externalPageRouter),
122
+ * { "https://val.build": { title: "Val" } },
123
+ * );
124
+ */
76
125
  externalPageRouter: ValRouter;
77
126
  };
78
127
  export {};
@@ -12,7 +12,7 @@ var NextImage = require('next/image');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
  var ValApp = require('./ValApp-294e5b53.cjs.dev.js');
14
14
  var ValModulesClient = require('./ValModulesClient-4ecabf99.cjs.dev.js');
15
- var version = require('./version-4302babf.cjs.dev.js');
15
+ var version = require('./version-7b12f65c.cjs.dev.js');
16
16
  var createForOfIteratorHelper = require('./createForOfIteratorHelper-0445603c.cjs.dev.js');
17
17
  require('./defineProperty-f90345d7.cjs.dev.js');
18
18
  require('./unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
@@ -12,7 +12,7 @@ var NextImage = require('next/image');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
  var ValApp = require('./ValApp-ab8a96fd.cjs.prod.js');
14
14
  var ValModulesClient = require('./ValModulesClient-10c0721c.cjs.prod.js');
15
- var version = require('./version-eb9498d9.cjs.prod.js');
15
+ var version = require('./version-ed4739ee.cjs.prod.js');
16
16
  var createForOfIteratorHelper = require('./createForOfIteratorHelper-d4afcad8.cjs.prod.js');
17
17
  require('./defineProperty-8951f469.cjs.prod.js');
18
18
  require('./unsupportedIterableToArray-0d2087a2.cjs.prod.js');
@@ -11,7 +11,7 @@ import NextImage from 'next/image';
11
11
  import { jsx } from 'react/jsx-runtime';
12
12
  export { ValApp } from './ValApp-3a70afe1.esm.js';
13
13
  export { ValModulesClient, useRegisterValModules } from './ValModulesClient-0dd601a1.esm.js';
14
- import { V as VERSION } from './version-8ab79315.esm.js';
14
+ import { V as VERSION } from './version-f8b10637.esm.js';
15
15
  import { _ as _createForOfIteratorHelper } from './createForOfIteratorHelper-5758a730.esm.js';
16
16
  import './defineProperty-cca5affa.esm.js';
17
17
  import './unsupportedIterableToArray-5baabfdc.esm.js';
@@ -14,7 +14,7 @@ var packageJson = {
14
14
  "next",
15
15
  "react"
16
16
  ],
17
- version: "0.125.0",
17
+ version: "0.126.0",
18
18
  scripts: {
19
19
  typecheck: "tsc --noEmit",
20
20
  test: "jest"
@@ -14,7 +14,7 @@ var packageJson = {
14
14
  "next",
15
15
  "react"
16
16
  ],
17
- version: "0.125.0",
17
+ version: "0.126.0",
18
18
  scripts: {
19
19
  typecheck: "tsc --noEmit",
20
20
  test: "jest"
@@ -12,7 +12,7 @@ var packageJson = {
12
12
  "next",
13
13
  "react"
14
14
  ],
15
- version: "0.125.0",
15
+ version: "0.126.0",
16
16
  scripts: {
17
17
  typecheck: "tsc --noEmit",
18
18
  test: "jest"
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "next",
13
13
  "react"
14
14
  ],
15
- "version": "0.125.0",
15
+ "version": "0.126.0",
16
16
  "main": "dist/valbuild-next.cjs.js",
17
17
  "module": "dist/valbuild-next.esm.js",
18
18
  "exports": {
@@ -47,13 +47,13 @@
47
47
  "dependencies": {
48
48
  "client-only": "^0.0.1",
49
49
  "server-only": "^0.0.1",
50
- "@valbuild/core": "0.125.0",
51
- "@valbuild/mcp": "0.125.0",
52
- "@valbuild/language-server": "0.125.0",
53
- "@valbuild/react": "0.125.0",
54
- "@valbuild/server": "0.125.0",
55
- "@valbuild/shared": "0.125.0",
56
- "@valbuild/ui": "0.125.0"
50
+ "@valbuild/core": "0.126.0",
51
+ "@valbuild/language-server": "0.126.0",
52
+ "@valbuild/react": "0.126.0",
53
+ "@valbuild/mcp": "0.126.0",
54
+ "@valbuild/server": "0.126.0",
55
+ "@valbuild/shared": "0.126.0",
56
+ "@valbuild/ui": "0.126.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@testing-library/react": "^16.3.3",
@@ -11,7 +11,7 @@ var stega = require('@valbuild/react/stega');
11
11
  var core = require('@valbuild/core');
12
12
  var internal = require('@valbuild/shared/internal');
13
13
  var server = require('@valbuild/server');
14
- var version = require('../../dist/version-4302babf.cjs.dev.js');
14
+ var version = require('../../dist/version-7b12f65c.cjs.dev.js');
15
15
  require('../../dist/createForOfIteratorHelper-0445603c.cjs.dev.js');
16
16
  require('../../dist/unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
17
17
  require('../../dist/slicedToArray-44036a76.cjs.dev.js');
@@ -11,7 +11,7 @@ var stega = require('@valbuild/react/stega');
11
11
  var core = require('@valbuild/core');
12
12
  var internal = require('@valbuild/shared/internal');
13
13
  var server = require('@valbuild/server');
14
- var version = require('../../dist/version-eb9498d9.cjs.prod.js');
14
+ var version = require('../../dist/version-ed4739ee.cjs.prod.js');
15
15
  require('../../dist/createForOfIteratorHelper-d4afcad8.cjs.prod.js');
16
16
  require('../../dist/unsupportedIterableToArray-0d2087a2.cjs.prod.js');
17
17
  require('../../dist/slicedToArray-ce613de6.cjs.prod.js');
@@ -7,7 +7,7 @@ import { SET_RSC, stegaEncode, SET_AUTO_TAG_JSX_ENABLED } from '@valbuild/react/
7
7
  import { Internal } from '@valbuild/core';
8
8
  import { VAL_SESSION_COOKIE } from '@valbuild/shared/internal';
9
9
  import { createValServer } from '@valbuild/server';
10
- import { V as VERSION } from '../../dist/version-8ab79315.esm.js';
10
+ import { V as VERSION } from '../../dist/version-f8b10637.esm.js';
11
11
  import '../../dist/createForOfIteratorHelper-5758a730.esm.js';
12
12
  import '../../dist/unsupportedIterableToArray-5baabfdc.esm.js';
13
13
  import '../../dist/slicedToArray-aa291011.esm.js';
@@ -9,7 +9,7 @@ var objectSpread2 = require('../../dist/objectSpread2-58024783.cjs.dev.js');
9
9
  var core = require('@valbuild/core');
10
10
  var server = require('@valbuild/server');
11
11
  var server$1 = require('next/server');
12
- var version = require('../../dist/version-4302babf.cjs.dev.js');
12
+ var version = require('../../dist/version-7b12f65c.cjs.dev.js');
13
13
  var mcp = require('@valbuild/mcp');
14
14
  require('../../dist/unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
15
15
  require('../../dist/defineProperty-f90345d7.cjs.dev.js');
@@ -9,7 +9,7 @@ var objectSpread2 = require('../../dist/objectSpread2-13f847a9.cjs.prod.js');
9
9
  var core = require('@valbuild/core');
10
10
  var server = require('@valbuild/server');
11
11
  var server$1 = require('next/server');
12
- var version = require('../../dist/version-eb9498d9.cjs.prod.js');
12
+ var version = require('../../dist/version-ed4739ee.cjs.prod.js');
13
13
  var mcp = require('@valbuild/mcp');
14
14
  require('../../dist/unsupportedIterableToArray-0d2087a2.cjs.prod.js');
15
15
  require('../../dist/defineProperty-8951f469.cjs.prod.js');
@@ -5,7 +5,7 @@ import { _ as _objectSpread2 } from '../../dist/objectSpread2-60d1bd93.esm.js';
5
5
  import { Internal } from '@valbuild/core';
6
6
  import { createValApiRouter, createValServer } from '@valbuild/server';
7
7
  import { NextResponse } from 'next/server';
8
- import { V as VERSION } from '../../dist/version-8ab79315.esm.js';
8
+ import { V as VERSION } from '../../dist/version-f8b10637.esm.js';
9
9
  import { initValMcp as initValMcp$1 } from '@valbuild/mcp';
10
10
  import '../../dist/unsupportedIterableToArray-5baabfdc.esm.js';
11
11
  import '../../dist/defineProperty-cca5affa.esm.js';