@valbuild/core 0.124.0 → 0.126.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/dist/declarations/src/getSourcePathFromRoute.d.ts +12 -3
  3. package/dist/declarations/src/index.d.ts +7 -3
  4. package/dist/declarations/src/initSchema.d.ts +46 -15
  5. package/dist/declarations/src/initVal.d.ts +108 -0
  6. package/dist/declarations/src/render.d.ts +5 -4
  7. package/dist/declarations/src/router.d.ts +45 -0
  8. package/dist/declarations/src/schema/array.d.ts +65 -0
  9. package/dist/declarations/src/schema/boolean.d.ts +44 -0
  10. package/dist/declarations/src/schema/code.d.ts +63 -0
  11. package/dist/declarations/src/schema/color.d.ts +54 -0
  12. package/dist/declarations/src/schema/date.d.ts +64 -2
  13. package/dist/declarations/src/schema/datetime.d.ts +87 -2
  14. package/dist/declarations/src/schema/discriminatedUnion.d.ts +166 -0
  15. package/dist/declarations/src/schema/enum.d.ts +122 -0
  16. package/dist/declarations/src/schema/file.d.ts +77 -0
  17. package/dist/declarations/src/schema/image.d.ts +102 -0
  18. package/dist/declarations/src/schema/index.d.ts +42 -2
  19. package/dist/declarations/src/schema/keyOf.d.ts +50 -0
  20. package/dist/declarations/src/schema/literal.d.ts +46 -0
  21. package/dist/declarations/src/schema/number.d.ts +58 -0
  22. package/dist/declarations/src/schema/object.d.ts +63 -0
  23. package/dist/declarations/src/schema/record.d.ts +136 -0
  24. package/dist/declarations/src/schema/richtext.d.ts +78 -0
  25. package/dist/declarations/src/schema/route.d.ts +44 -0
  26. package/dist/declarations/src/schema/string.d.ts +99 -0
  27. package/dist/declarations/src/schema/union.d.ts +54 -91
  28. package/dist/{index-22a40d42.cjs.prod.js → index-6db78f3c.cjs.prod.js} +2161 -423
  29. package/dist/{index-fcd087bd.cjs.dev.js → index-b41b638f.cjs.dev.js} +2161 -423
  30. package/dist/{index-9c14df37.esm.js → index-cd25c2dc.esm.js} +2155 -423
  31. package/dist/valbuild-core.cjs.dev.js +8 -2
  32. package/dist/valbuild-core.cjs.prod.js +8 -2
  33. package/dist/valbuild-core.esm.js +1 -1
  34. package/package.json +1 -1
  35. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  36. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  37. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -50,7 +50,52 @@ 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>;
@@ -64,12 +109,30 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
64
109
  * instead of a preview row that navigates to it.
65
110
  *
66
111
  * Static configuration, not a callback — see `render.ts`.
112
+ *
113
+ * @example
114
+ * // A page builder: each row edits the object in place, instead of
115
+ * // navigating to it.
116
+ * const block = s
117
+ * .object({ heading: s.string(), body: s.string() })
118
+ * .render({ as: "inline" });
119
+ * export default c.define("/example.val.ts", s.array(block), [
120
+ * { heading: "Hello", body: "World" },
121
+ * ]);
67
122
  */
68
123
  render(input: FieldRender): ObjectSchema<Props, Src>;
69
124
  /**
70
125
  * How this VALUE is shown where a preview of it is needed — a row in a
71
126
  * sortable list, a reference dropdown, a search hit. Never how the field
72
127
  * itself is edited (that is `render`). See `preview.ts`.
128
+ *
129
+ * @example
130
+ * const author = s
131
+ * .object({ name: s.string(), role: s.string() })
132
+ * .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
133
+ * export default c.define("/example.val.ts", s.array(author), [
134
+ * { name: "Ada", role: "Engineer" },
135
+ * ]);
73
136
  */
74
137
  preview(select: ItemPreviewInput<Src>): ObjectSchema<Props, Src>;
75
138
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
@@ -108,7 +108,48 @@ RO extends boolean = false> extends Schema<Src> {
108
108
  * sites.
109
109
  */
110
110
  externalLabel?: string | null);
