@valbuild/core 0.117.0 → 0.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/dist/declarations/src/index.d.ts +3 -0
  3. package/dist/declarations/src/initSchema.d.ts +27 -0
  4. package/dist/declarations/src/initVal.d.ts +11 -8
  5. package/dist/declarations/src/schema/array.d.ts +2 -2
  6. package/dist/declarations/src/schema/boolean.d.ts +2 -2
  7. package/dist/declarations/src/schema/code.d.ts +2 -2
  8. package/dist/declarations/src/schema/color.d.ts +2 -2
  9. package/dist/declarations/src/schema/date.d.ts +2 -2
  10. package/dist/declarations/src/schema/datetime.d.ts +2 -2
  11. package/dist/declarations/src/schema/file.d.ts +2 -2
  12. package/dist/declarations/src/schema/image.d.ts +2 -2
  13. package/dist/declarations/src/schema/index.d.ts +12 -3
  14. package/dist/declarations/src/schema/keyOf.d.ts +2 -2
  15. package/dist/declarations/src/schema/literal.d.ts +2 -2
  16. package/dist/declarations/src/schema/number.d.ts +2 -2
  17. package/dist/declarations/src/schema/object.d.ts +2 -2
  18. package/dist/declarations/src/schema/record.d.ts +2 -2
  19. package/dist/declarations/src/schema/richtext.d.ts +3 -3
  20. package/dist/declarations/src/schema/route.d.ts +2 -2
  21. package/dist/declarations/src/schema/settings.d.ts +102 -0
  22. package/dist/declarations/src/schema/string.d.ts +2 -2
  23. package/dist/declarations/src/schema/union.d.ts +2 -2
  24. package/dist/declarations/src/selector/index.d.ts +3 -2
  25. package/dist/declarations/src/settingsModule.d.ts +60 -0
  26. package/dist/declarations/src/source/index.d.ts +2 -1
  27. package/dist/declarations/src/source/richtext.d.ts +57 -67
  28. package/dist/declarations/src/source/settings.d.ts +94 -0
  29. package/dist/{index-b8004eea.esm.js → index-5fe6fb98.esm.js} +767 -266
  30. package/dist/{index-941877c6.cjs.prod.js → index-61237991.cjs.prod.js} +772 -265
  31. package/dist/{index-0ba5047e.cjs.dev.js → index-9860179f.cjs.dev.js} +772 -265
  32. package/dist/valbuild-core.cjs.dev.js +7 -1
  33. package/dist/valbuild-core.cjs.prod.js +7 -1
  34. package/dist/valbuild-core.esm.js +1 -1
  35. package/package.json +1 -1
  36. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  37. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  38. package/patch/dist/valbuild-core-patch.esm.js +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,140 @@
1
1
  # @valbuild/core
2
2
 
