@valbuild/core 0.125.0 → 0.126.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/declarations/src/index.d.ts +3 -1
  3. package/dist/declarations/src/initSchema.d.ts +46 -15
  4. package/dist/declarations/src/initVal.d.ts +108 -0
  5. package/dist/declarations/src/render.d.ts +5 -4
  6. package/dist/declarations/src/schema/array.d.ts +65 -0
  7. package/dist/declarations/src/schema/boolean.d.ts +44 -0
  8. package/dist/declarations/src/schema/code.d.ts +63 -0
  9. package/dist/declarations/src/schema/color.d.ts +54 -0
  10. package/dist/declarations/src/schema/date.d.ts +64 -2
  11. package/dist/declarations/src/schema/datetime.d.ts +87 -2
  12. package/dist/declarations/src/schema/discriminatedUnion.d.ts +166 -0
  13. package/dist/declarations/src/schema/enum.d.ts +122 -0
  14. package/dist/declarations/src/schema/file.d.ts +77 -0
  15. package/dist/declarations/src/schema/image.d.ts +102 -0
  16. package/dist/declarations/src/schema/index.d.ts +42 -2
  17. package/dist/declarations/src/schema/keyOf.d.ts +50 -0
  18. package/dist/declarations/src/schema/literal.d.ts +46 -0
  19. package/dist/declarations/src/schema/number.d.ts +58 -0
  20. package/dist/declarations/src/schema/object.d.ts +63 -0
  21. package/dist/declarations/src/schema/record.d.ts +136 -0
  22. package/dist/declarations/src/schema/richtext.d.ts +78 -0
  23. package/dist/declarations/src/schema/route.d.ts +44 -0
  24. package/dist/declarations/src/schema/string.d.ts +99 -0
  25. package/dist/declarations/src/schema/union.d.ts +54 -91
  26. package/dist/{index-a3846321.cjs.prod.js → index-6db78f3c.cjs.prod.js} +1953 -449
  27. package/dist/{index-daa122af.cjs.dev.js → index-b41b638f.cjs.dev.js} +1953 -449
  28. package/dist/{index-915dd675.esm.js → index-cd25c2dc.esm.js} +1952 -449
  29. package/dist/valbuild-core.cjs.dev.js +3 -2
  30. package/dist/valbuild-core.cjs.prod.js +3 -2
  31. package/dist/valbuild-core.esm.js +1 -1
  32. package/package.json +1 -1
  33. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  34. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  35. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -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;
@@ -1,93 +1,56 @@
1
- import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ItemPreviewInput, PreviewItem, ReifiedPreview, PreviewScope } from "../preview.js";
3
- import { FieldRender } from "../render.js";
1
+ import { Schema } from "./index.js";
4
2
  import { SelectorSource } from "../selector/index.js";