111
+ /**
112
+ * Describe this field.
113
+ *
114
+ * The description is shown next to the field's label in the Val editor, so
115
+ * it is where you say what an editor needs to know but the field name cannot
116
+ * carry. It also travels in the serialized schema, which is what the AI
117
+ * assistant and the MCP tools read.
118
+ *
119
+ * Pass `null` to clear a description set earlier.
120
+ *
121
+ * @example
122
+ * const schema = s
123
+ * .record(s.string())
124
+ * .describe("Button labels, keyed by locale");
125
+ * export default c.define("/example.val.ts", schema, { en: "Read more" });
126
+ */
111
127
  describe(description: string | null): RecordSchema<T, K, Src>;
128
+ /**
129
+ * Add a custom validation rule to this field.
130
+ *
131
+ * The function is called with the field's value and returns `false` when the
132
+ * value is fine, or a STRING with the message to show when it is not. Call it
133
+ * more than once to add more rules — they all run, and every message is
134
+ * reported.
135
+ *
136
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
137
+ * when the value is fine, and `true` is not one of the two answers.
138
+ *
139
+ * Validation runs in the Studio as you type, in `npx val validate` and
140
+ * before a publish.
141
+ *
142
+ * The function is given the whole record. To validate one entry, put a
143
+ * `.validate(...)` on the ITEM schema instead — that reports the error on
144
+ * the entry's own row, and on a `.jsonValues()` record it does not force
145
+ * every entry to be loaded.
146
+ *
147
+ * @example
148
+ * const schema = s.record(s.string()).validate((val) =>
149
+ * "en" in val ? false : "There has to be an 'en' entry",
150
+ * );
151
+ * export default c.define("/example.val.ts", schema, { en: "Read more" });
152
+ */
112
153
  validate(validationFunction: (src: Src) => false | string): RecordSchema<T, K, Src>;
113
154
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
114
155
  private isRemoteUrl;
