@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +299 -0
  2. package/dist/declarations/src/index.d.ts +8 -2
  3. package/dist/declarations/src/initSchema.d.ts +62 -15
  4. package/dist/declarations/src/initVal.d.ts +108 -0
  5. package/dist/declarations/src/locale.d.ts +55 -0
  6. package/dist/declarations/src/render.d.ts +5 -4
  7. package/dist/declarations/src/schema/array.d.ts +69 -0
  8. package/dist/declarations/src/schema/boolean.d.ts +44 -0
  9. package/dist/declarations/src/schema/code.d.ts +63 -0
  10. package/dist/declarations/src/schema/color.d.ts +54 -0
  11. package/dist/declarations/src/schema/date.d.ts +64 -2
  12. package/dist/declarations/src/schema/datetime.d.ts +87 -2
  13. package/dist/declarations/src/schema/declaredKeys.d.ts +37 -0
  14. package/dist/declarations/src/schema/discriminatedUnion.d.ts +170 -0
  15. package/dist/declarations/src/schema/discriminatedUnionBranch.d.ts +20 -0
  16. package/dist/declarations/src/schema/enum.d.ts +131 -0
  17. package/dist/declarations/src/schema/file.d.ts +77 -0
  18. package/dist/declarations/src/schema/image.d.ts +102 -0
  19. package/dist/declarations/src/schema/index.d.ts +155 -2
  20. package/dist/declarations/src/schema/keyOf.d.ts +50 -0
  21. package/dist/declarations/src/schema/literal.d.ts +48 -0
  22. package/dist/declarations/src/schema/locale.d.ts +132 -0
  23. package/dist/declarations/src/schema/number.d.ts +58 -0
  24. package/dist/declarations/src/schema/object.d.ts +70 -0
  25. package/dist/declarations/src/schema/record.d.ts +187 -1
  26. package/dist/declarations/src/schema/richtext.d.ts +78 -0
  27. package/dist/declarations/src/schema/route.d.ts +44 -0
  28. package/dist/declarations/src/schema/settings.d.ts +54 -1
  29. package/dist/declarations/src/schema/string.d.ts +99 -0
  30. package/dist/declarations/src/schema/union.d.ts +54 -91
  31. package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
  32. package/dist/declarations/src/source/settings.d.ts +51 -0
  33. package/dist/{index-915dd675.esm.js → index-5a6e6398.esm.js} +3057 -573
  34. package/dist/{index-daa122af.cjs.dev.js → index-889ac441.cjs.dev.js} +3064 -573
  35. package/dist/{index-a3846321.cjs.prod.js → index-d44deccb.cjs.prod.js} +3064 -573
  36. package/dist/valbuild-core.cjs.dev.js +9 -2
  37. package/dist/valbuild-core.cjs.prod.js +9 -2
  38. package/dist/valbuild-core.esm.js +1 -1
  39. package/package.json +1 -1
  40. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  41. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  42. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -0,0 +1,170 @@