5
- import { SourceObject } from "../source/index.js";
6
- import { ModuleFilePath, SourcePath } from "../val/index.js";
7
- import { SerializedLiteralSchema } from "./literal.js";
8
- import { SerializedObjectSchema } from "./object.js";
9
- import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
10
- export type SerializedUnionSchema = SerializedStringUnionSchema | SerializedObjectUnionSchema;
11
- export type SerializedStringUnionSchema = {
12
- type: "union";
13
- /** Static layout config, carried whole in the serialized schema — see `render.ts`. */
14
- render?: FieldRender;
15
- /** Set when this schema declares a `preview`. The closure itself cannot serialize. */
16
- preview?: true;
17
- key: SerializedLiteralSchema;
18
- items: SerializedLiteralSchema[];
19
- opt: boolean;
20
- customValidate?: boolean;
21
- readonly?: boolean;
22
- hidden?: boolean;
23
- description?: string;
24
- };
25
- export type SerializedObjectUnionSchema = {
26
- type: "union";
27
- /** Static layout config, carried whole in the serialized schema — see `render.ts`. */
28
- render?: FieldRender;
29
- /** Set when this schema declares a `preview`. The closure itself cannot serialize. */
30
- preview?: true;
31
- key: string;
32
- items: SerializedObjectSchema[];
33
- opt: boolean;
34
- customValidate?: boolean;
35
- readonly?: boolean;
36
- hidden?: boolean;
37
- description?: string;
38
- };
39
- type SourceOf<Key extends string | Schema<string>, T extends Schema<Key extends string ? SourceObject & {
40
- [k in Key]: string;
41
- } : Key extends Schema<string> ? string : unknown>[]> = T extends Schema<infer S>[] ? S extends SelectorSource ? S | (Key extends Schema<infer K> ? K : never) : never : never;
42
- export declare class UnionSchema<Key extends string | Schema<string>, T extends Schema<Key extends string ? SourceObject & {
43
- [k in Key]: string;
44
- } : Key extends Schema<string> ? string : unknown>[], Src extends SourceOf<Key, T> | null> extends Schema<Src> {
45
- private readonly key;
46
- private readonly items;
47
- private readonly opt;
48
- private readonly customValidateFunctions;
49
- private readonly isReadonly;
50
- private readonly isHidden;
51
- private readonly description?;
52
- private readonly renderInput;
53
- private readonly previewInput;
54
- describe(description: string | null): UnionSchema<Key, T, Src>;
55
- validate(validationFunction: (src: Src) => false | string): UnionSchema<Key, T, Src>;
56
- protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
57
- protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
58
- nullable(): UnionSchema<Key, T, Src | null>;
59
- readonly(isReadonly?: boolean): UnionSchema<Key, T, Src>;
60
- hidden(isHidden?: boolean): UnionSchema<Key, T, Src>;
61
- protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
62
- /**
63
- * The variant schema a value takes, for a TAGGED union (string `key`). `null`
64
- * for a literal union (its variants are leaves with nothing to descend into) or
65
- * when the value matches none of them — a structural error `executeValidate`
66
- * already reports.
67
- */
68
- private matchedVariant;
69
- /**
70
- * How this field is laid out in the editor when it is the item of an array
71
- * or record: `{ as: "inline" }` renders the field itself inside each row,
72
- * instead of a preview row that navigates to it.
73
- *
74
- * Static configuration, not a callback — see `render.ts`.
75
- */
76
- render(input: FieldRender): UnionSchema<Key, T, Src>;
77
- /**
78
- * How this VALUE is shown where a preview of it is needed — a row in a
79
- * sortable list, a reference dropdown, a search hit. Never how the field
80
- * itself is edited (that is `render`). See `preview.ts`.
81
- *
82
- * Without one of its own, a tagged union previews as the VARIANT the value
83
- * takes — declare `preview` on the member objects and the union dispatches.
84
- */
85
- preview(select: ItemPreviewInput<Src>): UnionSchema<Key, T, Src>;
86
- protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
87
- protected declaresItemPreview(): boolean;
88
- protected executeSerialize(): SerializedSchema;
89
- constructor(key: Key, items: T, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
90
- protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
91
- }
92
- export declare const union: <Key extends string | Schema<string>, T extends Schema<Key extends string ? SourceObject & { [k in Key]: string; } : Key extends Schema<string> ? string : unknown>[]>(key: Key, ...objects: T) => UnionSchema<Key, T, SourceOf<Key, T>>;
3
+ import { DiscriminatedUnionItems, DiscriminatedUnionSchema, DiscriminatedUnionSourceOf, SerializedDiscriminatedUnionSchema } from "./discriminatedUnion.js";
4
+ import { EnumSchema, SerializedEnumSchema } from "./enum.js";
5
+ /**
6
+ * @deprecated Use {@link SerializedDiscriminatedUnionSchema} or
7
+ * {@link SerializedEnumSchema}. A serialized union is now one or the other,
8
+ * distinguished by `type` rather than by the shape of `key`.
9
+ */
10
+ export type SerializedUnionSchema = SerializedDiscriminatedUnionSchema | SerializedEnumSchema;
11
+ /** @deprecated Use {@link SerializedEnumSchema}. */
12
+ export type SerializedStringUnionSchema = SerializedEnumSchema;
13
+ /** @deprecated Use {@link SerializedDiscriminatedUnionSchema}. */
14
+ export type SerializedObjectUnionSchema = SerializedDiscriminatedUnionSchema;
15
+ /**
16
+ * @deprecated There is no single union schema class any more: name
17
+ * {@link DiscriminatedUnionSchema} or {@link EnumSchema}. The type parameters
18
+ * are kept so existing annotations still compile, but only `Src` is used.
19
+ */
20
+ export type UnionSchema<_Key = unknown, _T = unknown, Src extends SelectorSource = SelectorSource> = Schema<Src>;
21
+ /**
22
+ * The value type of a list of literal schemas — how `s.union` typed a union of
23
+ * literals. Inferred from `Schema<infer S>` rather than `LiteralSchema<infer S>`
24
+ * because a `LiteralSchema` is invariant in its source (its custom validate
25
+ * functions put it in a parameter position), so `LiteralSchema<"a">` is not
26
+ * assignable to `LiteralSchema<"a" | "b">`.
27
+ */
28
+ type EnumSourceOf<T extends Schema<string>[]> = T extends Schema<infer S>[] ? S extends string ? S : never : never;
29
+ /**
30
+ * @deprecated Use `s.discriminatedUnion(key, ...objects)` for a tagged union of
31
+ * objects, or `s.enum("a", "b")` for one of a fixed set of strings.
32
+ *
33
+ * One thing does NOT survive the literal form: an enum's values are strings,
34
+ * so a member literal's own `.validate()` and `.describe()` are dropped rather
35
+ * than carried over. `s.union(s.literal("a").validate(...), ...)` ran that
36
+ * validator as part of deciding whether the value matched; `s.enum` has no
37
+ * member to run. A validator on a single-valued literal could only accept or
38
+ * reject that one value, which the enum's own value list already decides — but
39
+ * if you rely on one, keep it by validating the enum itself.
40
+ *
41
+ * @example
42
+ * // was: s.union("type", s.object({ type: s.literal("a"), a: s.string() }))
43
+ * const schema = s.discriminatedUnion(
44
+ * "type",
45
+ * s.object({ type: s.literal("a"), a: s.string() }),
46
+ * );
47
+ * export default c.define("/example.val.ts", schema, { type: "a", a: "one" });
48
+ *
49
+ * @example
50
+ * // was: s.union(s.literal("a"), s.literal("b"))
51
+ * const schema = s.enum("a", "b");
52
+ * export default c.define("/example.val.ts", schema, "a");
53
+ */
54
+ export declare function union<Key extends string, T extends DiscriminatedUnionItems<Key>>(key: Key, ...objects: T): DiscriminatedUnionSchema<Key, T, DiscriminatedUnionSourceOf<Key, T>>;
55
+ export declare function union<T extends Schema<string>[]>(...literals: T): EnumSchema<EnumSourceOf<T>>;
93
56
  export {};