@@ -128,10 +169,55 @@ RO extends boolean = false> extends Schema<Src> {
128
169
  * must come BEFORE `.external()`: afterwards the value is a `Schema`, and
129
170
  * there is no `.readonly()` left to call — which is the error the compiler
130
171
  * gives, and a clearer one than a runtime throw.
172
+ *
173
+ * @example
174
+ * const schema = s.record(s.string()).readonly();
175
+ * export default c.define("/example.val.ts", schema, { en: "Read more" });
131
176
  */
132
177
  readonly<B extends boolean = true>(isReadonly?: B): RecordSchema<T, K, Src, B>;
133
178
  hidden(isHidden?: boolean): RecordSchema<T, K, Src>;
179
+ /**
180
+ * Turn this record into a page router: its keys are route paths of your
181
+ * application, and they are validated against the router's conventions.
182
+ *
183
+ * `s.router(router, item)` builds the same thing in one call and is what
184
+ * most code should use — this is for adding a router to a record that
185
+ * already exists.
186
+ *
187
+ * @example
188
+ * import { nextAppRouter } from "../val.config";
189
+ * const schema = s
190
+ * .record(s.object({ title: s.string() }))
191
+ * .router(nextAppRouter);
192
+ * export default c.define("/app/[slug]/page.val.ts", schema, {
193
+ * "/a-page-slug": { title: "First Page" },
194
+ * });
195
+ */
134
196
  router(router: ValRouter): RecordSchema<T, K, Src>;
197
+ /**
198
+ * Allow the files in this gallery to be stored on Val's remote content host
199
+ * instead of in your repository.
200
+ *
201
+ * For `s.images()` and `s.files()`, which are records of media. Remote is off
202
+ * until this is called.
203
+ *
204
+ * Each entry is then keyed by its URL on the content host rather than by a
205
+ * path under `/public`. That key is not something to write by hand: upload
206
+ * in the Studio, or add a local path and let `npx val validate --fix`
207
+ * upload it and rewrite the key.
208
+ *
209
+ * @example
210
+ * const schema = s.images({ directory: "/public/val/images" }).remote();
211
+ * export default c.define("/content/images.val.ts", schema, {
212
+ * "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/images/hero.webp":
213
+ * {
214
+ * width: 1920,
215
+ * height: 1080,
216
+ * mimeType: "image/webp",
217
+ * alt: "Hero image",
218
+ * },
219
+ * });
220
+ */
135
221
  remote(): RecordSchema<T, K, Src>;
136
222
  /**
137
223
  * Store each entry's value in its own lazily-loaded `*.val.json` file instead
@@ -146,6 +232,20 @@ RO extends boolean = false> extends Schema<Src> {
146
232
  * nested inside an object/array/record is rejected at startup with a module
147
233
  * error, because the single-entry fetch endpoint, the Studio's content
148
234
  * substitution and content validation are all root-only.
235
+ *
236
+ * Must come BEFORE `.validate()` and `.preview()`: those are typed against
237
+ * the un-lazy source shape, so a validator or preview declared first cannot
238
+ * be carried over, and declaring one first throws rather than silently
239
+ * dropping it.
240
+ *
241
+ * @example
242
+ * import { nextAppRouter } from "../val.config";
243
+ * const schema = s
244
+ * .router(nextAppRouter, s.object({ title: s.string(), body: s.string() }))
245
+ * .jsonValues();
246
+ * export default c.define("/app/support/[slug]/page.val.ts", schema, {
247
+ * "/support/faq": c.json(() => import("./content/faq.val.json")),
248
+ * });
149
249
  */
150
250
  jsonValues(): RecordSchema<T, K, JsonValuesRecordSrc<T, K>>;
151
251
  /**
@@ -166,6 +266,19 @@ RO extends boolean = false> extends Schema<Src> {
166
266
  * nested external record would leave a hole in the middle of a module's
167
267
  * source, and the Studio, patching and validation all assume a module's
168
268
  * source is complete apart from its entries.
269
+ *
270
+ * @example
271
+ * const schema = s.record(s.object({ title: s.string() })).external("posts");
272
+ * export default c.define("/content/posts.val.ts", schema, c.external());
273
+ *
274
+ * @example
275
+ * // A read-only external record: `.readonly()` must come FIRST, because
276
+ * // afterwards there is no `.readonly()` left on the type to call.
277
+ * const schema = s
278
+ * .record(s.object({ title: s.string() }))
279
+ * .readonly()
280
+ * .external("posts");
281
+ * export default c.define("/content/posts.val.ts", schema, c.external());
169
282
  */
170
283
  external<L extends string>(label: L): RecordSchema<T, K, ExternalRecordSrc<SelectorOfSchema<T>, L, RO>, RO>;
171
284
  /**
@@ -197,6 +310,25 @@ RO extends boolean = false> extends Schema<Src> {
197
310
  * is the item of another container, in search, in references. What its
198
311
  * entries show is the ITEM schema's `preview`, not this. Never how the field
199
312
  * is edited (that is `render`). See `preview.ts`.
313
+ *
314
+ * @example
315
+ * // The preview of the RECORD, for where it is an item of something else.
316
+ * // What its own rows show is the ITEM schema's preview — see below.
317
+ * const labels = s.record(s.string()).preview(({ val }) => ({
318
+ * title: `${Object.keys(val).length} locales`,
319
+ * }));
320
+ * export default c.define("/example.val.ts", s.array(labels), [
321
+ * { en: "Read more" },
322
+ * ]);
323
+ *
324
+ * @example
325
+ * // Rows of a record come from the ITEM's preview, not the record's:
326
+ * const author = s
327
+ * .object({ name: s.string(), role: s.string() })
328
+ * .preview(({ val }) => ({ title: val.name, subtitle: val.role }));
329
+ * export default c.define("/example.val.ts", s.record(author), {
330
+ * ada: { name: "Ada", role: "Engineer" },
331
+ * });
200
332
  */
201
333
  preview(select: ItemPreviewInput<Src>): RecordSchema<T, K, Src>;
202
334
  /**
@@ -205,6 +337,10 @@ RO extends boolean = false> extends Schema<Src> {
205
337
  * instead of a preview row that navigates to it.
206
338
  *
207
339
  * Static configuration, not a callback — see `render.ts`.
340
+ *
341
+ * @example
342
+ * const schema = s.array(s.record(s.string()).render({ as: "inline" }));
343
+ * export default c.define("/example.val.ts", schema, [{ en: "Read more" }]);
208
344
  */
209
345
  render(input: FieldRender): RecordSchema<T, K, Src>;
210
346
  }
@@ -31,9 +31,71 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
31
31
  private readonly renderInput;
32
32
  private readonly previewInput;
