@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.
- package/CHANGELOG.md +115 -0
- package/dist/declarations/src/index.d.ts +3 -1
- package/dist/declarations/src/initSchema.d.ts +46 -15
- package/dist/declarations/src/initVal.d.ts +108 -0
- package/dist/declarations/src/render.d.ts +5 -4
- package/dist/declarations/src/schema/array.d.ts +65 -0
- package/dist/declarations/src/schema/boolean.d.ts +44 -0
- package/dist/declarations/src/schema/code.d.ts +63 -0
- package/dist/declarations/src/schema/color.d.ts +54 -0
- package/dist/declarations/src/schema/date.d.ts +64 -2
- package/dist/declarations/src/schema/datetime.d.ts +87 -2
- package/dist/declarations/src/schema/discriminatedUnion.d.ts +166 -0
- package/dist/declarations/src/schema/enum.d.ts +122 -0
- package/dist/declarations/src/schema/file.d.ts +77 -0
- package/dist/declarations/src/schema/image.d.ts +102 -0
- package/dist/declarations/src/schema/index.d.ts +42 -2
- package/dist/declarations/src/schema/keyOf.d.ts +50 -0
- package/dist/declarations/src/schema/literal.d.ts +46 -0
- package/dist/declarations/src/schema/number.d.ts +58 -0
- package/dist/declarations/src/schema/object.d.ts +63 -0
- package/dist/declarations/src/schema/record.d.ts +136 -0
- package/dist/declarations/src/schema/richtext.d.ts +78 -0
- package/dist/declarations/src/schema/route.d.ts +44 -0
- package/dist/declarations/src/schema/string.d.ts +99 -0
- package/dist/declarations/src/schema/union.d.ts +54 -91
- package/dist/{index-a3846321.cjs.prod.js → index-6db78f3c.cjs.prod.js} +1953 -449
- package/dist/{index-daa122af.cjs.dev.js → index-b41b638f.cjs.dev.js} +1953 -449
- package/dist/{index-915dd675.esm.js → index-cd25c2dc.esm.js} +1952 -449
- package/dist/valbuild-core.cjs.dev.js +3 -2
- package/dist/valbuild-core.cjs.prod.js +3 -2
- package/dist/valbuild-core.esm.js +1 -1
- package/package.json +1 -1
- package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
- package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
- package/patch/dist/valbuild-core-patch.esm.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,120 @@
|
|
|
1
1
|
# @valbuild/core
|
|
2
2
|
|
|
3
|
+
## 0.126.0
|
|
4
|
+
|
|
5
|
+
### Minor 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
|
+
### Patch Changes
|
|
87
|
+
|
|
88
|
+
- [#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
|
|
89
|
+
your editor shows what to write.
|
|
90
|
+
|
|
91
|
+
That covers the whole builder surface — `.describe()`, `.validate()`,
|
|
92
|
+
`.nullable()`, `.readonly()`, `.hidden()`, `.preview()`, `.render()`, and the
|
|
93
|
+
per-type methods such as `.minLength()`, `.regexp()`, `.raw()`, `.multiline()`,
|
|
94
|
+
`.from()` / `.to()`, `.remote()`, `.jsonValues()` and `.external()` — as well as
|
|
95
|
+
`c.define()`, `c.json()`, `c.external()` and the `val` helpers (`val.attrs`,
|
|
96
|
+
`val.raw`, `val.unstable_getPath` and friends).
|
|
97
|
+
|
|
98
|
+
One of the examples corrected a real trap: a custom validator returns
|
|
99
|
+
`false | string`, so the natural-looking
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
s.string().validate((val) => val.trim() === val || "No surrounding spaces");
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
does not type check — `||` yields `true`, and `true` is not one of the two
|
|
106
|
+
answers. Write it as a ternary instead:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
s.string().validate((val) =>
|
|
110
|
+
val.trim() === val ? false : "No surrounding spaces",
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The examples are checked in CI, not just written: one test asks the TypeScript
|
|
115
|
+
checker for the doc each method actually resolves to and fails if it has no
|
|
116
|
+
`@example`, and another compiles every example it finds.
|
|
117
|
+
|
|
3
118
|
## 0.125.0
|
|
4
119
|
|
|
5
120
|
### Minor Changes
|
|
@@ -63,7 +63,9 @@ export { type SerializedDateTimeSchema, DateTimeSchema, } from "./schema/datetim
|
|
|
63
63
|
export { type SerializedKeyOfSchema, KeyOfSchema } from "./schema/keyOf.js";
|
|
64
64
|
export { type SerializedRouteSchema, RouteSchema } from "./schema/route.js";
|
|
65
65
|
export { type SerializedRichTextSchema, RichTextSchema, } from "./schema/richtext.js";
|
|
66
|
-
export { type
|
|
66
|
+
export { type SerializedDiscriminatedUnionSchema, type DiscriminatedUnionSourceOf, DiscriminatedUnionSchema, } from "./schema/discriminatedUnion.js";
|
|
67
|
+
export { type SerializedEnumSchema, EnumSchema } from "./schema/enum.js";
|
|
68
|
+
export { type SerializedUnionSchema, type UnionSchema, type SerializedStringUnionSchema, type SerializedObjectUnionSchema, } from "./schema/union.js";
|
|
67
69
|
export { type SerializedLiteralSchema, LiteralSchema } from "./schema/literal.js";
|
|
68
70
|
export { deserializeSchema } from "./schema/deserialize.js";
|
|
69
71
|
export { type PreviewItem, type ItemPreviewInput, type RecordPreview, type ArrayPreview, type ReifiedPreview, type PreviewScope, previewScope, } from "./preview.js";
|
|
@@ -4,6 +4,8 @@ import { object } from "./schema/object.js";
|
|
|
4
4
|
import { string } from "./schema/string.js";
|
|
5
5
|
import { boolean } from "./schema/boolean.js";
|
|
6
6
|
import { union } from "./schema/union.js";
|
|
7
|
+
import { discriminatedUnion } from "./schema/discriminatedUnion.js";
|
|
8
|
+
import { enumSchema } from "./schema/enum.js";
|
|
7
9
|
import { richtext } from "./schema/richtext.js";
|
|
8
10
|
import { image } from "./schema/image.js";
|
|
9
11
|
import { literal } from "./schema/literal.js";
|
|
@@ -67,32 +69,58 @@ export type InitSchema = {
|
|
|
67
69
|
*/
|
|
68
70
|
readonly number: typeof number;
|
|
69
71
|
/**
|
|
70
|
-
* Define a
|
|
72
|
+
* Define one of several object shapes, told apart by a tag field.
|
|
71
73
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
74
|
+
* The first argument names the field that carries the tag; every object must
|
|
75
|
+
* set it to a distinct `s.literal(...)`. The editor shows a dropdown of the
|
|
76
|
+
* tags and the fields of whichever one is selected.
|
|
75
77
|
*
|
|
76
|
-
* @example
|
|
77
|
-
* const schema = s.
|
|
78
|
-
* type: s.literal("
|
|
79
|
-
* value: s.
|
|
80
|
-
*
|
|
81
|
-
* type: s.literal("test2"),
|
|
82
|
-
* value: s.string()
|
|
83
|
-
* }));
|
|
78
|
+
* @example
|
|
79
|
+
* const schema = s.discriminatedUnion("type",
|
|
80
|
+
* s.object({ type: s.literal("text"), value: s.string() }),
|
|
81
|
+
* s.object({ type: s.literal("image"), value: s.image() }),
|
|
82
|
+
* );
|
|
84
83
|
* export default c.define("/example.val.ts", schema, {
|
|
85
|
-
* type: "
|
|
84
|
+
* type: "text",
|
|
86
85
|
* value: "test"
|
|
87
86
|
* });
|
|
88
87
|
*
|
|
89
88
|
*/
|
|
89
|
+
readonly discriminatedUnion: typeof discriminatedUnion;
|
|
90
|
+
/**
|
|
91
|
+
* Define a string that must be one of a fixed set of values.
|
|
92
|
+
*
|
|
93
|
+
* The editor shows a dropdown of the values, in the order they are given.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* const schema = s.enum("primary", "secondary", "ghost");
|
|
97
|
+
* export default c.define("/example.val.ts", schema, "primary");
|
|
98
|
+
*
|
|
99
|
+
*/
|
|
100
|
+
readonly enum: typeof enumSchema;
|
|
101
|
+
/**
|
|
102
|
+
* Define a union.
|
|
103
|
+
*
|
|
104
|
+
* @deprecated Use `s.discriminatedUnion` for a tagged union of objects, or
|
|
105
|
+
* `s.enum` for one of a fixed set of strings.
|
|
106
|
+
*
|
|
107
|
+
* @example // was: a union of string literals
|
|
108
|
+
* const schema = s.enum("test", "test2");
|
|
109
|
+
*
|
|
110
|
+
* @example // was: a tagged union of objects
|
|
111
|
+
* const schema = s.discriminatedUnion("type",
|
|
112
|
+
* s.object({ type: s.literal("test"), value: s.string() }),
|
|
113
|
+
* s.object({ type: s.literal("test2"), value: s.string() }),
|
|
114
|
+
* );
|
|
115
|
+
*
|
|
116
|
+
*/
|
|
90
117
|
readonly union: typeof union;
|
|
91
118
|
/**
|
|
92
119
|
* Define a rich text.
|
|
93
120
|
*
|
|
94
121
|
* @example
|
|
95
|
-
*
|
|
122
|
+
* // Every tag and style is off until the options ask for it.
|
|
123
|
+
* const schema = s.richtext({ h1: true, bold: true });
|
|
96
124
|
* export default c.define("/example.val.ts", schema, [
|
|
97
125
|
* { tag: "h1", children: ["Title 1"] },
|
|
98
126
|
* ]);
|
|
@@ -119,6 +147,7 @@ export type InitSchema = {
|
|
|
119
147
|
*
|
|
120
148
|
* @example
|
|
121
149
|
* // Backed by a gallery: width, height and mimeType live there.
|
|
150
|
+
* import galleryVal from "./gallery.val"; // an s.images() module
|
|
122
151
|
* const schema = s.image(galleryVal);
|
|
123
152
|
* export default c.define("/example.val.ts", schema, {
|
|
124
153
|
* path: "/public/val/example.png",
|
|
@@ -346,7 +375,9 @@ export declare function initSchema(): {
|
|
|
346
375
|
max?: number;
|
|
347
376
|
min?: number;
|
|
348
377
|
}) => import("./index.js").NumberSchema<number>;
|
|
349
|
-
|
|
378
|
+
discriminatedUnion: <Key extends string, T extends import("./schema/discriminatedUnion.js").DiscriminatedUnionItems<Key>>(key: Key, ...objects: T) => import("./index.js").DiscriminatedUnionSchema<Key, T, import("./index.js").DiscriminatedUnionSourceOf<Key, T>>;
|
|
379
|
+
enum: <T extends string>(values_0: T, ...values: T[]) => import("./index.js").EnumSchema<T>;
|
|
380
|
+
union: typeof union;
|
|
350
381
|
richtext: <O extends import("./index.js").RichTextOptions>(options?: O) => import("./index.js").RichTextSchema<O, import("./index.js").RichTextSource<O>>;
|
|
351
382
|
image: typeof image;
|
|
352
383
|
literal: <T extends string>(value: T) => import("./index.js").LiteralSchema<T>;
|
|
@@ -4,11 +4,71 @@ import { getValPath as getPath } from "./val/index.js";
|
|
|
4
4
|
import { json } from "./source/json.js";
|
|
5
5
|
import { external } from "./source/external.js";
|
|
6
6
|
export type ContentConstructor = {
|
|
7
|
+
/**
|
|
8
|
+
* Define a content module: a schema, and source that matches it.
|
|
9
|
+
*
|
|
10
|
+
* The first argument is the module's own file path, starting at the root of
|
|
11
|
+
* the project. It has to match the file the call is written in - that is how
|
|
12
|
+
* the Studio, validation and patching find the module again - so a
|
|
13
|
+
* `c.define` is always the default export of one `*.val.ts` file.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const schema = s.object({ title: s.string() });
|
|
17
|
+
* export default c.define("/content/page.val.ts", schema, {
|
|
18
|
+
* title: "Hello",
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
7
21
|
define: typeof define;
|
|
22
|
+
/**
|
|
23
|
+
* Put one entry of a `.jsonValues()` record in its own `*.val.json` file,
|
|
24
|
+
* loaded lazily.
|
|
25
|
+
*
|
|
26
|
+
* What is written in the module is the `import()` thunk, not the content: the
|
|
27
|
+
* `.val.ts` stays small, and the runtime, the Studio and validation each work
|
|
28
|
+
* an entry at a time. That is what lets a record or a router hold many
|
|
29
|
+
* thousands of entries.
|
|
30
|
+
*
|
|
31
|
+
* Only for the entries of a record that said `.jsonValues()`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* import { nextAppRouter } from "../val.config";
|
|
35
|
+
* const schema = s
|
|
36
|
+
* .router(nextAppRouter, s.object({ title: s.string() }))
|
|
37
|
+
* .jsonValues();
|
|
38
|
+
* export default c.define("/app/support/[slug]/page.val.ts", schema, {
|
|
39
|
+
* "/support/faq": c.json(() => import("./content/faq.val.json")),
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
8
42
|
json: typeof json;
|
|
43
|
+
/**
|
|
44
|
+
* The source of a record whose entries live behind an adapter - a database,
|
|
45
|
+
* an HTTP API, a bucket - instead of in the module.
|
|
46
|
+
*
|
|
47
|
+
* It takes no arguments, and there is nothing to fill in: it says only that
|
|
48
|
+
* the entries are somewhere else. WHICH adapter is the schema's business
|
|
49
|
+
* (`.external("posts")`), and the binding is registered where the app is set
|
|
50
|
+
* up.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* const schema = s.record(s.object({ title: s.string() })).external("posts");
|
|
54
|
+
* export default c.define("/content/posts.val.ts", schema, c.external());
|
|
55
|
+
*/
|
|
9
56
|
external: typeof external;
|
|
10
57
|
};
|
|
11
58
|
export type ValConstructor = {
|
|
59
|
+
/**
|
|
60
|
+
* The source path of a value inside a module, or `undefined` for something
|
|
61
|
+
* that did not come from Val.
|
|
62
|
+
*
|
|
63
|
+
* A path is what every Val API addresses content by, so this is the way from
|
|
64
|
+
* a value you are holding back to the thing the Studio, a patch or a
|
|
65
|
+
* validation error names. Unstable: the shape of a path is not part of the
|
|
66
|
+
* public API yet.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* import pageVal from "./page.val";
|
|
70
|
+
* const path = val.unstable_getPath(pageVal); // "/page.val.ts"
|
|
71
|
+
*/
|
|
12
72
|
unstable_getPath: typeof getPath;
|
|
13
73
|
};
|
|
14
74
|
export type ConfigDirectory = `/public` | `/public/${string}`;
|
|
@@ -39,9 +99,57 @@ export type ValConfig = {
|
|
|
39
99
|
};
|
|
40
100
|
};
|
|
41
101
|
export type InitVal = {
|
|
102
|
+
/**
|
|
103
|
+
* The content constructor: `c.define` for a module, `c.json` and
|
|
104
|
+
* `c.external` for entries that live outside it.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* const schema = s.object({ title: s.string() });
|
|
108
|
+
* export default c.define("/content/page.val.ts", schema, {
|
|
109
|
+
* title: "Hello",
|
|
110
|
+
* });
|
|
111
|
+
*/
|
|
42
112
|
c: ContentConstructor;
|
|
113
|
+
/**
|
|
114
|
+
* Helpers for working with content your app is already holding, rather than
|
|
115
|
+
* for declaring it.
|
|
116
|
+
*
|
|
117
|
+
* `@valbuild/next` and `@valbuild/tanstack` add to this: `val.attrs` for
|
|
118
|
+
* visual editing, `val.raw` to strip the stega encoding off a string.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* import pageVal from "./page.val";
|
|
122
|
+
* const path = val.unstable_getPath(pageVal);
|
|
123
|
+
*/
|
|
43
124
|
val: ValConstructor;
|
|
125
|
+
/**
|
|
126
|
+
* The schema constructor: one method per type of content Val can hold.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* const schema = s.object({
|
|
130
|
+
* title: s.string().describe("Shown in the browser tab"),
|
|
131
|
+
* image: s.image().nullable(),
|
|
132
|
+
* });
|
|
133
|
+
* export default c.define("/content/page.val.ts", schema, {
|
|
134
|
+
* title: "Hello",
|
|
135
|
+
* image: null,
|
|
136
|
+
* });
|
|
137
|
+
*/
|
|
44
138
|
s: InitSchema;
|
|
139
|
+
/**
|
|
140
|
+
* The config this `initVal` was called with, as the rest of Val reads it.
|
|
141
|
+
*
|
|
142
|
+
* Exported from `val.config.ts` so that the Val API route and the Studio get
|
|
143
|
+
* the same object the content modules were built with.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* import { initVal } from "@valbuild/core";
|
|
147
|
+
* const { s, c, val, config } = initVal({
|
|
148
|
+
* project: "myorg/myproject",
|
|
149
|
+
* files: { directory: "/public/val" },
|
|
150
|
+
* });
|
|
151
|
+
* export { s, c, val, config };
|
|
152
|
+
*/
|
|
45
153
|
config: ValConfig;
|
|
46
154
|
};
|
|
47
155
|
export declare const initVal: (config?: ValConfig) => InitVal;
|
|
@@ -59,10 +59,11 @@ export type FieldRender = InlineRender;
|
|
|
59
59
|
* question, and a disagreement between them is a row you can edit in place but
|
|
60
60
|
* that "add" navigates away from.
|
|
61
61
|
*
|
|
62
|
-
* A
|
|
63
|
-
* ANY of its variants does. A page-builder list is
|
|
64
|
-
* block, block, ...))` and the natural
|
|
65
|
-
* blocks, one per block type — the union
|
|
62
|
+
* A discriminated union counts as inline when the union itself declares it OR
|
|
63
|
+
* when ANY of its variants does. A page-builder list is
|
|
64
|
+
* `s.array(s.discriminatedUnion("type", block, block, ...))` and the natural
|
|
65
|
+
* place to write the render is on the blocks, one per block type — the union
|
|
66
|
+
* is a dispatch, not something the
|
|
66
67
|
* author thinks of as the field. `some` rather than `every` because the row
|
|
67
68
|
* draws the union's own editor (the tag selector plus the matched variant's
|
|
68
69
|
* fields), which handles every variant either way: with `every`, adding one
|
|
@@ -38,7 +38,47 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
|
|
|
38
38
|
private readonly previewInput;
|
|
39
39
|
private readonly renderInput;
|
|
40
40
|
constructor(item: T, opt?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null, renderInput?: FieldRender | null);
|
|
41
|
+
/**
|
|
42
|
+
* Describe this field.
|
|
43
|
+
*
|
|
44
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
45
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
46
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
47
|
+
* assistant and the MCP tools read.
|
|
48
|
+
*
|
|
49
|
+
* Pass `null` to clear a description set earlier.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const schema = s
|
|
53
|
+
* .array(s.string())
|
|
54
|
+
* .describe("Bullet points shown under the heading");
|
|
55
|
+
* export default c.define("/example.val.ts", schema, ["First", "Second"]);
|
|
56
|
+
*/
|
|
41
57
|
describe(description: string | null): ArraySchema<T, Src>;
|
|
58
|
+
/**
|
|
59
|
+
* Add a custom validation rule to this field.
|
|
60
|
+
*
|
|
61
|
+
* The function is called with the field's value and returns `false` when the
|
|
62
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
63
|
+
* more than once to add more rules — they all run, and every message is
|
|
64
|
+
* reported.
|
|
65
|
+
*
|
|
66
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
67
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
68
|
+
*
|
|
69
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
70
|
+
* before a publish.
|
|
71
|
+
*
|
|
72
|
+
* The function is given the whole array. To validate one item, put a
|
|
73
|
+
* `.validate(...)` on the ITEM schema instead — that reports the error on
|
|
74
|
+
* the item's own row.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const schema = s.array(s.string()).validate((val) =>
|
|
78
|
+
* val.length <= 3 ? false : "At most 3 items",
|
|
79
|
+
* );
|
|
80
|
+
* export default c.define("/example.val.ts", schema, ["First", "Second"]);
|
|
81
|
+
*/
|
|
42
82
|
validate(validationFunction: (src: Src) => false | string): ArraySchema<T, Src>;
|
|
43
83
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
44
84
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
@@ -55,6 +95,25 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
|
|
|
55
95
|
* is the item of another container, in search, in references. What its rows
|
|
56
96
|
* show is the ITEM schema's `preview`, not this. Never how the field is
|
|
57
97
|
* edited (that is `render`). See `preview.ts`.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* // The preview of the ARRAY, for where it is an item of something else.
|
|
101
|
+
* // What its own rows show is the ITEM schema's preview — see below.
|
|
102
|
+
* const tags = s.array(s.string()).preview(({ val }) => ({
|
|
103
|
+
* title: `${val.length} tags`,
|
|
104
|
+
* }));
|
|
105
|
+
* export default c.define("/example.val.ts", s.record(tags), {
|
|
106
|
+
* "a-post": ["news", "release"],
|
|
107
|
+
* });
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* // Rows of an array come from the ITEM's preview, not the array's:
|
|
111
|
+
* const author = s
|
|
112
|
+
* .object({ name: s.string(), role: s.string() })
|
|
113
|
+
* .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
|
|
114
|
+
* export default c.define("/example.val.ts", s.array(author), [
|
|
115
|
+
* { name: "Ada", role: "Engineer" },
|
|
116
|
+
* ]);
|
|
58
117
|
*/
|
|
59
118
|
preview(select: ItemPreviewInput<Src>): ArraySchema<T, Src>;
|
|
60
119
|
/**
|
|
@@ -63,6 +122,12 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
|
|
|
63
122
|
* instead of a preview row that navigates to it.
|
|
64
123
|
*
|
|
65
124
|
* Static configuration, not a callback — see `render.ts`.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* const schema = s.record(s.array(s.string()).render({ as: "inline" }));
|
|
128
|
+
* export default c.define("/example.val.ts", schema, {
|
|
129
|
+
* "a-post": ["news", "release"],
|
|
130
|
+
* });
|
|
66
131
|
*/
|
|
67
132
|
render(input: FieldRender): ArraySchema<T, Src>;
|
|
68
133
|
}
|
|
@@ -24,7 +24,41 @@ export declare class BooleanSchema<Src extends boolean | null> extends Schema<Sr
|
|
|
24
24
|
private readonly renderInput;
|
|
25
25
|
private readonly previewInput;
|
|
26
26
|
constructor(opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
27
|
+
/**
|
|
28
|
+
* Describe this field.
|
|
29
|
+
*
|
|
30
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
31
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
32
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
33
|
+
* assistant and the MCP tools read.
|
|
34
|
+
*
|
|
35
|
+
* Pass `null` to clear a description set earlier.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const schema = s.boolean().describe("Show this item in the main menu");
|
|
39
|
+
* export default c.define("/example.val.ts", schema, true);
|
|
40
|
+
*/
|
|
27
41
|
describe(description: string | null): BooleanSchema<Src>;
|
|
42
|
+
/**
|
|
43
|
+
* Add a custom validation rule to this field.
|
|
44
|
+
*
|
|
45
|
+
* The function is called with the field's value and returns `false` when the
|
|
46
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
47
|
+
* more than once to add more rules — they all run, and every message is
|
|
48
|
+
* reported.
|
|
49
|
+
*
|
|
50
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
51
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
52
|
+
*
|
|
53
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
54
|
+
* before a publish.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const schema = s.boolean().validate((val) =>
|
|
58
|
+
* val === true ? false : "This has to be on for now",
|
|
59
|
+
* );
|
|
60
|
+
* export default c.define("/example.val.ts", schema, true);
|
|
61
|
+
*/
|
|
28
62
|
validate(validationFunction: (src: Src) => false | string): BooleanSchema<Src>;
|
|
29
63
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
30
64
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
@@ -38,12 +72,22 @@ export declare class BooleanSchema<Src extends boolean | null> extends Schema<Sr
|
|
|
38
72
|
* instead of a preview row that navigates to it.
|
|
39
73
|
*
|
|
40
74
|
* Static configuration, not a callback — see `render.ts`.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const schema = s.array(s.boolean().render({ as: "inline" }));
|
|
78
|
+
* export default c.define("/example.val.ts", schema, [true, false]);
|
|
41
79
|
*/
|
|
42
80
|
render(input: FieldRender): BooleanSchema<Src>;
|
|
43
81
|
/**
|
|
44
82
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
45
83
|
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
46
84
|
* itself is edited (that is `render`). See `preview.ts`.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* const schema = s.array(
|
|
88
|
+
* s.boolean().preview(({ val }) => ({ title: val ? "On" : "Off" })),
|
|
89
|
+
* );
|
|
90
|
+
* export default c.define("/example.val.ts", schema, [true, false]);
|
|
47
91
|
*/
|
|
48
92
|
preview(select: ItemPreviewInput<Src>): BooleanSchema<Src>;
|
|
49
93
|
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|
|
@@ -55,7 +55,52 @@ export declare class CodeSchema<Src extends string | null> extends Schema<Src> {
|
|
|
55
55
|
private readonly renderInput;
|
|
56
56
|
private readonly previewInput;
|
|
57
57
|
constructor(options?: CodeOptions | undefined, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
58
|
+
/**
|
|
59
|
+
* Describe this field.
|
|
60
|
+
*
|
|
61
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
62
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
63
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
64
|
+
* assistant and the MCP tools read.
|
|
65
|
+
*
|
|
66
|
+
* Pass `null` to clear a description set earlier.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const schema = s
|
|
70
|
+
* .code({ language: "css" })
|
|
71
|
+
* .describe("Injected into the page head — keep it small");
|
|
72
|
+
* export default c.define(
|
|
73
|
+
* "/example.val.ts",
|
|
74
|
+
* schema,
|
|
75
|
+
* ".hero { color: red; }",
|
|
76
|
+
* );
|
|
77
|
+
*/
|
|
58
78
|
describe(description: string | null): CodeSchema<Src>;
|
|
79
|
+
/**
|
|
80
|
+
* Add a custom validation rule to this field.
|
|
81
|
+
*
|
|
82
|
+
* The function is called with the field's value and returns `false` when the
|
|
83
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
84
|
+
* more than once to add more rules — they all run, and every message is
|
|
85
|
+
* reported.
|
|
86
|
+
*
|
|
87
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
88
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
89
|
+
*
|
|
90
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
91
|
+
* before a publish.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const schema = s.code({ language: "json" }).validate((val) => {
|
|
95
|
+
* try {
|
|
96
|
+
* JSON.parse(val);
|
|
97
|
+
* return false;
|
|
98
|
+
* } catch {
|
|
99
|
+
* return "Must be valid JSON";
|
|
100
|
+
* }
|
|
101
|
+
* });
|
|
102
|
+
* export default c.define("/example.val.ts", schema, '{ "a": 1 }');
|
|
103
|
+
*/
|
|
59
104
|
validate(validationFunction: (src: Src) => false | string): CodeSchema<Src>;
|
|
60
105
|
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
61
106
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
@@ -69,12 +114,30 @@ export declare class CodeSchema<Src extends string | null> extends Schema<Src> {
|
|
|
69
114
|
* instead of a preview row that navigates to it.
|
|
70
115
|
*
|
|
71
116
|
* Static configuration, not a callback — see `render.ts`.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* const schema = s.array(
|
|
120
|
+
* s.code({ language: "css" }).render({ as: "inline" }),
|
|
121
|
+
* );
|
|
122
|
+
* export default c.define("/example.val.ts", schema, [
|
|
123
|
+
* ".hero { color: red; }",
|
|
124
|
+
* ]);
|
|
72
125
|
*/
|
|
73
126
|
render(input: FieldRender): CodeSchema<Src>;
|
|
74
127
|
/**
|
|
75
128
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
76
129
|
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
77
130
|
* itself is edited (that is `render`). See `preview.ts`.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* const schema = s.array(
|
|
134
|
+
* s.code({ language: "css" }).preview(({ val }) => ({
|
|
135
|
+
* title: val.split("\n")[0],
|
|
136
|
+
* })),
|
|
137
|
+
* );
|
|
138
|
+
* export default c.define("/example.val.ts", schema, [
|
|
139
|
+
* ".hero { color: red; }",
|
|
140
|
+
* ]);
|
|
78
141
|
*/
|
|
79
142
|
preview(select: ItemPreviewInput<Src>): CodeSchema<Src>;
|
|
80
143
|
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|