3
+ ## 0.121.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#605](https://github.com/valbuild/val/pull/605) [`6794d29`](https://github.com/valbuild/val/commit/6794d2980bc81284ab7f2cc667f01cc21c9e3a79) Thanks [@freekh](https://github.com/freekh)! - `s.settings()`: the project's settings, as content.
8
+
9
+ A settings module is one per project, at the root of the content tree:
10
+
11
+ ```typescript
12
+ // settings.val.ts
13
+ export default c.define("/settings.val.ts", s.settings(), {});
14
+ ```
15
+
16
+ Register it in `val.modules.ts` like any other module, and it shows up in the
17
+ Studio under the cog at the foot of the left rail. Everything in it is content:
18
+ it is edited as a draft, it appears in the publish diff, and it is the same for
19
+ everyone working on the project.
20
+
21
+ Every key is optional, at every level, so `{}` is a complete settings module —
22
+ and stays one as sections are added. What it holds today is the assistant:
23
+
24
+ ```typescript
25
+ export default c.define("/settings.val.ts", s.settings(), {
26
+ assistant: {
27
+ enabled: true,
28
+ context: "A CMS for developers, run by a team of four in Oslo.",
29
+ tone: "Plain and direct. British English, sentence case in headings.",
30
+ },
31
+ });
32
+ ```
33
+
34
+ `context` is background the assistant would otherwise guess at; `tone` is how it
35
+ should write when it writes content. Both are sent with every message it makes.
36
+
37
+ `enabled` decides whether editors have an assistant, and it has **three** states
38
+ rather than two:
39
+
40
+ - `true` — they do.
41
+ - `false` — they do not, and every trace of it goes: no button in the top bar,
42
+ no row in the quick actions, no panel, nothing sent.
43
+ - unset — nobody has decided. The assistant is still **shown**, and asks to be
44
+ turned on before it is used. Hiding an assistant nobody has decided about
45
+ means nobody discovers it; quietly enabling one means a project starts sending
46
+ its content to a model because it did not know to say no.
47
+
48
+ A project with no settings module at all has an assistant, as before: there is
49
+ nowhere to record a decision, and nowhere for the prompt to write the answer.
50
+
51
+ **Breaking: `ai.chat` is gone from `val.config.ts`.** Whether the assistant is
52
+ available is a decision about the project's content, made by the people who edit
53
+ it, so it moved to settings — turning the chat on used to take a developer, a
54
+ deploy and a code review of a boolean. Remove the whole block:
55
+
56
+ ```diff
57
+ const { s, c, val, config } = initVal({
58
+ - ai: {
59
+ - chat: {
60
+ - experimental: { enable: true },
61
+ - suggestions: ["Summarize", "Fix typos at this page"],
62
+ - title: "Ask me anything",
63
+ - description: "Val can answer questions about the content.",
64
+ - },
65
+ - },
66
+ });
67
+ ```
68
+
69
+ `experimental.enable` becomes `assistant.enabled` in the settings module.
70
+ `suggestions`, `title` and `description` are removed with nothing replacing
71
+ them: the assistant now opens with its own copy. A project that had the chat
72
+ enabled and wants it to stay on for everyone should write
73
+ `assistant: { enabled: true }` — otherwise editors are offered it and asked.
74
+
75
+ `ai.commitMessages` stays in `val.config.ts`, and is unaffected.
76
+
77
+ Two settings modules, or one in a subdirectory, is a module error: the dev
78
+ server refuses to serve sources, `npx val validate` reports it against the file,
79
+ and the Studio says so rather than picking one.
80
+
81
+ ### Patch Changes
82
+
83
+ - [#607](https://github.com/valbuild/val/pull/607) [`2bcbee1`](https://github.com/valbuild/val/commit/2bcbee1be682c2bbd5b7bc7d152ddd4204162fd2) Thanks [@freekh](https://github.com/freekh)! - `.readonly()` and `.hidden()` now take the flag as an argument, so a schema can
84
+ decide these from a variable instead of only from whether the call was written at
85
+ all:
86
+
87
+ ```ts
88
+ s.string().readonly(!canEdit);
89
+ s.image().hidden(hideMedia);
90
+ ```
91
+
92
+ The argument defaults to `true`, so `.readonly()` and `.readonly(true)` are the
93
+ same thing and nothing about existing schemas changes. Passing `false` leaves the
94
+ field editable or visible, which is also what a schema is without the call - it
95
+ is there so the flag can come from a variable.
96
+
97
+ ## 0.120.0
98
+
99
+ ### Minor Changes
100
+
101
+ - [#589](https://github.com/valbuild/val/pull/589) [`c2d3c0e`](https://github.com/valbuild/val/commit/c2d3c0e6c2010c0a94c725d9dbaa618998773e8a) Thanks [@freekh](https://github.com/freekh)! - **Breaking:** `s.richtext()` options are flat.
102
+
103
+ The `style`, `block` and `inline` groups are gone — every option is a key of its
104
+ own. The names are unchanged, so updating a schema is only a matter of removing
105
+ the wrappers:
106
+
107
+ ```ts
108
+ // before
109
+ s.richtext({
110
+ style: { bold: true, italic: true },
111
+ block: { h1: true, ul: true },
112
+ inline: { a: true, img: s.image() },
113
+ });
114
+
115
+ // after
116
+ s.richtext({
117
+ bold: true,
118
+ italic: true,
119
+ h1: true,
120
+ ul: true,
121
+ a: true,
122
+ img: s.image(),
123
+ });
124
+ ```
125
+
126
+ The groups never carried any meaning the option names did not already have, and
127
+ they cost something real: an option name and its `ValRichText` theme key were
128
+ spelled differently (`block.h1` vs `theme.h1`), so the type that keeps a theme
129
+ exhaustive had to restate all thirteen options by hand. It is now a mapped type
130
+ over the options themselves — which also fixes an inconsistency in it: enabling
131
+ links with a schema (`a: s.route()`) rather than `a: true` now requires an `a`
132
+ key in the theme, the way `img` always has.
133
+
134
+ `ValRichText` themes were already flat and are unchanged. The serialized schema
135
+ that the server sends the Studio is flat too, so a project must not mix
136
+ `@valbuild/*` versions across this release.
137
+
3
138
  ## 0.117.0
4
139
 
5
140
  ### Patch Changes
@@ -50,6 +50,9 @@ export { type SerializedBooleanSchema, BooleanSchema } from "./schema/boolean.js
50
50
  export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
51
51
  export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
52
52
  export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
53
+ export { type SerializedSettingsSchema, SettingsSchema, } from "./schema/settings.js";
54
+ export { type SettingsSource, type AssistantSettingsSource, type AssistantAvailability, ASSISTANT_SETTINGS_MAX_LENGTH, assistantAvailability, } from "./source/settings.js";
55
+ export { type ResolvedSettingsModule, SETTINGS_MODULE_CONVENTION, isRootModuleFilePath, resolveSettingsModule, } from "./settingsModule.js";
53
56
  export { type SerializedColorSchema, type ColorOptions, ColorSchema, } from "./schema/color.js";
54
57
  export { type SerializedCodeSchema, type CodeOptions, type CodeLanguage, CodeSchema, CODE_LANGUAGES, } from "./schema/code.js";
55
58
  export { type ColorFormat, type ParsedColor, COLOR_FORMATS, DEFAULT_COLOR_FORMAT, } from "./schema/colorFormat.js";
@@ -18,6 +18,7 @@ import { color } from "./schema/color.js";
18
18
  import { route } from "./schema/route.js";
19
19
  import { router } from "./schema/router.js";
20
20
  import { images } from "./schema/images.js";
21
+ import { settings } from "./schema/settings.js";
21
22
  export type InitSchema = {
22
23
  /**
23
24
  * Define a string.
@@ -299,6 +300,31 @@ export type InitSchema = {
299
300
  * ```
300
301
  */
301
302
  readonly files: typeof files;
303
+ /**
304
+ * Define the project's settings.
305
+ *
306
+ * One per project, at the root of the content tree — a module file path with
307
+ * no directory segment, `/settings.val.ts` by convention. Every section is
308
+ * optional, so `{}` is a complete settings module and stays one as sections
309
+ * are added.
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * export default c.define("/settings.val.ts", s.settings(), {});
314
+ * ```
315
+ *
316
+ * @example
317
+ * ```typescript
318
+ * export default c.define("/settings.val.ts", s.settings(), {
319
+ * assistant: {
320
+ * enabled: true,
321
+ * context: "A CMS for developers. British English throughout.",
322
+ * tone: "Plain and direct. Sentence case in headings.",
323
+ * },
324
+ * });
325
+ * ```
326
+ */
327
+ readonly settings: typeof settings;
302
328
  };
303
329
  export declare function initSchema(): {
304
330
  string: <T extends string>(options?: Record<string, never>) => import("./index.js").StringSchema<T>;
@@ -349,4 +375,5 @@ export declare function initSchema(): {
349
375
  mimeType: string;
350
376
  alt: string | null;
351
377
  }>, import("./schema/index.js").Schema<string>, Record<string, import("./schema/images.js").ImagesEntryMetadata>>;
378
+ settings: typeof settings;
352
379
  };
@@ -20,17 +20,20 @@ export type ValConfig = {
20
20
  gitBranch?: string;
21
21
  defaultTheme?: "dark" | "light";
22
22
  ai?: {
23
+ /**
24
+ * The AI commit-message summariser.
25
+ *
26
+ * Still config, and deliberately: it is about how the repository is
27
+ * written to rather than about the content, and it is getting settings of
28
+ * its own. The ASSISTANT is not here — it is `s.settings()`, under
29
+ * `assistant.enabled`, because whether editors have a chat is a decision
30
+ * about the
31
+ * project's content, made by the people who edit it, and published like any
32
+ * other content change.
33
+ */
23
34
  commitMessages?: {
24
35
  disabled?: boolean;
25
36
  };
26
- chat?: {
27
- experimental?: {
28
- enable?: boolean;
29
- };
30
- suggestions?: string[];
31
- title?: string;
32
- description?: string;
33
- };
34
37
  };
35
38
  };
36
39
  export type InitVal = {
@@ -43,8 +43,8 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
43
43
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
44
44
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
45
45
  nullable(): ArraySchema<T, Src | null>;
46
- readonly(): ArraySchema<T, Src>;
47
- hidden(): ArraySchema<T, Src>;
46
+ readonly(isReadonly?: boolean): ArraySchema<T, Src>;
47
+ hidden(isHidden?: boolean): ArraySchema<T, Src>;
48
48
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
49
49
  protected executeSerialize(): SerializedArraySchema;
50
50
  protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
@@ -29,8 +29,8 @@ export declare class BooleanSchema<Src extends boolean | null> extends Schema<Sr
29
29
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
30
30
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
31
31
  nullable(): BooleanSchema<Src | null>;
32
- readonly(): BooleanSchema<Src>;
33
- hidden(): BooleanSchema<Src>;
32
+ readonly(isReadonly?: boolean): BooleanSchema<Src>;
33
+ hidden(isHidden?: boolean): BooleanSchema<Src>;
34
34
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
35
35
  /**
36
36
  * How this field is laid out in the editor when it is the item of an array
@@ -61,8 +61,8 @@ export declare class CodeSchema<Src extends string | null> extends Schema<Src> {
61
61
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
62
62
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
63
63
  nullable(): CodeSchema<Src | null>;
64
- readonly(): CodeSchema<Src>;
65
- hidden(): CodeSchema<Src>;
64
+ readonly(isReadonly?: boolean): CodeSchema<Src>;
65
+ hidden(isHidden?: boolean): CodeSchema<Src>;
66
66
  /**
67
67
  * How this field is laid out in the editor when it is the item of an array
68
68
  * or record: `{ as: "inline" }` renders the field itself inside each row,
@@ -56,8 +56,8 @@ export declare class ColorSchema<Src extends string | null> extends Schema<Src>
56
56
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
57
57
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
58
58
  nullable(): ColorSchema<Src | null>;
59
- readonly(): ColorSchema<Src>;
60
- hidden(): ColorSchema<Src>;
59
+ readonly(isReadonly?: boolean): ColorSchema<Src>;
60
+ hidden(isHidden?: boolean): ColorSchema<Src>;
61
61
  /**
62
62
  * How this field is laid out in the editor when it is the item of an array
63
63
  * or record: `{ as: "inline" }` renders the field itself inside each row,
@@ -50,8 +50,8 @@ export declare class DateSchema<Src extends string | null> extends Schema<Src> {
50
50
  from(from: string): DateSchema<Src>;
51
51
  to(to: string): DateSchema<Src>;
52
52
  nullable(): DateSchema<Src | null>;
53
- readonly(): DateSchema<Src>;
54
- hidden(): DateSchema<Src>;
53
+ readonly(isReadonly?: boolean): DateSchema<Src>;
54
+ hidden(isHidden?: boolean): DateSchema<Src>;
55
55
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
56
56
  /**
57
57
  * How this field is laid out in the editor when it is the item of an array
@@ -54,8 +54,8 @@ export declare class DateTimeSchema<Src extends string | null> extends Schema<Sr
54
54
  from(from: string): DateTimeSchema<Src>;
55
55
  to(to: string): DateTimeSchema<Src>;
56
56
  nullable(): DateTimeSchema<Src | null>;
57
- readonly(): DateTimeSchema<Src>;
58
- hidden(): DateTimeSchema<Src>;
57
+ readonly(isReadonly?: boolean): DateTimeSchema<Src>;
58
+ hidden(isHidden?: boolean): DateTimeSchema<Src>;
59
59
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
60
60
  /**
61
61
  * How this field is laid out in the editor when it is the item of an array
@@ -48,8 +48,8 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
48
48
  private galleryEntries;
49
49
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
50
50
  nullable(): FileSchema<Src | null>;
51
- readonly(): FileSchema<Src>;
52
- hidden(): FileSchema<Src>;
51
+ readonly(isReadonly?: boolean): FileSchema<Src>;
52
+ hidden(isHidden?: boolean): FileSchema<Src>;
53
53
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
54
54
  /**
55
55
  * How this field is laid out in the editor when it is the item of an array
@@ -93,8 +93,8 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
93
93
  private galleryEntries;
94
94
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
95
95
  nullable(): ImageSchema<Src | null>;
96
- readonly(): ImageSchema<Src>;
97
- hidden(): ImageSchema<Src>;
96
+ readonly(isReadonly?: boolean): ImageSchema<Src>;
97
+ hidden(isHidden?: boolean): ImageSchema<Src>;
98
98
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
99
99
  /**
100
100
  * How this field is laid out in the editor when it is the item of an array
@@ -17,11 +17,12 @@ import { SerializedColorSchema } from "./color.js";
17
17
  import { SerializedDateSchema } from "./date.js";
18
18
  import { SerializedDateTimeSchema } from "./datetime.js";
19
19
  import { SerializedRouteSchema } from "./route.js";
20
+ import { SerializedSettingsSchema } from "./settings.js";
20
21
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
21
22
  import { FileSource } from "../source/media.js";
22
23
  import { GenericRichTextSourceNode, RichTextSource } from "../source/richtext.js";
23
24
  import { ReifiedPreview, PreviewScope, PreviewItem } from "../preview.js";
24
- export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedImageSchema;
25
+ export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedSettingsSchema | SerializedImageSchema;
25
26
  type Primitives = number | string | boolean | null | FileSource;
26
27
  export type AssertError = {
27
28
  message: string;
@@ -97,15 +98,23 @@ export declare abstract class Schema<Src extends SelectorSource> {
97
98
  *
98
99
  * This is a UI-only flag: the field is rendered disabled in the editor, but
99
100
  * the value is not otherwise validated or enforced differently.
101
+ *
102
+ * The flag defaults to `true`, so `.readonly()` and `.readonly(true)` are the
103
+ * same thing. `.readonly(false)` leaves the field editable, which is what a
104
+ * schema is anyway - pass it when the decision comes from a variable.
100
105
  */
101
- abstract readonly(): Schema<Src>;
106
+ abstract readonly(isReadonly?: boolean): Schema<Src>;
102
107
  /**
103
108
  * Hide this field from the Val editor.
104
109
  *
105
110
  * This is a UI-only flag: the field is not rendered in the editor, but the
106
111
  * value is still stored, validated and serialized as normal.
112
+ *
113
+ * The flag defaults to `true`, so `.hidden()` and `.hidden(true)` are the
114
+ * same thing. `.hidden(false)` leaves the field visible, which is what a
115
+ * schema is anyway - pass it when the decision comes from a variable.
107
116
  */
108
- abstract hidden(): Schema<Src>;
117
+ abstract hidden(isHidden?: boolean): Schema<Src>;
109
118
  protected abstract executeSerialize(): SerializedSchema;
110
119
  /**
111
120
  * @param scope Which paths the caller needs a preview for. Absent means the
@@ -47,8 +47,8 @@ export declare class KeyOfSchema<Sel extends GenericSelector<SourceObject>, Src
47
47
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
48
48
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
49
49
  nullable(): KeyOfSchema<Sel, Src | null>;
50
- readonly(): KeyOfSchema<Sel, Src>;
51
- hidden(): KeyOfSchema<Sel, Src>;
50
+ readonly(isReadonly?: boolean): KeyOfSchema<Sel, Src>;
51
+ hidden(isHidden?: boolean): KeyOfSchema<Sel, Src>;
52
52
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
53
53
  /**
54
54
  * How this field is laid out in the editor when it is the item of an array
@@ -31,8 +31,8 @@ export declare class LiteralSchema<Src extends string | null> extends Schema<Src
31
31
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
32
32
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
33
33
  nullable(): LiteralSchema<Src | null>;
34
- readonly(): LiteralSchema<Src>;
35
- hidden(): LiteralSchema<Src>;
34
+ readonly(isReadonly?: boolean): LiteralSchema<Src>;
35
+ hidden(isHidden?: boolean): LiteralSchema<Src>;
36
36
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
37
37
  /**
38
38
  * How this field is laid out in the editor when it is the item of an array
@@ -35,8 +35,8 @@ export declare class NumberSchema<Src extends number | null> extends Schema<Src>
35
35
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
36
36
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
37
37
  nullable(): NumberSchema<Src | null>;
38
- readonly(): NumberSchema<Src>;
39
- hidden(): NumberSchema<Src>;
38
+ readonly(isReadonly?: boolean): NumberSchema<Src>;
39
+ hidden(isHidden?: boolean): NumberSchema<Src>;
40
40
  max(max: number): NumberSchema<Src>;
41
41
  min(min: number): NumberSchema<Src>;
42
42
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
@@ -55,8 +55,8 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
55
55
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
56
56
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
57
57
  nullable(): ObjectSchema<Props, Src | null>;
58
- readonly(): ObjectSchema<Props, Src>;
59
- hidden(): ObjectSchema<Props, Src>;
58
+ readonly(isReadonly?: boolean): ObjectSchema<Props, Src>;
59
+ hidden(isHidden?: boolean): ObjectSchema<Props, Src>;
60
60
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
61
61
  /**
62
62
  * How this field is laid out in the editor when it is the item of an array
@@ -84,8 +84,8 @@ export declare class RecordSchema<T extends Schema<SelectorSource>, K extends Sc
84
84
  private validateMediaMimeType;
85
85
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
86
86
  nullable(): RecordSchema<T, K, Src | null>;
87
- readonly(): RecordSchema<T, K, Src>;
88
- hidden(): RecordSchema<T, K, Src>;
87
+ readonly(isReadonly?: boolean): RecordSchema<T, K, Src>;
88
+ hidden(isHidden?: boolean): RecordSchema<T, K, Src>;
89
89
  router(router: ValRouter): RecordSchema<T, K, Src>;
90
90
  remote(): RecordSchema<T, K, Src>;
91
91
  /**
@@ -15,7 +15,7 @@ export type SerializedRichTextSchema = {
15
15
  /** Set when this schema declares a `preview`. The closure itself cannot serialize. */
16
16
  preview?: true;
17
17
  opt: boolean;
18
- options?: SerializedRichTextOptions & ValidationOptions;
18
+ options?: SerializedRichTextOptions;
19
19
  customValidate?: boolean;
20
20
  readonly?: boolean;
21
21
  hidden?: boolean;
@@ -40,8 +40,8 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
40
40
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
41
41
  private recursiveAssert;
42
42
  nullable(): RichTextSchema<O, Src | null>;
43
- readonly(): RichTextSchema<O, Src>;
44
- hidden(): RichTextSchema<O, Src>;
43
+ readonly(isReadonly?: boolean): RichTextSchema<O, Src>;
44
+ hidden(isHidden?: boolean): RichTextSchema<O, Src>;
45
45
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
46
46
  /**
47
47
  * How this field is laid out in the editor when it is the item of an array
@@ -71,8 +71,8 @@ export declare class RouteSchema<Src extends string | null> extends Schema<Src>
71
71
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
72
72
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
73
73
  nullable(): RouteSchema<Src | null>;
74
- readonly(): RouteSchema<Src>;
75
- hidden(): RouteSchema<Src>;
74
+ readonly(isReadonly?: boolean): RouteSchema<Src>;
75
+ hidden(isHidden?: boolean): RouteSchema<Src>;
76
76
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
77
77
  /**
78
78
  * How this field is laid out in the editor when it is the item of an array
@@ -0,0 +1,102 @@
1
+ import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
+ import { PreviewScope, ReifiedPreview } from "../preview.js";
3
+ import { FieldRender } from "../render.js";
4
+ import { SelectorSource } from "../selector/index.js";
5
+ import { SettingsSource } from "../source/settings.js";
6
+ import { ModuleFilePath, SourcePath } from "../val/index.js";
7
+ import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
8
+ export type SerializedSettingsSchema = {
9
+ type: "settings";
10
+ /**
11
+ * The sections this settings schema knows about, keyed by name.
12
+ *
13
+ * Carried in full rather than reconstructed from the Val version, so a Studio
14
+ * that predates a section still deserializes the schema (it renders what it
15
+ * recognises and leaves the rest alone) instead of failing on an unknown
16
+ * shape.
17
+ */
18
+ items: Record<string, SerializedSchema>;
19
+ opt: boolean;
20
+ /**
21
+ * Present in the shape, never set by `s.settings()`.
22
+ *
23
+ * `render` and `preview` say how a value is drawn where it is an ITEM of an
24
+ * array or a record, and `customValidate` marks a user's `validate` closure. A
25
+ * settings module is not an item, and `s.settings()` takes no arguments to
26
+ * declare any of the three with — so nothing writes them.
27
+ *
28
+ * They are declared anyway, and with their real types rather than `never`, for
29
+ * two reasons: reading `schema.render` off a `SerializedSchema` has to stay
30
+ * legal (`isInlineRender` does exactly that), and a `?: never` member is typed
31
+ * `undefined`, which makes the whole `SerializedSchema` union stop being
32
+ * assignable to `Json` — and serialized schemas travel to the Studio and to
33
+ * the MCP tools as JSON.
34
+ */
35
+ render?: FieldRender;
36
+ preview?: true;
37
+ customValidate?: boolean;
38
+ readonly?: boolean;
39
+ hidden?: boolean;
40
+ description?: string;
41
+ };
42
+ /**
43
+ * An object whose keys are all optional: absent means unset.
44
+ *
45
+ * Both the settings module itself and each of its sections are this. See
46
+ * {@link SettingsSource} for why settings cannot be an object schema, and
47
+ * {@link settings} for what a settings module holds.
48
+ *
49
+ * There is no `describe`, `validate`, `render` or `preview` on purpose: the
50
+ * shape is Val's, not the schema author's — `s.settings()` takes no arguments —
51
+ * and the Studio renders each section with a UI built for it.
52
+ */
53
+ export declare class SettingsSchema<Src extends {
54
+ [key: string]: SelectorSource;
55
+ } | null> extends Schema<Src> {
56
+ private readonly items;
57
+ private readonly opt;
58
+ private readonly isReadonly;
59
+ private readonly isHidden;
60
+ private readonly description?;
61
+ constructor(items: Record<string, Schema<SelectorSource>>, opt?: boolean, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined);
62
+ protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
63
+ protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
64
+ nullable(): SettingsSchema<Src | null>;
65
+ readonly(): SettingsSchema<Src>;
66
+ hidden(): SettingsSchema<Src>;
67
+ protected executeCustomValidateAt(): ValidationError[];
68
+ protected executeSerialize(): SerializedSchema;
69
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
70
+ }
71
+ /**
72
+ * Define the project's settings.
73
+ *
74
+ * One settings module per project, at the root of the content tree — a module
75
+ * file path with no directory segment, `/settings.val.ts` by convention:
76
+ *
77
+ * ```typescript
78
+ * export default c.define("/settings.val.ts", s.settings(), {});
79
+ * ```
80
+ *
81
+ * Every section is optional, so `{}` is a complete settings module and stays
82
+ * one as sections are added. Fill in what the project needs:
83
+ *
84
+ * ```typescript
85
+ * export default c.define("/settings.val.ts", s.settings(), {
86
+ * assistant: {
87
+ * enabled: true,
88
+ * context: "Val is a CMS for developers. British English, and 'Val' is never 'VAL'.",
89
+ * tone: "Plain and direct. No exclamation marks, sentence case in headings.",
90
+ * },
91
+ * });
92
+ * ```
93
+ *
94
+ * The Studio edits it under the cog at the foot of the left rail, and the
95
+ * assistant is told `assistant.context` and `assistant.tone` on every message.
96
+ *
97
+ * `s.settings()` takes no arguments: the shape is Val's, which is what lets the
98
+ * Studio render a UI built for each field rather than a generic form. A
99
+ * project's own global content — a site name, social links — belongs in a
100
+ * module of its own.
101
+ */
102
+ export declare function settings(): SettingsSchema<SettingsSource>;
@@ -82,8 +82,8 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
82
82
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
83
83
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
84
84
  nullable(): StringSchema<Src | null>;
85
- readonly(): StringSchema<Src>;
86
- hidden(): StringSchema<Src>;
85
+ readonly(isReadonly?: boolean): StringSchema<Src>;
86
+ hidden(isHidden?: boolean): StringSchema<Src>;
87
87
  raw(): StringSchema<Src extends null ? RawString | null : RawString>;
88
88
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
89
89
  protected executeSerialize(): SerializedSchema;
@@ -56,8 +56,8 @@ export declare class UnionSchema<Key extends string | Schema<string>, T extends
56
56
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
57
57
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
58
58
  nullable(): UnionSchema<Key, T, Src | null>;
59
- readonly(): UnionSchema<Key, T, Src>;
60
- hidden(): UnionSchema<Key, T, Src>;
59
+ readonly(isReadonly?: boolean): UnionSchema<Key, T, Src>;
60
+ hidden(isHidden?: boolean): UnionSchema<Key, T, Src>;
61
61
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
62
62
  /**
63
63
  * The variant schema a value takes, for a TAGGED union (string `key`). `null`
@@ -12,10 +12,11 @@ import { MediaSource } from "../source/media.js";
12
12
  import { AllRichTextOptions, RichTextSource } from "../source/richtext.js";
13
13
  import { RichTextSelector } from "./richtext.js";
14
14
  import { JsonSource } from "../source/json.js";
15
- export type Selector<T extends Source> = Source extends T ? GenericSelector<T> : T extends MediaSource ? GenericSelector<T> : T extends JsonSource ? GenericSelector<JsonSource> : T extends RichTextSource<infer O> ? RichTextSelector<O> : T extends SourceObject ? ObjectSelector<T> : T extends SourceArray ? ArraySelector<T> : T extends string ? StringSelector<T> : T extends number ? NumberSelector<T> : T extends boolean ? BooleanSelector<T> : T extends null ? PrimitiveSelector<null> : never;
15
+ import { SettingsSource } from "../source/settings.js";
16
+ export type Selector<T extends Source> = Source extends T ? GenericSelector<T> : T extends MediaSource ? GenericSelector<T> : T extends JsonSource ? GenericSelector<JsonSource> : T extends RichTextSource<infer O> ? RichTextSelector<O> : T extends SourceObject ? ObjectSelector<T> : T extends SourceArray ? ArraySelector<T> : T extends SettingsSource ? GenericSelector<T> : T extends string ? StringSelector<T> : T extends number ? NumberSelector<T> : T extends boolean ? BooleanSelector<T> : T extends null ? PrimitiveSelector<null> : never;
16
17
  export type SelectorSource = SourcePrimitive | undefined | readonly SelectorSource[] | {
17
18
  [key: string]: SelectorSource;
18
- } | MediaSource | JsonSource | RichTextSource<AllRichTextOptions> | GenericSelector<Source>;
19
+ } | MediaSource | JsonSource | SettingsSource | RichTextSource<AllRichTextOptions> | GenericSelector<Source>;
19
20
  /**
20
21
  * @internal
21
22
  */