33
33
  constructor(options: O & ValidationOptions, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
34
+ /**
35
+ * Describe this field.
36
+ *
37
+ * The description is shown next to the field's label in the Val editor, so
38
+ * it is where you say what an editor needs to know but the field name cannot
39
+ * carry. It also travels in the serialized schema, which is what the AI
40
+ * assistant and the MCP tools read.
41
+ *
42
+ * Pass `null` to clear a description set earlier.
43
+ *
44
+ * @example
45
+ * const schema = s.richtext().describe("The body of the article");
46
+ * export default c.define("/example.val.ts", schema, [
47
+ * { tag: "p", children: ["Hello"] },
48
+ * ]);
49
+ */
34
50
  describe(description: string | null): RichTextSchema<O, Src>;
51
+ /**
52
+ * Validate that the rich text holds at most `max` characters.
53
+ *
54
+ * Counted over the TEXT, not the markup: tags, attributes and the structure
55
+ * of the document do not add to it.
56
+ *
57
+ * @example
58
+ * const schema = s.richtext().maxLength(280);
59
+ * export default c.define("/example.val.ts", schema, [
60
+ * { tag: "p", children: ["Hello"] },
61
+ * ]);
62
+ */
35
63
  maxLength(max: number): RichTextSchema<O, Src>;
64
+ /**
65
+ * Validate that the rich text holds at least `min` characters.
66
+ *
67
+ * Counted over the TEXT, not the markup: tags, attributes and the structure
68
+ * of the document do not add to it.
69
+ *
70
+ * @example
71
+ * const schema = s.richtext().minLength(20).maxLength(280);
72
+ * export default c.define("/example.val.ts", schema, [
73
+ * { tag: "p", children: ["Long enough to clear the minimum length."] },
74
+ * ]);
75
+ */
36
76
  minLength(min: number): RichTextSchema<O, Src>;
77
+ /**
78
+ * Add a custom validation rule to this field.
79
+ *
80
+ * The function is called with the field's value and returns `false` when the
81
+ * value is fine, or a STRING with the message to show when it is not. Call it
82
+ * more than once to add more rules — they all run, and every message is
83
+ * reported.
84
+ *
85
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
86
+ * when the value is fine, and `true` is not one of the two answers.
87
+ *
88
+ * Validation runs in the Studio as you type, in `npx val validate` and
89
+ * before a publish.
90
+ *
91
+ * @example
92
+ * const schema = s.richtext().validate((val) =>
93
+ * val.length > 0 ? false : "Write something",
94
+ * );
95
+ * export default c.define("/example.val.ts", schema, [
96
+ * { tag: "p", children: ["Hello"] },
97
+ * ]);
98
+ */
37
99
  validate(validationFunction: (src: Src) => false | string): RichTextSchema<O, Src>;
38
100
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
39
101
  private internalValidate;
@@ -49,12 +111,28 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
49
111
  * instead of a preview row that navigates to it.
50
112
  *
51
113
  * Static configuration, not a callback — see `render.ts`.
114
+ *
115
+ * @example
116
+ * const schema = s.array(s.richtext().render({ as: "inline" }));
117
+ * export default c.define("/example.val.ts", schema, [
118
+ * [{ tag: "p", children: ["Hello"] }],
119
+ * ]);
52
120
  */
53
121
  render(input: FieldRender): RichTextSchema<O, Src>;
54
122
  /**
55
123
  * How this VALUE is shown where a preview of it is needed — a row in a
56
124
  * sortable list, a reference dropdown, a search hit. Never how the field
57
125
  * itself is edited (that is `render`). See `preview.ts`.
126
+ *
127
+ * @example
128
+ * const schema = s.array(
129
+ * s.richtext().preview(({ val }) => ({
130
+ * title: val.length > 0 ? "Rich text" : "Empty",
131
+ * })),
132
+ * );
133
+ * export default c.define("/example.val.ts", schema, [
134
+ * [{ tag: "p", children: ["Hello"] }],
135
+ * ]);
58
136
  */
59
137
  preview(select: ItemPreviewInput<Src>): RichTextSchema<O, Src>;
60
138
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
@@ -40,6 +40,20 @@ export declare class RouteSchema<Src extends string | null> extends Schema<Src>
40
40
  private readonly renderInput;
41
41
  private readonly previewInput;
42
42
  constructor(options?: RouteOptions | undefined, opt?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
43
+ /**
44
+ * Describe this field.
45
+ *
46
+ * The description is shown next to the field's label in the Val editor, so
47
+ * it is where you say what an editor needs to know but the field name cannot
48
+ * carry. It also travels in the serialized schema, which is what the AI
49
+ * assistant and the MCP tools read.
50
+ *
51
+ * Pass `null` to clear a description set earlier.
52
+ *
53
+ * @example
54
+ * const schema = s.route().describe("Where the button goes");
55
+ * export default c.define("/example.val.ts", schema, "/a-page-slug");
56
+ */
43
57
  describe(description: string | null): RouteSchema<Src>;
44
58
  /**
45
59
  * Specify a pattern for which routes are allowed.
@@ -67,6 +81,26 @@ export declare class RouteSchema<Src extends string | null> extends Schema<Src>
67
81
  * s.route().include(/^\/api\//).exclude(/^\/api\/internal\//) // API routes except internal
68
82
  */
69
83
  exclude(pattern: RegExp): RouteSchema<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
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
93
+ * when the value is fine, and `true` is not one of the two answers.
94
+ *
95
+ * Validation runs in the Studio as you type, in `npx val validate` and
96
+ * before a publish.
97
+ *
98
+ * @example
99
+ * const schema = s.route().validate((val) =>
100
+ * val.endsWith("/") ? "Leave off the trailing slash" : false,
101
+ * );
102
+ * export default c.define("/example.val.ts", schema, "/a-page-slug");
103
+ */
70
104
  validate(validationFunction: (src: Src) => false | string): RouteSchema<Src>;
71
105
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
72
106
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
@@ -80,12 +114,22 @@ export declare class RouteSchema<Src extends string | null> extends Schema<Src>
80
114
  * instead of a preview row that navigates to it.
81
115
  *
82
116
  * Static configuration, not a callback — see `render.ts`.
117
+ *
118
+ * @example
119
+ * const schema = s.array(s.route().render({ as: "inline" }));
120
+ * export default c.define("/example.val.ts", schema, ["/a-page-slug"]);
83
121
  */
84
122
  render(input: FieldRender): RouteSchema<Src>;
85
123
  /**
86
124
  * How this VALUE is shown where a preview of it is needed — a row in a
87
125
  * sortable list, a reference dropdown, a search hit. Never how the field
88
126
  * itself is edited (that is `render`). See `preview.ts`.
127
+ *
128
+ * @example
129
+ * const schema = s.array(
130
+ * s.route().preview(({ val }) => ({ title: val })),
131
+ * );
132
+ * export default c.define("/example.val.ts", schema, ["/a-page-slug"]);
89
133
  */
90
134
  preview(select: ItemPreviewInput<Src>): RouteSchema<Src>;
91
135
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
@@ -66,24 +66,113 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
66
66
  private readonly previewInput;
67
67
  private readonly isMultiline;
68
68
  constructor(options?: StringOptions | undefined, opt?: boolean, isRaw?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], renderInput?: FieldRender | null, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null, isMultiline?: boolean);
69
+ /**
70
+ * Describe this field.
71
+ *
72
+ * The description is shown next to the field's label in the Val editor, so
73
+ * it is where you say what an editor needs to know but the field name cannot
74
+ * carry. It also travels in the serialized schema, which is what the AI
75
+ * assistant and the MCP tools read.
76
+ *
77
+ * Pass `null` to clear a description set earlier.
78
+ *
79
+ * @example
80
+ * const schema = s
81
+ * .string()
82
+ * .describe("Shown in the browser tab and in search results");
83
+ * export default c.define("/example.val.ts", schema, "Hello");
84
+ */
69
85
  describe(description: string | null): StringSchema<Src>;
70
86
  /**
71
87
  * @deprecated Use `minLength` instead
88
+ *
89
+ * @example
90
+ * const schema = s.string().minLength(2);
91
+ * export default c.define("/example.val.ts", schema, "Hi");
72
92
  */
73
93
  min(minLength: number): StringSchema<Src>;
94
+ /**
95
+ * Validate that the string is at least `minLength` characters long.
96
+ *
97
+ * @example
98
+ * const schema = s.string().minLength(2);
99
+ * export default c.define("/example.val.ts", schema, "Hi");
100
+ */
74
101
  minLength(minLength: number): StringSchema<Src>;
75
102
  /**
76
103
  * @deprecated Use `maxLength` instead
104
+ *
105
+ * @example
106
+ * const schema = s.string().maxLength(60);
107
+ * export default c.define("/example.val.ts", schema, "A title that fits");
77
108
  */
78
109
  max(maxLength: number): StringSchema<Src>;
110
+ /**
111
+ * Validate that the string is at most `maxLength` characters long.
112
+ *
113
+ * @example
114
+ * const schema = s.string().maxLength(60);
115
+ * export default c.define("/example.val.ts", schema, "A title that fits");
116
+ */
79
117
  maxLength(maxLength: number): StringSchema<Src>;
118
+ /**
119
+ * Validate that the string matches `regexp`.
120
+ *
121
+ * `message` is what the editor is shown when it does not match. Without one
122
+ * the error names the pattern, which is rarely what a non-developer needs.
123
+ *
124
+ * @example
125
+ * const schema = s
126
+ * .string()
127
+ * .regexp(/^[a-z0-9-]+$/, "Lower case letters, digits and dashes only");
128
+ * export default c.define("/example.val.ts", schema, "my-slug");
129
+ */
80
130
  regexp(regexp: RegExp, message?: string): StringSchema<Src>;
131
+ /**
132
+ * Add a custom validation rule to this field.
133
+ *
134
+ * The function is called with the field's value and returns `false` when the
135
+ * value is fine, or a STRING with the message to show when it is not. Call it
136
+ * more than once to add more rules — they all run, and every message is
137
+ * reported.
138
+ *
139
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
140
+ * when the value is fine, and `true` is not one of the two answers.
141
+ *
142
+ * Validation runs in the Studio as you type, in `npx val validate` and
143
+ * before a publish.
144
+ *
145
+ * @example
146
+ * const schema = s.string().validate((val) =>
147
+ * val.trim() === val ? false : "Must not start or end with a space",
148
+ * );
149
+ * export default c.define("/example.val.ts", schema, "Hello");
150
+ */
81
151
  validate(validationFunction: (src: Src) => false | string): StringSchema<Src>;
82
152
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
83
153
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
84
154
  nullable(): StringSchema<Src | null>;
85
155
  readonly(isReadonly?: boolean): StringSchema<Src>;
86
156
  hidden(isHidden?: boolean): StringSchema<Src>;
157
+ /**
158
+ * Do not stega encode this string.
159
+ *
160
+ * Val normally encodes an invisible pointer back to the source into every
161
+ * string it hands your app, which is what makes click-to-edit work. A raw
162
+ * string is handed over exactly as written — use it wherever the value is
163
+ * not rendered as visible text: a slug, a class name, an `id`, a URL,
164
+ * anything parsed or compared.
165
+ *
166
+ * @example
167
+ * const schema = s.object({
168
+ * title: s.string(),
169
+ * slug: s.string().raw(),
170
+ * });
171
+ * export default c.define("/example.val.ts", schema, {
172
+ * title: "Hello",
173
+ * slug: "hello",
174
+ * });
175
+ */
87
176
  raw(): StringSchema<Src extends null ? RawString | null : RawString>;
88
177
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
89
178
  protected executeSerialize(): SerializedSchema;
@@ -107,12 +196,22 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
107
196
  *
108
197
  * Static configuration, not a callback — see `render.ts`. What a CONTAINER
109
198
  * shows for its items is a `preview`, which is a different thing entirely.
199
+ *
200
+ * @example
201
+ * const schema = s.array(s.string().render({ as: "inline" }));
202
+ * export default c.define("/example.val.ts", schema, ["First", "Second"]);
110
203
  */
111
204
  render(input: FieldRender): StringSchema<Src>;
112
205
  /**
113
206
  * How this VALUE is shown where a preview of it is needed — a row in a
114
207
  * sortable list, a reference dropdown, a search hit. Never how the field
115
208
  * itself is edited (that is `render`). See `preview.ts`.
209
+ *
210
+ * @example
211
+ * const schema = s.array(
212
+ * s.string().preview(({ val }) => ({ title: val })),
213
+ * );
214
+ * export default c.define("/example.val.ts", schema, ["First", "Second"]);
116
215
  */
117
216
  preview(select: ItemPreviewInput<Src>): StringSchema<Src>;
118
217
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;