1
+ import { CustomValidateFunction, Schema, SchemaAssertResult } from "./index.js";
2
+ import { ItemPreviewInput, PreviewItem, ReifiedPreview, PreviewScope } from "../preview.js";
3
+ import { FieldRender } from "../render.js";
4
+ import { SelectorSource } from "../selector/index.js";
5
+ import { SourceObject } from "../source/index.js";
6
+ import { ModuleFilePath, SourcePath } from "../val/index.js";
7
+ import { SerializedObjectSchema } from "./object.js";
8
+ import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
9
+ export type SerializedDiscriminatedUnionSchema = {
10
+ type: "discriminated-union";
11
+ /** Static layout config, carried whole in the serialized schema — see `render.ts`. */
12
+ render?: FieldRender;
13
+ /** Set when this schema declares a `preview`. The closure itself cannot serialize. */
14
+ preview?: true;
15
+ /** The name of the field every variant tags itself with. */
16
+ key: string;
17
+ items: SerializedObjectSchema[];
18
+ opt: boolean;
19
+ customValidate?: boolean;
20
+ readonly?: boolean;
21
+ hidden?: boolean;
22
+ description?: string;
23
+ };
24
+ /** The variant objects a discriminated union on `Key` may be built from. */
25
+ export type DiscriminatedUnionItem<Key extends string> = Schema<SourceObject & {
26
+ [k in Key]: string;
27
+ }>;
28
+ /**
29
+ * At least one variant, so a union with none cannot be written.
30
+ *
31
+ * Everything downstream reads the first one where it needs any — `emptyOf`
32
+ * takes `items[0]` for the value a new entry starts as — and a union with
33
+ * nothing to select is not a thing an author means. `s.enum` says the same
34
+ * about its values.
35
+ */
36
+ export type DiscriminatedUnionItems<Key extends string> = [
37
+ DiscriminatedUnionItem<Key>,
38
+ ...DiscriminatedUnionItem<Key>[]
39
+ ];
40
+ /** The source type a discriminated union over `T` accepts. */
41
+ export type DiscriminatedUnionSourceOf<Key extends string, T extends DiscriminatedUnionItem<Key>[]> = T extends Schema<infer S>[] ? S extends SelectorSource ? S : never : never;
42
+ /**
43
+ * One of several object shapes, told apart by a tag field.
44
+ *
45
+ * Every variant is an object with `key` set to a distinct `s.literal(...)`, and
46
+ * the value of that field decides which variant a value is. Unlike
47
+ * {@link EnumSchema} this is a CONTAINER: the variant's fields are the fields
48
+ * being edited, and everything that walks the schema tree has to descend
49
+ * through the matching variant.
50
+ */
51
+ export declare class DiscriminatedUnionSchema<Key extends string, T extends DiscriminatedUnionItem<Key>[], Src extends DiscriminatedUnionSourceOf<Key, T> | null> extends Schema<Src> {
52
+ private readonly key;
53
+ private readonly items;
54
+ private readonly opt;
55
+ private readonly customValidateFunctions;
56
+ private readonly isReadonly;
57
+ private readonly isHidden;
58
+ private readonly description?;
59
+ private readonly renderInput;
60
+ private readonly previewInput;
61
+ constructor(key: Key, items: T, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
62
+ /**
63
+ * Describe this field.
64
+ *
65
+ * The description is shown next to the field's label in the Val editor, so it is
66
+ * where you say what an editor needs to know but the field name cannot carry. It
67
+ * also travels in the serialized schema, which is what the AI assistant and the
68
+ * MCP tools read.
69
+ *
70
+ * Pass `null` to clear a description set earlier.
71
+ *
72
+ * @example
73
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
74
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
75
+ * const schema = s
76
+ * .discriminatedUnion("type", hero, text)
77
+ * .describe("What this section of the page is");
78
+ * export default c.define("/example.val.ts", schema, {
79
+ * type: "hero",
80
+ * title: "Hello",
81
+ * });
82
+ */
83
+ describe(description: string | null): DiscriminatedUnionSchema<Key, T, Src>;
84
+ /**
85
+ * Add a custom validation rule to this field.
86
+ *
87
+ * The function is called with the field's value and returns `false` when the
88
+ * value is fine, or a STRING with the message to show when it is not. Call it
89
+ * more than once to add more rules — they all run, and every message is
90
+ * reported.
91
+ *
92
+ * Validation runs in the Studio as you type, in `npx val validate` and before a
93
+ * publish.
94
+ *
95
+ * The function is given the whole value, whichever variant it took. A rule
96
+ * that only concerns one variant belongs on that variant's object schema.
97
+ *
98
+ * @example
99
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
100
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
101
+ * const schema = s
102
+ * .discriminatedUnion("type", hero, text)
103
+ * .validate((val) =>
104
+ * val.type === "hero" && !val.title ? "A hero needs a title" : false,
105
+ * );
106
+ * export default c.define("/example.val.ts", schema, {
107
+ * type: "hero",
108
+ * title: "Hello",
109
+ * });
110
+ */
111
+ validate(validationFunction: (src: Src) => false | string): DiscriminatedUnionSchema<Key, T, Src>;
112
+ protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
113
+ protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
114
+ nullable(): DiscriminatedUnionSchema<Key, T, Src | null>;
115
+ readonly(isReadonly?: boolean): DiscriminatedUnionSchema<Key, T, Src>;
116
+ hidden(isHidden?: boolean): DiscriminatedUnionSchema<Key, T, Src>;
117
+ protected localeScopeChildren(): {
118
+ key: string;
119
+ schema: Schema<SelectorSource>;
120
+ }[];
121
+ protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
122
+ /**
123
+ * The variant schema a value takes. `null` when the value matches none of
124
+ * them — a structural error `executeValidate` already reports.
125
+ */
126
+ private matchedVariant;
127
+ /**
128
+ * How this field is laid out in the editor when it is the item of an array
129
+ * or record: `{ as: "inline" }` renders the field itself inside each row,
130
+ * instead of a preview row that navigates to it.
131
+ *
132
+ * Static configuration, not a callback — see `render.ts`.
133
+ *
134
+ * @example
135
+ * // A page builder: the row draws the tag selector and the matched
136
+ * // variant's fields.
137
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
138
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
139
+ * const block = s.discriminatedUnion("type", hero, text).render({ as: "inline" });
140
+ * export default c.define("/example.val.ts", s.array(block), [
141
+ * { type: "hero", title: "Hello" },
142
+ * ]);
143
+ */
144
+ render(input: FieldRender): DiscriminatedUnionSchema<Key, T, Src>;
145
+ /**
146
+ * How this VALUE is shown where a preview of it is needed — a row in a
147
+ * sortable list, a reference dropdown, a search hit. Never how the field
148
+ * itself is edited (that is `render`). See `preview.ts`.
149
+ *
150
+ * Without one of its own, a discriminated union previews as the VARIANT the
151
+ * value takes — declare `preview` on the member objects and the union
152
+ * dispatches.
153
+ *
154
+ * @example
155
+ * const hero = s.object({ type: s.literal("hero"), title: s.string() });
156
+ * const text = s.object({ type: s.literal("text"), body: s.string() });
157
+ * const block = s
158
+ * .discriminatedUnion("type", hero, text)
159
+ * .preview(({ val }) => ({ title: val.type }));
160
+ * export default c.define("/example.val.ts", s.array(block), [
161
+ * { type: "hero", title: "Hello" },
162
+ * ]);
163
+ */
164
+ preview(select: ItemPreviewInput<Src>): DiscriminatedUnionSchema<Key, T, Src>;
165
+ protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
166
+ protected declaresItemPreview(): boolean;
167
+ protected executeSerialize(): SerializedDiscriminatedUnionSchema;
168
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
169
+ }
170
+ export declare const discriminatedUnion: <Key extends string, T extends DiscriminatedUnionItems<Key>>(key: Key, ...objects: T) => DiscriminatedUnionSchema<Key, T, DiscriminatedUnionSourceOf<Key, T>>;
@@ -0,0 +1,20 @@
1
+ import { SerializedSchema } from "./index.js";
2
+ /**
3
+ * The variant of a discriminated union that a value takes, by its tag.
4
+ *
5
+ * A discriminated union is a fork rather than a level: the variant IS the node,
6
+ * and everything a caller wants to know about that node — its fields, its
7
+ * `locale` field, the schema to draw it with — is on the variant and not on the
8
+ * union. Resolving it takes the source, so it cannot be done from the schema
9
+ * alone, which is why every caller that has only the union ends up needing this.
10
+ *
11
+ * `undefined` where the tag matches nothing, rather than a fallback to the first
12
+ * variant: a value whose tag names no variant is not a member of the union,
13
+ * validation is already saying so, and reading fields out of the wrong shape
14
+ * would answer confidently and wrongly.
15
+ *
16
+ * `s.enum()` never reaches here — it is a leaf whose values are strings, with no
17
+ * variants to resolve — so anything that is not a discriminated union answers
18
+ * `undefined` too.
19
+ */
20
+ export declare function discriminatedUnionBranchOf(schema: SerializedSchema | undefined, tag: unknown): SerializedSchema | undefined;
@@ -0,0 +1,131 @@
1
+ import { CustomValidateFunction, Schema, SchemaAssertResult } 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 SerializedEnumSchema = {
7
+ type: "enum";
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
+ /**
13
+ * Every value this field may take, in declaration order — which is the order
14
+ * the editor's dropdown offers them in.
15
+ */
16
+ values: string[];
17
+ opt: boolean;
18
+ customValidate?: boolean;
19
+ readonly?: boolean;
20
+ hidden?: boolean;
21
+ description?: string;
22
+ };
23
+ /**
24
+ * One of a fixed set of strings.
25
+ *
26
+ * The values are the schema — there are no member schemas to descend into, so
27
+ * this is a LEAF, like `s.string()` with a closed domain. That is the whole
28
+ * difference from {@link DiscriminatedUnionSchema}, and the reason the two are
29
+ * separate: everything that walks a schema tree has to recurse into a
30
+ * discriminated union's variants and must not try to recurse into these.
31
+ */
32
+ export declare class EnumSchema<Src extends string | null> extends Schema<Src> {
33
+ private readonly values;
34
+ private readonly opt;
35
+ private readonly customValidateFunctions;
36
+ private readonly isReadonly;
37
+ private readonly isHidden;
38
+ private readonly description?;
39
+ private readonly renderInput;
40
+ private readonly previewInput;
41
+ /**
42
+ * Type-only marker: as a record key, this declares the key set.
43
+ *
44
+ * An enum names every value it may take, so a record keyed by one holds
45
+ * exactly those keys — the same claim `s.literal()` and `s.locale()` make.
46
+ * `DiscriminatedUnionSchema` carries no marker: it discriminates objects and
47
+ * cannot be a record key at all. See `LocaleSchema`.
48
+ */
49
+ readonly __declaresRecordKeys: true;
50
+ constructor(values: readonly string[], opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
51
+ /**
52
+ * Describe this field.
53
+ *
54
+ * The description is shown next to the field's label in the Val editor, so it is
55
+ * where you say what an editor needs to know but the field name cannot carry. It
56
+ * also travels in the serialized schema, which is what the AI assistant and the
57
+ * MCP tools read.
58
+ *
59
+ * Pass `null` to clear a description set earlier.
60
+ *
61
+ * @example
62
+ * const schema = s
63
+ * .enum("draft", "published")
64
+ * .describe("Only published pages are built");
65
+ * export default c.define("/example.val.ts", schema, "draft");
66
+ */
67
+ describe(description: string | null): EnumSchema<Src>;
68
+ /**
69
+ * Add a custom validation rule to this field.
70
+ *
71
+ * The function is called with the field's value and returns `false` when the
72
+ * value is fine, or a STRING with the message to show when it is not. Call it
73
+ * more than once to add more rules — they all run, and every message is
74
+ * reported.
75
+ *
76
+ * Validation runs in the Studio as you type, in `npx val validate` and before a
77
+ * publish.
78
+ *
79
+ * @example
80
+ * const schema = s
81
+ * .enum("draft", "published")
82
+ * .validate((val) =>
83
+ * val === "published" ? "Publishing is frozen this week" : false,
84
+ * );
85
+ * export default c.define("/example.val.ts", schema, "draft");
86
+ */
87
+ validate(validationFunction: (src: Src) => false | string): EnumSchema<Src>;
88
+ protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
89
+ protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
90
+ nullable(): EnumSchema<Src | null>;
91
+ readonly(isReadonly?: boolean): EnumSchema<Src>;
92
+ hidden(isHidden?: boolean): EnumSchema<Src>;
93
+ protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
94
+ /**
95
+ * How this field is laid out in the editor when it is the item of an array
96
+ * or record: `{ as: "inline" }` renders the field itself inside each row,
97
+ * instead of a preview row that navigates to it.
98
+ *
99
+ * Static configuration, not a callback — see `render.ts`.
100
+ *
101
+ * @example
102
+ * const schema = s.array(
103
+ * s.enum("draft", "published").render({ as: "inline" }),
104
+ * );
105
+ * export default c.define("/example.val.ts", schema, ["draft"]);
106
+ */
107
+ render(input: FieldRender): EnumSchema<Src>;
108
+ /**
109
+ * How this VALUE is shown where a preview of it is needed — a row in a
110
+ * sortable list, a reference dropdown, a search hit. Never how the field
111
+ * itself is edited (that is `render`). See `preview.ts`.
112
+ *
113
+ * @example
114
+ * const schema = s.array(
115
+ * s.enum("draft", "published").preview(({ val }) => ({ title: val })),
116
+ * );
117
+ * export default c.define("/example.val.ts", schema, ["draft"]);
118
+ */
119
+ preview(select: ItemPreviewInput<Src>): EnumSchema<Src>;
120
+ protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
121
+ protected declaresItemPreview(): boolean;
122
+ protected executeSerialize(): SerializedEnumSchema;
123
+ protected executePreview(): ReifiedPreview;
124
+ }
125
+ /**
126
+ * Define a string that must be one of a fixed set of values.
127
+ *
128
+ * Named `enumSchema` rather than `enum` because `enum` is a reserved word and
129
+ * cannot be a binding name; it is exposed as `s.enum`.
130
+ */
131
+ export declare const enumSchema: <T extends string>(...values: [T, ...T[]]) => EnumSchema<T>;
@@ -37,8 +37,71 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
37
37
  private readonly renderInput;
38
38
  private readonly previewInput;
39
39
  constructor(options?: FileOptions | undefined, opt?: boolean, isRemote?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], moduleMetadata?: Record<ModulePath, Record<string, FilesEntryMetadata>>, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
40
+ /**
41
+ * Describe this field.
42
+ *
43
+ * The description is shown next to the field's label in the Val editor, so
44
+ * it is where you say what an editor needs to know but the field name cannot
45
+ * carry. It also travels in the serialized schema, which is what the AI
46
+ * assistant and the MCP tools read.
47
+ *
48
+ * Pass `null` to clear a description set earlier.
49
+ *
50
+ * @example
51
+ * const schema = s.file().describe("The PDF offered for download");
52
+ * export default c.define("/example.val.ts", schema, {
53
+ * path: "/public/val/example.pdf",
54
+ * mimeType: "application/pdf",
55
+ * });
56
+ */
40
57
  describe(description: string | null): FileSchema<Src>;
58
+ /**
59
+ * Store the file on Val's remote content host instead of in your repository.
60
+ *
61
+ * The bytes still go into the patch store when the file is uploaded — the
62
+ * push to the remote host happens at publish. What changes is where the
63
+ * published file lives: `path` becomes a remote URL rather than a path under
64
+ * `/public`, so the repository does not grow with every upload.
65
+ *
66
+ * The `path` of a remote file is a URL on the content host, and it is not
67
+ * something to write by hand: upload the file in the Studio, or write a
68
+ * local path and let `npx val validate --fix` upload it and rewrite the
69
+ * `path` to the ref below.
70
+ *
71
+ * @example
72
+ * const schema = s.file({ accept: "application/pdf" }).remote();
73
+ * export default c.define("/example.val.ts", schema, {
74
+ * path: "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/example.pdf",
75
+ * mimeType: "application/pdf",
76
+ * });
77
+ */
41
78
  remote(): FileSchema<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
+ * The second argument carries the `path` of the field being validated, for
94
+ * when the message needs to say where the problem is.
95
+ *
96
+ * @example
97
+ * const schema = s.file().validate((val) =>
98
+ * val.path.endsWith(".pdf") ? false : "Must be a PDF",
99
+ * );
100
+ * export default c.define("/example.val.ts", schema, {
101
+ * path: "/public/val/example.pdf",
102
+ * mimeType: "application/pdf",
103
+ * });
104
+ */
42
105
  validate(validationFunction: CustomValidateFunction<Src>): FileSchema<Src>;
43
106
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
44
107
  /**
@@ -57,12 +120,26 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
57
120
  * instead of a preview row that navigates to it.
58
121
  *
59
122
  * Static configuration, not a callback — see `render.ts`.
123
+ *
124
+ * @example
125
+ * const schema = s.array(s.file().render({ as: "inline" }));
126
+ * export default c.define("/example.val.ts", schema, [
127
+ * { path: "/public/val/example.pdf", mimeType: "application/pdf" },
128
+ * ]);
60
129
  */
61
130
  render(input: FieldRender): FileSchema<Src>;
62
131
  /**
63
132
  * How this VALUE is shown where a preview of it is needed — a row in a
64
133
  * sortable list, a reference dropdown, a search hit. Never how the field
65
134
  * itself is edited (that is `render`). See `preview.ts`.
135
+ *
136
+ * @example
137
+ * const schema = s.array(
138
+ * s.file().preview(({ val }) => ({ title: val.path })),
139
+ * );
140
+ * export default c.define("/example.val.ts", schema, [
141
+ * { path: "/public/val/example.pdf", mimeType: "application/pdf" },
142
+ * ]);
66
143
  */
67
144
  preview(select: ItemPreviewInput<Src>): FileSchema<Src>;
68
145
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
@@ -82,8 +82,82 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
82
82
  private readonly renderInput;
83
83
  private readonly previewInput;
84
84
  constructor(options?: ImageOptions | undefined, opt?: boolean, isRemote?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], moduleMetadata?: Record<ModulePath, Record<string, ImagesEntryMetadata>>, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
85
+ /**
86
+ * Describe this field.
87
+ *
88
+ * The description is shown next to the field's label in the Val editor, so
89
+ * it is where you say what an editor needs to know but the field name cannot
90
+ * carry. It also travels in the serialized schema, which is what the AI
91
+ * assistant and the MCP tools read.
92
+ *
93
+ * Pass `null` to clear a description set earlier.
94
+ *
95
+ * @example
96
+ * const schema = s
97
+ * .image()
98
+ * .describe("Shown at the top of the page, 16:9 works best");
99
+ * export default c.define("/example.val.ts", schema, {
100
+ * path: "/public/val/example.png",
101
+ * width: 100,
102
+ * height: 100,
103
+ * mimeType: "image/png",
104
+ * });
105
+ */
85
106
  describe(description: string | null): ImageSchema<Src>;
107
+ /**
108
+ * Store the image on Val's remote content host instead of in your repository.
109
+ *
110
+ * The bytes still go into the patch store when the image is uploaded — the
111
+ * push to the remote host happens at publish. What changes is where the
112
+ * published image lives: `path` becomes a remote URL rather than a path
113
+ * under `/public`, so the repository does not grow with every upload.
114
+ *
115
+ * The `path` of a remote image is a URL on the content host, and it is not
116
+ * something to write by hand: upload the image in the Studio, or write a
117
+ * local path and let `npx val validate --fix` upload it and rewrite the
118
+ * `path` to the ref below.
119
+ *
120
+ * @example
121
+ * const schema = s
122
+ * .image({ accept: "image/webp", directory: "/public/val/images" })
123
+ * .remote();
124
+ * export default c.define("/example.val.ts", schema, {
125
+ * path: "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/images/example.webp",
126
+ * width: 100,
127
+ * height: 100,
128
+ * mimeType: "image/webp",
129
+ * });
130
+ */
86
131
  remote(): ImageSchema<Src>;
132
+ /**
133
+ * Add a custom validation rule to this field.
134
+ *
135
+ * The function is called with the field's value and returns `false` when the
136
+ * value is fine, or a STRING with the message to show when it is not. Call it
137
+ * more than once to add more rules — they all run, and every message is
138
+ * reported.
139
+ *
140
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
141
+ * when the value is fine, and `true` is not one of the two answers.
142
+ *
143
+ * Validation runs in the Studio as you type, in `npx val validate` and
144
+ * before a publish.
145
+ *
146
+ * The second argument carries the `path` of the field being validated, for
147
+ * when the message needs to say where the problem is.
148
+ *
149
+ * @example
150
+ * const schema = s.image().validate((val) =>
151
+ * val.alt ? false : "Every image needs alt text",
152
+ * );
153
+ * export default c.define("/example.val.ts", schema, {
154
+ * path: "/public/val/example.png",
155
+ * width: 100,
156
+ * height: 100,
157
+ * mimeType: "image/png",
158
+ * alt: "An example",
159
+ * });
160
+ */
87
161
  validate(validationFunction: CustomValidateFunction<Src>): ImageSchema<Src>;
88
162
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
89
163
  /**
@@ -102,12 +176,40 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
102
176
  * instead of a preview row that navigates to it.
103
177
  *
104
178
  * Static configuration, not a callback — see `render.ts`.
179
+ *
180
+ * @example
181
+ * const schema = s.array(s.image().render({ as: "inline" }));
182
+ * export default c.define("/example.val.ts", schema, [
183
+ * {
184
+ * path: "/public/val/example.png",
185
+ * width: 100,
186
+ * height: 100,
187
+ * mimeType: "image/png",
188
+ * },
189
+ * ]);
105
190
  */
106
191
  render(input: FieldRender): ImageSchema<Src>;
107
192
  /**
108
193
  * How this VALUE is shown where a preview of it is needed — a row in a
109
194
  * sortable list, a reference dropdown, a search hit. Never how the field
110
195
  * itself is edited (that is `render`). See `preview.ts`.
196
+ *
197
+ * @example
198
+ * const schema = s.array(
199
+ * s.image().preview(({ val }) => ({
200
+ * title: val.alt ?? "Image",
201
+ * image: val,
202
+ * })),
203
+ * );
204
+ * export default c.define("/example.val.ts", schema, [
205
+ * {
206
+ * path: "/public/val/example.png",
207
+ * width: 100,
208
+ * height: 100,
209
+ * mimeType: "image/png",
210
+ * alt: "An example",
211
+ * },
212
+ * ]);
111
213
  */
112
214
  preview(select: ItemPreviewInput<Src>): ImageSchema<Src>;
113
215
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;