@valbuild/core 0.125.0 → 0.127.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 +299 -0
- package/dist/declarations/src/index.d.ts +8 -2
- package/dist/declarations/src/initSchema.d.ts +62 -15
- package/dist/declarations/src/initVal.d.ts +108 -0
- package/dist/declarations/src/locale.d.ts +55 -0
- package/dist/declarations/src/render.d.ts +5 -4
- package/dist/declarations/src/schema/array.d.ts +69 -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/declaredKeys.d.ts +37 -0
- package/dist/declarations/src/schema/discriminatedUnion.d.ts +170 -0
- package/dist/declarations/src/schema/discriminatedUnionBranch.d.ts +20 -0
- package/dist/declarations/src/schema/enum.d.ts +131 -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 +155 -2
- package/dist/declarations/src/schema/keyOf.d.ts +50 -0
- package/dist/declarations/src/schema/literal.d.ts +48 -0
- package/dist/declarations/src/schema/locale.d.ts +132 -0
- package/dist/declarations/src/schema/number.d.ts +58 -0
- package/dist/declarations/src/schema/object.d.ts +70 -0
- package/dist/declarations/src/schema/record.d.ts +187 -1
- 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/settings.d.ts +54 -1
- package/dist/declarations/src/schema/string.d.ts +99 -0
- package/dist/declarations/src/schema/union.d.ts +54 -91
- package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
- package/dist/declarations/src/source/settings.d.ts +51 -0
- package/dist/{index-915dd675.esm.js → index-5a6e6398.esm.js} +3057 -573
- package/dist/{index-daa122af.cjs.dev.js → index-889ac441.cjs.dev.js} +3064 -573
- package/dist/{index-a3846321.cjs.prod.js → index-d44deccb.cjs.prod.js} +3064 -573
- package/dist/valbuild-core.cjs.dev.js +9 -2
- package/dist/valbuild-core.cjs.prod.js +9 -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
|
@@ -11,18 +11,20 @@ import { SerializedObjectSchema } from "./object.js";
|
|
|
11
11
|
import { SerializedRecordSchema } from "./record.js";
|
|
12
12
|
import { SerializedRichTextSchema } from "./richtext.js";
|
|
13
13
|
import { RawString, SerializedStringSchema } from "./string.js";
|
|
14
|
-
import {
|
|
14
|
+
import { SerializedDiscriminatedUnionSchema } from "./discriminatedUnion.js";
|
|
15
|
+
import { SerializedEnumSchema } from "./enum.js";
|
|
15
16
|
import { SerializedCodeSchema } from "./code.js";
|
|
16
17
|
import { SerializedColorSchema } from "./color.js";
|
|
17
18
|
import { SerializedDateSchema } from "./date.js";
|
|
18
19
|
import { SerializedDateTimeSchema } from "./datetime.js";
|
|
20
|
+
import { SerializedLocaleSchema } from "./locale.js";
|
|
19
21
|
import { SerializedRouteSchema } from "./route.js";
|
|
20
22
|
import { SerializedSettingsSchema } from "./settings.js";
|
|
21
23
|
import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
|
|
22
24
|
import { FileSource } from "../source/media.js";
|
|
23
25
|
import { GenericRichTextSourceNode, RichTextSource } from "../source/richtext.js";
|
|
24
26
|
import { ReifiedPreview, PreviewScope, PreviewItem } from "../preview.js";
|
|
25
|
-
export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema |
|
|
27
|
+
export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedDiscriminatedUnionSchema | SerializedEnumSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedLocaleSchema | SerializedSettingsSchema | SerializedImageSchema;
|
|
26
28
|
type Primitives = number | string | boolean | null | FileSource;
|
|
27
29
|
export type AssertError = {
|
|
28
30
|
message: string;
|
|
@@ -73,6 +75,88 @@ export declare abstract class Schema<Src extends SelectorSource> {
|
|
|
73
75
|
* against the real instance. Structural errors publish first; these merge in.
|
|
74
76
|
*/
|
|
75
77
|
protected abstract executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
78
|
+
/**
|
|
79
|
+
* Whether this node is a locale — the field that MARKS a scope, `s.locale()`.
|
|
80
|
+
*
|
|
81
|
+
* Only `LocaleSchema` overrides this. A base implementation rather than an
|
|
82
|
+
* abstract one on purpose: every other schema class answers no, and a
|
|
83
|
+
* question every class had to answer would be twenty edits for one yes.
|
|
84
|
+
*
|
|
85
|
+
* Not the same question as {@link opensLocaleScope}: a locale field marks the
|
|
86
|
+
* scope, the object AROUND it is the scope. See `localeScope.ts`.
|
|
87
|
+
*/
|
|
88
|
+
protected isLocaleField(): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Whether this node OPENS a locale scope, and what opens it.
|
|
91
|
+
*
|
|
92
|
+
* An object with a `s.locale()` field opens one; so does a record keyed by
|
|
93
|
+
* `s.locale()`. Everything below such a node is in that one language, which
|
|
94
|
+
* is why a scope may not contain another — see `localeScope.ts` for the rule
|
|
95
|
+
* and the reason it is validated rather than typed.
|
|
96
|
+
*/
|
|
97
|
+
protected opensLocaleScope(): "field" | "key" | null;
|
|
98
|
+
/**
|
|
99
|
+
* The child schemas a locale scope reaches, by the path segment that reaches
|
|
100
|
+
* them.
|
|
101
|
+
*
|
|
102
|
+
* A structural walk that deliberately does NOT go through `executeSerialize`:
|
|
103
|
+
* serializing a subtree at every node to ask a question about its shape is
|
|
104
|
+
* quadratic, and this is asked during validation. `*` stands for the item of
|
|
105
|
+
* an array or record, which has no name of its own.
|
|
106
|
+
*/
|
|
107
|
+
protected localeScopeChildren(): {
|
|
108
|
+
key: string;
|
|
109
|
+
schema: Schema<SelectorSource>;
|
|
110
|
+
}[];
|
|
111
|
+
/**
|
|
112
|
+
* Every locale scope opened strictly BELOW this node, by path.
|
|
113
|
+
*
|
|
114
|
+
* Stops descending at each one it finds, so a scope three deep is reported
|
|
115
|
+
* once, by the scope immediately enclosing it, rather than by every ancestor.
|
|
116
|
+
*/
|
|
117
|
+
protected localeScopesBelow(prefix?: string[]): {
|
|
118
|
+
path: string[];
|
|
119
|
+
kind: "field" | "key";
|
|
120
|
+
}[];
|
|
121
|
+
/**
|
|
122
|
+
* The scope rule, as errors at this node — or `false` if it is not broken.
|
|
123
|
+
*
|
|
124
|
+
* Two things are wrong and both are wrong in the SCHEMA rather than in the
|
|
125
|
+
* content, so both are `schemaError`s: an object with two locale fields (the
|
|
126
|
+
* subtree below it would be in two languages at once), and a locale scope
|
|
127
|
+
* inside another (the inner one would silently override the outer for part
|
|
128
|
+
* of a subtree that is supposed to be one language throughout).
|
|
129
|
+
*
|
|
130
|
+
* Validated rather than typed on purpose. Expressing "no scope below this
|
|
131
|
+
* one" as a constraint means threading it through every schema class's type
|
|
132
|
+
* parameter, and the errors that fall out of a recursive constraint like
|
|
133
|
+
* that name the whole tree — an unrelated typo in a `.val.ts` would print
|
|
134
|
+
* pages. See the design notes on the locales PR.
|
|
135
|
+
*/
|
|
136
|
+
protected localeScopeErrors(): ValidationError[];
|
|
137
|
+
/**
|
|
138
|
+
* The answer, computed once per schema.
|
|
139
|
+
*
|
|
140
|
+
* Memoised because the question is asked from `executeValidate`, which runs
|
|
141
|
+
* once per SOURCE instance: an array of five hundred blocks asks the item
|
|
142
|
+
* schema five hundred times, and each answer walks the schema below it
|
|
143
|
+
* looking for a nested scope. The answer cannot differ between two instances
|
|
144
|
+
* of the same schema — nothing here reads the source — so the walk is done
|
|
145
|
+
* once and the result handed back after that.
|
|
146
|
+
*
|
|
147
|
+
* The error is still reported at every instance's path. Val's validation
|
|
148
|
+
* errors are keyed by source path and that is where the Studio draws them, so
|
|
149
|
+
* a schema error on a row is a schema error on every row; reporting it on one
|
|
150
|
+
* arbitrary row would put it somewhere nobody was looking.
|
|
151
|
+
*/
|
|
152
|
+
private localeScopeErrorsMemo?;
|
|
153
|
+
private computeLocaleScopeErrors;
|
|
154
|
+
/**
|
|
155
|
+
* This node's own `s.locale()` fields, where it is an object that has any.
|
|
156
|
+
*
|
|
157
|
+
* Empty everywhere else, so `localeScopeErrors` can live on the base class.
|
|
158
|
+
*/
|
|
159
|
+
protected localeFieldNames(): string[];
|
|
76
160
|
protected executeCustomValidateFunctions(src: Src, customValidateFunctions: CustomValidateFunction<Src>[], ctx: {
|
|
77
161
|
path: SourcePath;
|
|
78
162
|
}): ValidationError[];
|
|
@@ -92,6 +176,55 @@ export declare abstract class Schema<Src extends SelectorSource> {
|
|
|
92
176
|
* For example, if you have an object schema, you must assert each key / value pair manually.
|
|
93
177
|
*/
|
|
94
178
|
protected abstract executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
179
|
+
/**
|
|
180
|
+
* Allow `null` as a value for this field.
|
|
181
|
+
*
|
|
182
|
+
* The editor gets a way to clear the field, and the type of the source
|
|
183
|
+
* widens to include `null` — so consuming code has to handle it.
|
|
184
|
+
*
|
|
185
|
+
* `.validate(...)` may be declared before or after `.nullable()`: the
|
|
186
|
+
* validator is carried over either way. Declared before, its argument is
|
|
187
|
+
* typed as non-null even though `null` can reach it, so guard for it.
|
|
188
|
+
*
|
|
189
|
+
* Implementations MUST carry `customValidateFunctions` over to the new
|
|
190
|
+
* instance. `.nullable()` returns a copy, so dropping them there silently
|
|
191
|
+
* un-declares the user's `.validate(...)` whenever it was written before the
|
|
192
|
+
* `.nullable()` — which is the order most people write it in. Thirteen schema
|
|
193
|
+
* classes passed `[]` here until this was fixed; `nullableCustomValidate.test.ts`
|
|
194
|
+
* pins one instance of every factory on `s` against that, and does not compile
|
|
195
|
+
* until a newly added schema is listed in it.
|
|
196
|
+
*
|
|
197
|
+
* The validators keep running when the value IS null: a nullable schema's
|
|
198
|
+
* validator sees `Src | null` and decides for itself. That is what the
|
|
199
|
+
* classes that never dropped them (string, record, route, file, image) have
|
|
200
|
+
* always done.
|
|
201
|
+
*
|
|
202
|
+
* Carrying them over needs a cast, because `Src` sits in a PARAMETER position
|
|
203
|
+
* of {@link CustomValidateFunction} and so `CustomValidateFunction<Src>[]` is
|
|
204
|
+
* not assignable to `CustomValidateFunction<Src | null>[]`. Widening what the
|
|
205
|
+
* functions can be CALLED with is the intent here rather than something the
|
|
206
|
+
* cast gets away with: `null` is handed to them, and the paragraph above is
|
|
207
|
+
* the behaviour that buys.
|
|
208
|
+
*
|
|
209
|
+
* A validator's parameter type was never a runtime guarantee to begin with.
|
|
210
|
+
* `executeValidate` runs the custom validators BEFORE the structural checks
|
|
211
|
+
* (see {@link CustomValidateFunction}'s callers, e.g. `NumberSchema`, which
|
|
212
|
+
* calls them ahead of its `typeof src !== "number"`), so one can already be
|
|
213
|
+
* called with a value of the wrong type entirely — hand-written content, or
|
|
214
|
+
* a node the Studio's walker reached before its type was checked. That is why
|
|
215
|
+
* {@link executeCustomValidateFunctions} catches what a validator throws and
|
|
216
|
+
* reports it as a `schemaError` instead of letting it escape.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* const schema = s.object({
|
|
220
|
+
* title: s.string(),
|
|
221
|
+
* subtitle: s.string().nullable(),
|
|
222
|
+
* });
|
|
223
|
+
* export default c.define("/example.val.ts", schema, {
|
|
224
|
+
* title: "Hello",
|
|
225
|
+
* subtitle: null,
|
|
226
|
+
* });
|
|
227
|
+
*/
|
|
95
228
|
abstract nullable(): Schema<Src | null>;
|
|
96
229
|
/**
|
|
97
230
|
* Mark this field as read-only in the Val editor.
|
|
@@ -102,6 +235,16 @@ export declare abstract class Schema<Src extends SelectorSource> {
|
|
|
102
235
|
* The flag defaults to `true`, so `.readonly()` and `.readonly(true)` are the
|
|
103
236
|
* same thing. `.readonly(false)` leaves the field editable, which is what a
|
|
104
237
|
* schema is anyway - pass it when the decision comes from a variable.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* const schema = s.object({
|
|
241
|
+
* id: s.string().readonly(),
|
|
242
|
+
* title: s.string(),
|
|
243
|
+
* });
|
|
244
|
+
* export default c.define("/example.val.ts", schema, {
|
|
245
|
+
* id: "generated-by-the-build",
|
|
246
|
+
* title: "Hello",
|
|
247
|
+
* });
|
|
105
248
|
*/
|
|
106
249
|
abstract readonly(isReadonly?: boolean): Schema<Src>;
|
|
107
250
|
/**
|
|
@@ -113,6 +256,16 @@ export declare abstract class Schema<Src extends SelectorSource> {
|
|
|
113
256
|
* The flag defaults to `true`, so `.hidden()` and `.hidden(true)` are the
|
|
114
257
|
* same thing. `.hidden(false)` leaves the field visible, which is what a
|
|
115
258
|
* schema is anyway - pass it when the decision comes from a variable.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* const schema = s.object({
|
|
262
|
+
* title: s.string(),
|
|
263
|
+
* internalNotes: s.string().hidden(),
|
|
264
|
+
* });
|
|
265
|
+
* export default c.define("/example.val.ts", schema, {
|
|
266
|
+
* title: "Hello",
|
|
267
|
+
* internalNotes: "Not shown in the editor",
|
|
268
|
+
* });
|
|
116
269
|
*/
|
|
117
270
|
abstract hidden(isHidden?: boolean): Schema<Src>;
|
|
118
271
|
protected abstract executeSerialize(): SerializedSchema;
|
|
@@ -42,7 +42,45 @@ export declare class KeyOfSchema<Sel extends GenericSelector<SourceObject>, Src
|
|
|
42
42
|
private readonly renderInput;
|
|
43
43
|
private readonly previewInput;
|
|
44
44
|
constructor(schema?: SerializedRefSchema | undefined, sourcePath?: SourcePath | undefined, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
45
|
+
/**
|
|
46
|
+
* Describe this field.
|
|
47
|
+
*
|
|
48
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
49
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
50
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
51
|
+
* assistant and the MCP tools read.
|
|
52
|
+
*
|
|
53
|
+
* Pass `null` to clear a description set earlier.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* import authorsVal from "./authors.val"; // a record module
|
|
57
|
+
* const schema = s.keyOf(authorsVal).describe("Who wrote this post");
|
|
58
|
+
* export default c.define("/example.val.ts", schema, "ada");
|
|
59
|
+
*/
|
|
45
60
|
describe(description: string | null): KeyOfSchema<Sel, Src>;
|
|
61
|
+
/**
|
|
62
|
+
* Add a custom validation rule to this field.
|
|
63
|
+
*
|
|
64
|
+
* The function is called with the field's value and returns `false` when the
|
|
65
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
66
|
+
* more than once to add more rules — they all run, and every message is
|
|
67
|
+
* reported.
|
|
68
|
+
*
|
|
69
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
70
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
71
|
+
*
|
|
72
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
73
|
+
* before a publish.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* import authorsVal from "./authors.val"; // a record module
|
|
77
|
+
* const schema = s
|
|
78
|
+
* .keyOf(authorsVal)
|
|
79
|
+
* .validate((val) =>
|
|
80
|
+
* val === "placeholder" ? "Pick a real author" : false,
|
|
81
|
+
* );
|
|
82
|
+
* export default c.define("/example.val.ts", schema, "ada");
|
|
83
|
+
*/
|
|
46
84
|
validate(validationFunction: (src: Src) => false | string): KeyOfSchema<Sel, Src>;
|
|
47
85
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
48
86
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
@@ -56,12 +94,24 @@ export declare class KeyOfSchema<Sel extends GenericSelector<SourceObject>, Src
|
|
|
56
94
|
* instead of a preview row that navigates to it.
|
|
57
95
|
*
|
|
58
96
|
* Static configuration, not a callback — see `render.ts`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* import authorsVal from "./authors.val"; // a record module
|
|
100
|
+
* const schema = s.array(s.keyOf(authorsVal).render({ as: "inline" }));
|
|
101
|
+
* export default c.define("/example.val.ts", schema, ["ada"]);
|
|
59
102
|
*/
|
|
60
103
|
render(input: FieldRender): KeyOfSchema<Sel, Src>;
|
|
61
104
|
/**
|
|
62
105
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
63
106
|
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
64
107
|
* itself is edited (that is `render`). See `preview.ts`.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* import authorsVal from "./authors.val"; // a record module
|
|
111
|
+
* const schema = s.array(
|
|
112
|
+
* s.keyOf(authorsVal).preview(({ val }) => ({ title: val })),
|
|
113
|
+
* );
|
|
114
|
+
* export default c.define("/example.val.ts", schema, ["ada"]);
|
|
65
115
|
*/
|
|
66
116
|
preview(select: ItemPreviewInput<Src>): KeyOfSchema<Sel, Src>;
|
|
67
117
|
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|
|
@@ -25,8 +25,46 @@ export declare class LiteralSchema<Src extends string | null> extends Schema<Src
|
|
|
25
25
|
private readonly description?;
|
|
26
26
|
private readonly renderInput;
|
|
27
27
|
private readonly previewInput;
|
|
28
|
+
/** Type-only marker: as a record key, this declares the key set. See `LocaleSchema`. */
|
|
29
|
+
readonly __declaresRecordKeys: true;
|
|
28
30
|
constructor(value: string, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
31
|
+
/**
|
|
32
|
+
* Describe this field.
|
|
33
|
+
*
|
|
34
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
35
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
36
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
37
|
+
* assistant and the MCP tools read.
|
|
38
|
+
*
|
|
39
|
+
* Pass `null` to clear a description set earlier.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const schema = s
|
|
43
|
+
* .literal("hero")
|
|
44
|
+
* .describe("The block type — it identifies the variant");
|
|
45
|
+
* export default c.define("/example.val.ts", schema, "hero");
|
|
46
|
+
*/
|
|
29
47
|
describe(description: string | null): LiteralSchema<Src>;
|
|
48
|
+
/**
|
|
49
|
+
* Add a custom validation rule to this field.
|
|
50
|
+
*
|
|
51
|
+
* The function is called with the field's value and returns `false` when the
|
|
52
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
53
|
+
* more than once to add more rules — they all run, and every message is
|
|
54
|
+
* reported.
|
|
55
|
+
*
|
|
56
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
57
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
58
|
+
*
|
|
59
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
60
|
+
* before a publish.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const schema = s.literal("hero").validate((val) =>
|
|
64
|
+
* val === "hero" ? false : "Unknown block type",
|
|
65
|
+
* );
|
|
66
|
+
* export default c.define("/example.val.ts", schema, "hero");
|
|
67
|
+
*/
|
|
30
68
|
validate(validationFunction: (src: Src) => false | string): LiteralSchema<Src>;
|
|
31
69
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
32
70
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
@@ -40,12 +78,22 @@ export declare class LiteralSchema<Src extends string | null> extends Schema<Src
|
|
|
40
78
|
* instead of a preview row that navigates to it.
|
|
41
79
|
*
|
|
42
80
|
* Static configuration, not a callback — see `render.ts`.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const schema = s.array(s.literal("hero").render({ as: "inline" }));
|
|
84
|
+
* export default c.define("/example.val.ts", schema, ["hero"]);
|
|
43
85
|
*/
|
|
44
86
|
render(input: FieldRender): LiteralSchema<Src>;
|
|
45
87
|
/**
|
|
46
88
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
47
89
|
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
48
90
|
* itself is edited (that is `render`). See `preview.ts`.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const schema = s.array(
|
|
94
|
+
* s.literal("hero").preview(({ val }) => ({ title: val })),
|
|
95
|
+
* );
|
|
96
|
+
* export default c.define("/example.val.ts", schema, ["hero"]);
|
|
49
97
|
*/
|
|
50
98
|
preview(select: ItemPreviewInput<Src>): LiteralSchema<Src>;
|
|
51
99
|
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
|
|
2
|
+
import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
|
|
3
|
+
import { FieldRender } from "../render.js";
|
|
4
|
+
import { SourcePath } from "../val/index.js";
|
|
5
|
+
import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
|
|
6
|
+
export type SerializedLocaleSchema = {
|
|
7
|
+
type: "locale";
|
|
8
|
+
/** Static layout config, carried whole in the serialized schema — see `render.ts`. */
|
|
9
|
+
render?: FieldRender;
|
|
10
|
+
/** Set when this schema declares a `preview`. The closure itself cannot serialize. */
|
|
11
|
+
preview?: true;
|
|
12
|
+
opt: boolean;
|
|
13
|
+
customValidate?: boolean;
|
|
14
|
+
readonly?: boolean;
|
|
15
|
+
hidden?: boolean;
|
|
16
|
+
description?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* One of the languages the project publishes.
|
|
20
|
+
*
|
|
21
|
+
* The set is NOT here. It is declared in the settings module — `locales.available`
|
|
22
|
+
* — so that adding a language is something the people who write the content can
|
|
23
|
+
* do, and so that one list drives validation, the Studio's picker and the
|
|
24
|
+
* translation notes. This schema knows only that the value is one of them, which
|
|
25
|
+
* is checked cross-module through the `locale:check-locale` fix, exactly as
|
|
26
|
+
* `keyOf` and `route` check theirs.
|
|
27
|
+
*
|
|
28
|
+
* Never stega encoded (see `stegaEncode.ts`): a locale goes into `<html lang>`,
|
|
29
|
+
* into `hreflang` and into `Intl` constructors, none of which survive invisible
|
|
30
|
+
* characters.
|
|
31
|
+
*/
|
|
32
|
+
export declare class LocaleSchema<Src extends string | null> extends Schema<Src> {
|
|
33
|
+
private readonly opt;
|
|
34
|
+
private readonly customValidateFunctions;
|
|
35
|
+
private readonly isReadonly;
|
|
36
|
+
private readonly isHidden;
|
|
37
|
+
private readonly description?;
|
|
38
|
+
private readonly renderInput;
|
|
39
|
+
private readonly previewInput;
|
|
40
|
+
/**
|
|
41
|
+
* Type-only marker: as a record key, this schema DECLARES the key set.
|
|
42
|
+
*
|
|
43
|
+
* `declare` so it exists in the type and nowhere at runtime — reading it off
|
|
44
|
+
* an instance gives `undefined`, and nothing should. It is how `record()`
|
|
45
|
+
* knows to type entries as `Item | null`: a language nobody has translated
|
|
46
|
+
* into is a null entry, not an absent key. See `DeclaredKeySet` and
|
|
47
|
+
* `RecordSrcOf`.
|
|
48
|
+
*/
|
|
49
|
+
readonly __declaresRecordKeys: true;
|
|
50
|
+
constructor(opt?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
51
|
+
/**
|
|
52
|
+
* A note to whoever edits this field, shown under it in the Studio.
|
|
53
|
+
*
|
|
54
|
+
* Pass `null` to clear a description set earlier.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const schema = s.object({
|
|
58
|
+
* locale: s.locale().describe("The language everything below is written in"),
|
|
59
|
+
* title: s.string(),
|
|
60
|
+
* });
|
|
61
|
+
* export default c.define("/example.val.ts", schema, {
|
|
62
|
+
* locale: "nb-NO",
|
|
63
|
+
* title: "Vinterjakke",
|
|
64
|
+
* });
|
|
65
|
+
*/
|
|
66
|
+
describe(description: string | null): LocaleSchema<Src>;
|
|
67
|
+
/**
|
|
68
|
+
* A check of your own, on top of "is this one of the project's languages".
|
|
69
|
+
*
|
|
70
|
+
* Return a message when the value is WRONG and `false` when it is fine — the
|
|
71
|
+
* two answers are a complaint and no complaint, so write the check as a
|
|
72
|
+
* ternary rather than as `ok || "message"`, which returns `true` and `true`
|
|
73
|
+
* is neither.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const schema = s.object({
|
|
77
|
+
* locale: s.locale().validate((val) =>
|
|
78
|
+
* val === "en-US" ? "This section is not translated into English yet" : false,
|
|
79
|
+
* ),
|
|
80
|
+
* title: s.string(),
|
|
81
|
+
* });
|
|
82
|
+
* export default c.define("/example.val.ts", schema, {
|
|
83
|
+
* locale: "nb-NO",
|
|
84
|
+
* title: "Vinterjakke",
|
|
85
|
+
* });
|
|
86
|
+
*/
|
|
87
|
+
validate(validationFunction: (src: Src) => false | string): LocaleSchema<Src>;
|
|
88
|
+
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
89
|
+
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
90
|
+
nullable(): LocaleSchema<Src | null>;
|
|
91
|
+
readonly(): LocaleSchema<Src>;
|
|
92
|
+
hidden(): LocaleSchema<Src>;
|
|
93
|
+
protected isLocaleField(): boolean;
|
|
94
|
+
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
95
|
+
/**
|
|
96
|
+
* How this field is laid out where it is the item of an array or a record.
|
|
97
|
+
* Static configuration, not a callback — see `render.ts`.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* const schema = s.array(s.locale().render({ as: "inline" }));
|
|
101
|
+
* export default c.define("/example.val.ts", schema, ["nb-NO", "en-US"]);
|
|
102
|
+
*/
|
|
103
|
+
render(input: FieldRender): LocaleSchema<Src>;
|
|
104
|
+
/**
|
|
105
|
+
* How this VALUE is shown where a preview of it is needed. Never how the field
|
|
106
|
+
* itself is edited (that is `render`). See `preview.ts`.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const schema = s.array(
|
|
110
|
+
* s.locale().preview(({ val }) => ({ title: val })),
|
|
111
|
+
* );
|
|
112
|
+
* export default c.define("/example.val.ts", schema, ["nb-NO", "en-US"]);
|
|
113
|
+
*/
|
|
114
|
+
preview(select: ItemPreviewInput<Src>): LocaleSchema<Src>;
|
|
115
|
+
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|
|
116
|
+
protected declaresItemPreview(): boolean;
|
|
117
|
+
protected executeSerialize(): SerializedSchema;
|
|
118
|
+
protected executePreview(): ReifiedPreview;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Define one of the project's languages.
|
|
122
|
+
*
|
|
123
|
+
* The languages themselves are declared in the settings module, under
|
|
124
|
+
* `locales.available` — this says only that a value is one of them.
|
|
125
|
+
*
|
|
126
|
+
* @example // a field: everything under this object is in this language
|
|
127
|
+
* const schema = s.object({ locale: s.locale(), title: s.string() });
|
|
128
|
+
*
|
|
129
|
+
* @example // a key: one entry per language
|
|
130
|
+
* const schema = s.record(s.locale(), s.object({ title: s.string() }));
|
|
131
|
+
*/
|
|
132
|
+
export declare const locale: () => LocaleSchema<string>;
|
|
@@ -30,14 +30,62 @@ export declare class NumberSchema<Src extends number | null> extends Schema<Src>
|
|
|
30
30
|
private readonly renderInput;
|
|
31
31
|
private readonly previewInput;
|
|
32
32
|
constructor(options?: NumberOptions | undefined, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
33
|
+
/**
|
|
34
|
+
* Describe this field.
|
|
35
|
+
*
|
|
36
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
37
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
38
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
39
|
+
* assistant and the MCP tools read.
|
|
40
|
+
*
|
|
41
|
+
* Pass `null` to clear a description set earlier.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const schema = s.number().describe("How many columns the grid uses");
|
|
45
|
+
* export default c.define("/example.val.ts", schema, 3);
|
|
46
|
+
*/
|
|
33
47
|
describe(description: string | null): NumberSchema<Src>;
|
|
48
|
+
/**
|
|
49
|
+
* Add a custom validation rule to this field.
|
|
50
|
+
*
|
|
51
|
+
* The function is called with the field's value and returns `false` when the
|
|
52
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
53
|
+
* more than once to add more rules — they all run, and every message is
|
|
54
|
+
* reported.
|
|
55
|
+
*
|
|
56
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
57
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
58
|
+
*
|
|
59
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
60
|
+
* before a publish.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const schema = s.number().validate((val) =>
|
|
64
|
+
* Number.isInteger(val) ? false : "Must be a whole number",
|
|
65
|
+
* );
|
|
66
|
+
* export default c.define("/example.val.ts", schema, 3);
|
|
67
|
+
*/
|
|
34
68
|
validate(validationFunction: (src: Src) => false | string): NumberSchema<Src>;
|
|
35
69
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
36
70
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
37
71
|
nullable(): NumberSchema<Src | null>;
|
|
38
72
|
readonly(isReadonly?: boolean): NumberSchema<Src>;
|
|
39
73
|
hidden(isHidden?: boolean): NumberSchema<Src>;
|
|
74
|
+
/**
|
|
75
|
+
* Validate that the number is at most `max` (inclusive).
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* const schema = s.number().max(4);
|
|
79
|
+
* export default c.define("/example.val.ts", schema, 3);
|
|
80
|
+
*/
|
|
40
81
|
max(max: number): NumberSchema<Src>;
|
|
82
|
+
/**
|
|
83
|
+
* Validate that the number is at least `min` (inclusive).
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const schema = s.number().min(1).max(4);
|
|
87
|
+
* export default c.define("/example.val.ts", schema, 3);
|
|
88
|
+
*/
|
|
41
89
|
min(min: number): NumberSchema<Src>;
|
|
42
90
|
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
43
91
|
/**
|
|
@@ -46,12 +94,22 @@ export declare class NumberSchema<Src extends number | null> extends Schema<Src>
|
|
|
46
94
|
* instead of a preview row that navigates to it.
|
|
47
95
|
*
|
|
48
96
|
* Static configuration, not a callback — see `render.ts`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const schema = s.array(s.number().render({ as: "inline" }));
|
|
100
|
+
* export default c.define("/example.val.ts", schema, [1, 2]);
|
|
49
101
|
*/
|
|
50
102
|
render(input: FieldRender): NumberSchema<Src>;
|
|
51
103
|
/**
|
|
52
104
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
53
105
|
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
54
106
|
* itself is edited (that is `render`). See `preview.ts`.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const schema = s.array(
|
|
110
|
+
* s.number().preview(({ val }) => ({ title: String(val) })),
|
|
111
|
+
* );
|
|
112
|
+
* export default c.define("/example.val.ts", schema, [1, 2]);
|
|
55
113
|
*/
|
|
56
114
|
preview(select: ItemPreviewInput<Src>): NumberSchema<Src>;
|
|
57
115
|
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|
|
@@ -50,13 +50,65 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
|
|
|
50
50
|
private readonly renderInput;
|
|
51
51
|
private readonly previewInput;
|
|
52
52
|
constructor(items: Props, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
|
|
53
|
+
/**
|
|
54
|
+
* Describe this field.
|
|
55
|
+
*
|
|
56
|
+
* The description is shown next to the field's label in the Val editor, so
|
|
57
|
+
* it is where you say what an editor needs to know but the field name cannot
|
|
58
|
+
* carry. It also travels in the serialized schema, which is what the AI
|
|
59
|
+
* assistant and the MCP tools read.
|
|
60
|
+
*
|
|
61
|
+
* Pass `null` to clear a description set earlier.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* const schema = s
|
|
65
|
+
* .object({ street: s.string(), city: s.string() })
|
|
66
|
+
* .describe("Where the office is — shown on the contact page");
|
|
67
|
+
* export default c.define("/example.val.ts", schema, {
|
|
68
|
+
* street: "Torggata 1",
|
|
69
|
+
* city: "Oslo",
|
|
70
|
+
* });
|
|
71
|
+
*/
|
|
53
72
|
describe(description: string | null): ObjectSchema<Props, Src>;
|
|
73
|
+
/**
|
|
74
|
+
* Add a custom validation rule to this field.
|
|
75
|
+
*
|
|
76
|
+
* The function is called with the field's value and returns `false` when the
|
|
77
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
78
|
+
* more than once to add more rules — they all run, and every message is
|
|
79
|
+
* reported.
|
|
80
|
+
*
|
|
81
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
82
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
83
|
+
*
|
|
84
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
85
|
+
* before a publish.
|
|
86
|
+
*
|
|
87
|
+
* An object validator is the place for a rule that spans FIELDS — one field
|
|
88
|
+
* validated on its own belongs on that field's schema, where the error lands
|
|
89
|
+
* on the field itself.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const schema = s
|
|
93
|
+
* .object({ from: s.number(), to: s.number() })
|
|
94
|
+
* .validate((val) =>
|
|
95
|
+
* val.from <= val.to ? false : "'from' must not be after 'to'",
|
|
96
|
+
* );
|
|
97
|
+
* export default c.define("/example.val.ts", schema, { from: 1, to: 4 });
|
|
98
|
+
*/
|
|
54
99
|
validate(validationFunction: (src: Src) => false | string): ObjectSchema<Props, Src>;
|
|
55
100
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
56
101
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
57
102
|
nullable(): ObjectSchema<Props, Src | null>;
|
|
58
103
|
readonly(isReadonly?: boolean): ObjectSchema<Props, Src>;
|
|
59
104
|
hidden(isHidden?: boolean): ObjectSchema<Props, Src>;
|
|
105
|
+
/** The names of this object's `s.locale()` fields, in declaration order. */
|
|
106
|
+
protected localeFieldNames(): string[];
|
|
107
|
+
protected opensLocaleScope(): "field" | "key" | null;
|
|
108
|
+
protected localeScopeChildren(): {
|
|
109
|
+
key: string;
|
|
110
|
+
schema: Schema<SelectorSource>;
|
|
111
|
+
}[];
|
|
60
112
|
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
61
113
|
/**
|
|
62
114
|
* How this field is laid out in the editor when it is the item of an array
|
|
@@ -64,12 +116,30 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
|
|
|
64
116
|
* instead of a preview row that navigates to it.
|
|
65
117
|
*
|
|
66
118
|
* Static configuration, not a callback — see `render.ts`.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // A page builder: each row edits the object in place, instead of
|
|
122
|
+
* // navigating to it.
|
|
123
|
+
* const block = s
|
|
124
|
+
* .object({ heading: s.string(), body: s.string() })
|
|
125
|
+
* .render({ as: "inline" });
|
|
126
|
+
* export default c.define("/example.val.ts", s.array(block), [
|
|
127
|
+
* { heading: "Hello", body: "World" },
|
|
128
|
+
* ]);
|
|
67
129
|
*/
|
|
68
130
|
render(input: FieldRender): ObjectSchema<Props, Src>;
|
|
69
131
|
/**
|
|
70
132
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
71
133
|
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
72
134
|
* itself is edited (that is `render`). See `preview.ts`.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const author = s
|
|
138
|
+
* .object({ name: s.string(), role: s.string() })
|
|
139
|
+
* .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
|
|
140
|
+
* export default c.define("/example.val.ts", s.array(author), [
|
|
141
|
+
* { name: "Ada", role: "Engineer" },
|
|
142
|
+
* ]);
|
|
73
143
|
*/
|
|
74
144
|
preview(select: ItemPreviewInput<Src>): ObjectSchema<Props, Src>;
|
|
75
145
|
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|