@valbuild/core 0.100.0 → 0.102.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.
@@ -21,7 +21,7 @@ export type ExtractedValModules = {
21
21
  * Extracts schemas and sources from a ValModules registry and computes the
22
22
  * deterministic SHAs that the server and client both use to detect changes.
23
23
  *
24
- * Used by ValOps on the server and by ValSyncEngine on the client so the
24
+ * Used by ValOps on the server and by `HostStore` on the client so the
25
25
  * hash algorithm has a single source of truth.
26
26
  */
27
27
  export declare function extractValModules(valModules: ValModules): Promise<ExtractedValModules>;
@@ -3,6 +3,7 @@ export { modules, type ValModules } from "./modules.js";
3
3
  export { extractValModules, type ExtractedValModules, type ExtractedModuleError, } from "./extractValModules.js";
4
4
  export type { InitVal, ValConfig, ValConstructor, ContentConstructor, } from "./initVal.js";
5
5
  export { Schema, type SerializedSchema, type SelectorOfSchema } from "./schema/index.js";
6
+ export { hasRemoteFileSchema } from "./schema/hasRemoteFileSchema.js";
6
7
  export type { ImageMetadata } from "./schema/image.js";
7
8
  export type { FileMetadata } from "./schema/file.js";
8
9
  export type { ValModule, SerializedModule, InferValModuleType } from "./module.js";
@@ -41,6 +42,7 @@ import { getValidationBasis, getValidationHash } from "./remote/validationBasis.
41
42
  import { getFileHash, hashToRemoteFileHash } from "./remote/fileHash.js";
42
43
  import { splitRemoteRef } from "./remote/splitRemoteRef.js";
43
44
  import { convertRemoteSource } from "./schema/remote.js";
45
+ import { colorToHex, convertColor, detectColorFormat, formatColor, parseColor } from "./schema/colorFormat.js";
44
46
  export { type SerializedArraySchema, ArraySchema } from "./schema/array.js";
45
47
  export { type SerializedObjectSchema, ObjectSchema } from "./schema/object.js";
46
48
  export { type SerializedRecordSchema, RecordSchema } from "./schema/record.js";
@@ -50,6 +52,8 @@ export { type SerializedBooleanSchema, BooleanSchema } from "./schema/boolean.js
50
52
  export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
51
53
  export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
52
54
  export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
55
+ export { type SerializedColorSchema, type ColorOptions, ColorSchema, } from "./schema/color.js";
56
+ export { type ColorFormat, type ParsedColor, COLOR_FORMATS, DEFAULT_COLOR_FORMAT, } from "./schema/colorFormat.js";
53
57
  export { type SerializedDateTimeSchema, DateTimeSchema, } from "./schema/datetime.js";
54
58
  export { type SerializedKeyOfSchema, KeyOfSchema } from "./schema/keyOf.js";
55
59
  export { type SerializedRouteSchema, RouteSchema } from "./schema/route.js";
@@ -57,7 +61,7 @@ export { type SerializedRichTextSchema, RichTextSchema, } from "./schema/richtex
57
61
  export { type SerializedUnionSchema, UnionSchema, type SerializedStringUnionSchema, type SerializedObjectUnionSchema, } from "./schema/union.js";
58
62
  export { type SerializedLiteralSchema, LiteralSchema } from "./schema/literal.js";
59
63
  export { deserializeSchema } from "./schema/deserialize.js";
60
- export { type ListRecordRender, type ListArrayRender, type ReifiedRender, type CodeLanguage, type CodeRender, } from "./render.js";
64
+ export { type ListRecordRender, type ListArrayRender, type ReifiedRender, type RenderScope, type CodeLanguage, type CodeRender, renderScope, } from "./render.js";
61
65
  export type { ValRouter, RouteValidationError } from "./router.js";
62
66
  export { getSourcePathFromRoute } from "./getSourcePathFromRoute.js";
63
67
  export declare const FATAL_ERROR_TYPES: readonly ["no-schema", "no-source", "invalid-id", "no-module", "invalid-patch"];
@@ -79,6 +83,13 @@ declare const Internal: {
79
83
  joinModuleFilePathAndModulePath: typeof joinModuleFilePathAndModulePath;
80
84
  nextAppRouter: import("./router.js").ValRouter;
81
85
  externalPageRouter: import("./router.js").ValRouter;
86
+ color: {
87
+ parseColor: typeof parseColor;
88
+ formatColor: typeof formatColor;
89
+ convertColor: typeof convertColor;
90
+ detectColorFormat: typeof detectColorFormat;
91
+ colorToHex: typeof colorToHex;
92
+ };
82
93
  remote: {
83
94
  createRemoteRef: typeof createRemoteRef;
84
95
  getValidationBasis: typeof getValidationBasis;
@@ -13,6 +13,7 @@ import { file } from "./schema/file.js";
13
13
  import { files } from "./schema/files.js";
14
14
  import { date } from "./schema/date.js";
15
15
  import { datetime } from "./schema/datetime.js";
16
+ import { color } from "./schema/color.js";
16
17
  import { route } from "./schema/route.js";
17
18
  import { router } from "./schema/router.js";
18
19
  import { images } from "./schema/images.js";
@@ -173,6 +174,25 @@ export type InitSchema = {
173
174
  *
174
175
  */
175
176
  readonly datetime: typeof datetime;
177
+ /**
178
+ * Define a color.
179
+ *
180
+ * Stored as a CSS color string, so it can be used directly in `style`
181
+ * attributes or set as a CSS custom property.
182
+ *
183
+ * The notation is decided by the `format` option, which defaults to `"hsl"`.
184
+ * Set `alpha: true` to allow transparency.
185
+ *
186
+ * @example
187
+ * const schema = s.color();
188
+ * export default c.define("/example.val.ts", schema, "hsl(217.22 91.22% 59.8%)");
189
+ *
190
+ * @example
191
+ * const schema = s.color({ format: "hex" });
192
+ * export default c.define("/example.val.ts", schema, "#3b82f6");
193
+ *
194
+ */
195
+ readonly color: typeof color;
176
196
  /**
177
197
  * Define a string that references a route path in your application.
178
198
  *
@@ -283,9 +303,10 @@ export declare function initSchema(): {
283
303
  }>, import("./schema/index.js").Schema<string>, Record<string, import("./schema/files.js").FilesEntryMetadata>>;
284
304
  date: (options?: Record<string, never>) => import("./schema/date.js").DateSchema<import("./schema/string.js").RawString>;
285
305
  datetime: (options?: Record<string, never>) => import("./schema/datetime.js").DateTimeSchema<import("./schema/string.js").RawString>;
306
+ color: (options?: import("./schema/color.js").ColorOptions) => import("./schema/color.js").ColorSchema<import("./schema/string.js").RawString>;
286
307
  route: <T extends string>(options?: Record<string, never>) => import("./schema/route.js").RouteSchema<T>;
287
308
  router: typeof router;
288
- images: <Accept extends `image/${string}`>(options?: import("./schema/images.js").ImagesOptions<Accept>) => import("./schema/record.js").RecordSchema<import("./schema/object.js").ObjectSchema<{
309
+ images: <Accept extends `image/${string}`>(options: import("./schema/images.js").ImagesOptions<Accept>) => import("./schema/record.js").RecordSchema<import("./schema/object.js").ObjectSchema<{
289
310
  width: import("./schema/number.js").NumberSchema<number>;
290
311
  height: import("./schema/number.js").NumberSchema<number>;
291
312
  mimeType: import("./schema/string.js").StringSchema<string>;
@@ -19,11 +19,25 @@ export type ListRecordRender = {
19
19
  export type ListArrayRender = {
20
20
  layout: "list";
21
21
  parent: "array";
22
- items: {
23
- title: string;
24
- subtitle?: string | null;
25
- image?: ImageSource | RemoteSource<ImageMetadata> | null;
26
- }[];
22
+ /**
23
+ * The rendered items, each PAIRED WITH ITS INDEX — the same
24
+ * `[key, value][]` shape {@link ListRecordRender} uses.
25
+ *
26
+ * Not a positionally-indexed array, and the reason is
27
+ * {@link RenderScope}: a render computed for a subset of paths carries only
28
+ * those items, so `items[i]` would silently read the wrong row. Carrying the
29
+ * index makes a windowed render impossible to misread — a consumer looks its
30
+ * index up rather than trusting a position — and makes the array and record
31
+ * shapes symmetric.
32
+ */
33
+ items: [
34
+ index: number,
35
+ value: {
36
+ title: string;
37
+ subtitle?: string | null;
38
+ image?: ImageSource | RemoteSource<ImageMetadata> | null;
39
+ }
40
+ ][];
27
41
  };
28
42
  export type TextareaRender = {
29
43
  layout: "textarea";
@@ -45,5 +59,42 @@ type WithStatus<T> = {
45
59
  data: T;
46
60
  };
47
61
  export type ReifiedRender = Record<SourcePath | ModuleFilePath, WithStatus<RenderTypes>>;
62
+ /**
63
+ * Which paths a render is being computed FOR.
64
+ *
65
+ * `executeRender` takes a whole module, so one request has always walked every
66
+ * node in it and run every `select` closure — for `handboka`, with `select` at
67
+ * two nested array levels, that is every chapter and every section to serve one
68
+ * visible row. A scope is what makes the walk proportional to what is being
69
+ * looked at instead of to the project.
70
+ *
71
+ * Two questions, because a container and its items need different answers:
72
+ *
73
+ * - {@link RenderScope.wants} — is a render AT this exact path wanted? A
74
+ * container answers `true` here when the whole of it is being shown, and its
75
+ * list render is then computed in full.
76
+ * - {@link RenderScope.wantsUnder} — could anything at or below this path be
77
+ * wanted? Recursion is pruned where this is `false`, and a container whose own
78
+ * path is not wanted but which has wanted descendants renders a WINDOW: only
79
+ * the items that were asked for. That is the case a single visible row is, and
80
+ * it is why {@link ListArrayRender} carries indices.
81
+ *
82
+ * Absent scope means the whole module, which is what every existing caller
83
+ * passes and what every existing caller got.
84
+ */
85
+ export type RenderScope = {
86
+ wants(path: SourcePath | ModuleFilePath): boolean;
87
+ wantsUnder(path: SourcePath | ModuleFilePath): boolean;
88
+ };
89
+ /**
90
+ * A scope covering exactly `paths` and their subtrees.
91
+ *
92
+ * Compares SEGMENTS rather than string prefixes. A source path's module path is
93
+ * a quoted, dot-joined encoding (`?p="a".1."b"`), so `startsWith` gets the
94
+ * ancestor test wrong wherever a key is a prefix of a sibling key or contains a
95
+ * dot or a quote — `"title"` against `"titles"` being the cheapest example.
96
+ * Segments are parsed once here, not per comparison.
97
+ */
98
+ export declare function renderScope(paths: readonly SourcePath[]): RenderScope;
48
99
  export type RenderSelector<T extends Schema<SelectorSource>> = T extends Schema<infer S> ? S : never;
49
100
  export {};
@@ -1,5 +1,5 @@
1
1
  import { Schema, SchemaAssertResult, SelectorOfSchema, SerializedSchema } from "./index.js";
2
- import { RenderSelector, ReifiedRender } from "../render.js";
2
+ import { RenderSelector, ReifiedRender, RenderScope } from "../render.js";
3
3
  import { SelectorSource } from "../selector/index.js";
4
4
  import { ImageSource } from "../source/image.js";
5
5
  import { ModuleFilePath, SourcePath } from "../val/index.js";
@@ -8,6 +8,16 @@ export type SerializedArraySchema = {
8
8
  type: "array";
9
9
  item: SerializedSchema;
10
10
  opt: boolean;
11
+ /**
12
+ * Set when this schema declares a `render`.
13
+ *
14
+ * The render itself cannot be serialized — `select` is a user closure — but
15
+ * whether one EXISTS can be, and that is worth carrying: it lets the non-host
16
+ * side skip asking the host to render a module that cannot produce one.
17
+ * Measured in a browser: mounting 260 fields across 141 modules spent ~2.3ms
18
+ * of 3.1ms calling `executeRender` on modules that returned nothing.
19
+ */
20
+ render?: true;
11
21
  customValidate?: boolean;
12
22
  readonly?: boolean;
13
23
  hidden?: boolean;
@@ -41,7 +51,7 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
41
51
  hidden(): ArraySchema<T, Src>;
42
52
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
43
53
  protected executeSerialize(): SerializedArraySchema;
44
- protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src): ReifiedRender;
54
+ protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: RenderScope): ReifiedRender;
45
55
  render(input: ArrayRenderInput<T>): ArraySchema<T, Src>;
46
56
  }
47
57
  export declare const array: <S extends Schema<SelectorSource>>(schema: S) => ArraySchema<S, SelectorOfSchema<S>[]>;
@@ -0,0 +1,57 @@
1
+ import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
+ import { ReifiedRender } from "../render.js";
3
+ import { SourcePath } from "../val/index.js";
4
+ import { ColorFormat } from "./colorFormat.js";
5
+ import { RawString } from "./string.js";
6
+ import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
7
+ export type { ColorFormat } from "./colorFormat.js";
8
+ export type ColorOptions = {
9
+ /**
10
+ * The CSS notation the color is stored in.
11
+ *
12
+ * Defaults to `"hsl"`.
13
+ *
14
+ * @example
15
+ * "hex" // #3b82f6
16
+ * "rgb" // rgb(59 130 246)
17
+ * "hsl" // hsl(217.22 91.22% 59.8%)
18
+ * "oklch" // oklch(0.6231 0.188 259.81)
19
+ */
20
+ format?: ColorFormat;
21
+ /**
22
+ * Allow an alpha channel (transparency).
23
+ *
24
+ * Defaults to `false`: a color with an alpha channel is a validation error
25
+ * unless this is enabled.
26
+ */
27
+ alpha?: boolean;
28
+ };
29
+ export type SerializedColorSchema = {
30
+ type: "color";
31
+ options?: ColorOptions;
32
+ opt: boolean;
33
+ customValidate?: boolean;
34
+ readonly?: boolean;
35
+ hidden?: boolean;
36
+ description?: string;
37
+ };
38
+ export declare class ColorSchema<Src extends string | null> extends Schema<Src> {
39
+ private readonly options?;
40
+ private readonly opt;
41
+ private readonly customValidateFunctions;
42
+ private readonly isReadonly;
43
+ private readonly isHidden;
44
+ private readonly description?;
45
+ constructor(options?: ColorOptions | undefined, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined);
46
+ describe(description: string | null): ColorSchema<Src>;
47
+ validate(validationFunction: (src: Src) => false | string): ColorSchema<Src>;
48
+ protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
49
+ protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
50
+ protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
51
+ nullable(): ColorSchema<Src | null>;
52
+ readonly(): ColorSchema<Src>;
53
+ hidden(): ColorSchema<Src>;
54
+ protected executeSerialize(): SerializedSchema;
55
+ protected executeRender(): ReifiedRender;
56
+ }
57
+ export declare const color: (options?: ColorOptions) => ColorSchema<RawString>;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Parsing / formatting of the CSS color strings stored by `s.color()`.
3
+ *
4
+ * Colors are stored as plain CSS strings so that they can be used directly in
5
+ * `style` attributes and CSS custom properties. The schema decides which family
6
+ * (`hex`, `rgb`, `hsl` or `oklch`) the string is written in, this module does
7
+ * the conversion between them.
8
+ *
9
+ * All colors are sRGB: `oklch` is only used as an output notation, so any
10
+ * `oklch` value that falls outside the sRGB gamut is clipped when converted
11
+ * back to the other formats.
12
+ */
13
+ export declare const COLOR_FORMATS: readonly ["hex", "rgb", "hsl", "oklch"];
14
+ export type ColorFormat = (typeof COLOR_FORMATS)[number];
15
+ export declare const DEFAULT_COLOR_FORMAT: ColorFormat;
16
+ /**
17
+ * A color, normalized to sRGB.
18
+ *
19
+ * `r`, `g` and `b` are 0-255 (not rounded), `a` is 0-1.
20
+ */
21
+ export type ParsedColor = {
22
+ r: number;
23
+ g: number;
24
+ b: number;
25
+ a: number;
26
+ };
27
+ /**
28
+ * Which color notation a string uses, or `null` if it is not a notation we
29
+ * recognize. This looks at the syntax only: `detectColorFormat` says nothing
30
+ * about whether the value actually parses (`hsl(nope)` is still `"hsl"`).
31
+ */
32
+ export declare function detectColorFormat(value: string): ColorFormat | null;
33
+ /**
34
+ * Parses a CSS color string into sRGB channels.
35
+ *
36
+ * Accepts hex (3, 4, 6 and 8 digits), `rgb()` / `rgba()`, `hsl()` / `hsla()`
37
+ * and `oklch()`, in both the legacy comma and the modern space syntax. Returns
38
+ * `null` if the value is not a color we can parse - notably: named colors
39
+ * (`red`), `color()`, `lab()` and `color-mix()` are not supported.
40
+ */
41
+ export declare function parseColor(value: string): ParsedColor | null;
42
+ export declare function rgbToHsl(r: number, g: number, b: number): {
43
+ h: number;
44
+ s: number;
45
+ l: number;
46
+ };
47
+ export declare function hslToRgb(h: number, s: number, l: number): {
48
+ r: number;
49
+ g: number;
50
+ b: number;
51
+ };
52
+ /**
53
+ * sRGB to Oklch, using Björn Ottosson's Oklab matrices.
54
+ */
55
+ export declare function rgbToOklch(r: number, g: number, b: number): {
56
+ l: number;
57
+ c: number;
58
+ h: number;
59
+ };
60
+ /**
61
+ * Oklch to sRGB. Out of gamut colors are clipped per channel.
62
+ */
63
+ export declare function oklchToRgb(l: number, c: number, h: number): {
64
+ r: number;
65
+ g: number;
66
+ b: number;
67
+ };
68
+ /**
69
+ * Serializes a color to the canonical string of the given format.
70
+ *
71
+ * The modern, space separated CSS syntax is used for the color functions
72
+ * (`rgb(255 0 0 / 0.5)`). Alpha is only included when it is not fully opaque.
73
+ */
74
+ export declare function formatColor(color: ParsedColor, format: ColorFormat): string;
75
+ /**
76
+ * Converts a color string to the canonical string of the given format, or
77
+ * `null` if the input cannot be parsed.
78
+ */
79
+ export declare function convertColor(value: string, format: ColorFormat): string | null;
80
+ /**
81
+ * `#rrggbb`, which is what `<input type="color">` uses as its value.
82
+ */
83
+ export declare function colorToHex(color: ParsedColor): string;
@@ -14,9 +14,13 @@ export type FilesOptions = {
14
14
  /**
15
15
  * The directory where files should be stored.
16
16
  * Must start with "/public" (e.g., "/public/val/files")
17
- * @default "/public/val"
17
+ *
18
+ * Required, and deliberately so: it decides where every uploaded file in this
19
+ * collection lands, and it used to default to "/public/val" — which meant a
20
+ * collection that had simply not said where it wanted its files silently shared
21
+ * a directory with every other one.
18
22
  */
19
- directory?: "/public" | `/public/${string}`;
23
+ directory: "/public" | `/public/${string}`;
20
24
  /**
21
25
  * Whether remote files are allowed
22
26
  * @default false
@@ -0,0 +1,34 @@
1
+ import type { SerializedSchema } from "./index.js";
2
+ /**
3
+ * Does this schema store any file remotely?
4
+ *
5
+ * The one answer to that question. There used to be two — `hasRemoteFileSchema`
6
+ * in the server, gating whether `/save` demands remote credentials, and
7
+ * `findRequiredRemoteFiles` in the Studio, gating the `/remote/settings` fetch —
8
+ * and they disagreed about `s.images({ remote: true })`: the Studio's counted a
9
+ * remote media record, the server's did not, because a media collection
10
+ * serializes as a `record` of metadata rather than as an image schema.
11
+ *
12
+ * The server's answer was the wrong one, and silently so. With it false,
13
+ * `/save` runs `saveOrUploadFiles` in `skip-remote` mode, and that mode does not
14
+ * merely skip the upload — it drops every remote descriptor without an error. The
15
+ * commit then lands a remote ref with no bytes behind it: a broken image, in the
16
+ * repository, with nothing anywhere saying why.
17
+ *
18
+ * ## Why it stays a walk over the SERIALIZED schema
19
+ *
20
+ * Both callers have a `SerializedSchema` and not a `Schema`: the server reads
21
+ * them out of `getSchemas()`, and the Studio is handed them by `host.receive`.
22
+ * Asking the schema classes directly would mean a method on every one of them,
23
+ * and the two places that need the answer cannot call it.
24
+ *
25
+ * ## Throwing
26
+ *
27
+ * The `never` assignment at the end is the point of the default branch: adding a
28
+ * schema type without teaching this function about it must not compile. The throw
29
+ * is what happens if one arrives anyway — which for a caller reading schemas out
30
+ * of its own process should be impossible. A caller that cannot afford to throw
31
+ * (the Studio, whose whole UI is downstream of this) catches it; returning `false`
32
+ * instead would reintroduce exactly the silent skip described above.
33
+ */
34
+ export declare function hasRemoteFileSchema(schema: SerializedSchema): boolean;
@@ -20,9 +20,13 @@ export type ImagesOptions<Accept extends `image/${string}`> = {
20
20
  /**
21
21
  * The directory where images should be stored.
22
22
  * Must start with "/public" (e.g., "/public/val/images")
23
- * @default "/public/val"
23
+ *
24
+ * Required, and deliberately so: it decides where every uploaded file in this
25
+ * collection lands, and it used to default to "/public/val" — which meant a
26
+ * gallery that had simply not said where it wanted its files silently shared a
27
+ * directory with every other one.
24
28
  */
25
- directory?: "/public" | `/public/${string}`;
29
+ directory: "/public" | `/public/${string}`;
26
30
  /**
27
31
  * Alt text schema. Can be:
28
32
  * - s.string() for required alt text
@@ -65,8 +69,9 @@ type ImagesItemSrc = {
65
69
  /**
66
70
  * Define a collection of images.
67
71
  *
68
- * All options are optional: `s.images()` accepts any image type (`"image/*"`) in
69
- * the default `/public/val` directory, with nullable alt text and remote disabled.
72
+ * `directory` is required — it decides where uploads land, so it is not something
73
+ * to be inferred. The rest defaults: any image type (`"image/*"`), nullable alt
74
+ * text, remote disabled.
70
75
  *
71
76
  * @example
72
77
  * ```typescript
@@ -85,5 +90,5 @@ type ImagesItemSrc = {
85
90
  * });
86
91
  * ```
87
92
  */
88
- export declare const images: <Accept extends `image/${string}`>(options?: ImagesOptions<Accept>) => RecordSchema<ObjectSchema<ImagesItemProps, ImagesItemSrc>, Schema<string>, Record<string, ImagesEntryMetadata>>;
93
+ export declare const images: <Accept extends `image/${string}`>(options: ImagesOptions<Accept>) => RecordSchema<ObjectSchema<ImagesItemProps, ImagesItemSrc>, Schema<string>, Record<string, ImagesEntryMetadata>>;
89
94
  export {};
@@ -12,14 +12,15 @@ import { SerializedRecordSchema } from "./record.js";
12
12
  import { SerializedRichTextSchema } from "./richtext.js";
13
13
  import { RawString, SerializedStringSchema } from "./string.js";
14
14
  import { SerializedUnionSchema } from "./union.js";
15
+ import { SerializedColorSchema } from "./color.js";
15
16
  import { SerializedDateSchema } from "./date.js";
16
17
  import { SerializedDateTimeSchema } from "./datetime.js";
17
18
  import { SerializedRouteSchema } from "./route.js";
18
19
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
19
20
  import { FileSource } from "../source/file.js";
20
21
  import { GenericRichTextSourceNode, RichTextSource } from "../source/richtext.js";
21
- import { ReifiedRender } from "../render.js";
22
- export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedRouteSchema | SerializedImageSchema;
22
+ import { ReifiedRender, RenderScope } from "../render.js";
23
+ export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedRouteSchema | SerializedImageSchema;
23
24
  type Primitives = number | string | boolean | null | FileSource;
24
25
  export type AssertError = {
25
26
  message: string;
@@ -105,7 +106,14 @@ export declare abstract class Schema<Src extends SelectorSource> {
105
106
  */
106
107
  abstract hidden(): Schema<Src>;
107
108
  protected abstract executeSerialize(): SerializedSchema;
108
- protected abstract executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src): ReifiedRender;
109
+ /**
110
+ * @param scope Which paths the caller needs a render for. Absent means the
111
+ * whole module, which is what every caller passed before scoping existed.
112
+ * See {@link RenderScope}: a container prunes recursion where nothing is
113
+ * wanted, and renders a WINDOW when its own path is not wanted but some of its
114
+ * items are — which is the single-visible-row case.
115
+ */
116
+ protected abstract executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: RenderScope): ReifiedRender;
109
117
  /** MUTATES! since internal and perf sensitive */
110
118
  protected appendValidationError(current: ValidationErrors, path: SourcePath, message: string, value: unknown, schemaError?: boolean): ValidationErrors;
111
119
  /**
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SelectorOfSchema, SerializedSchema } from "./index.js";
2
- import { ReifiedRender } from "../render.js";
2
+ import { ReifiedRender, RenderScope } from "../render.js";
3
3
  import { SelectorSource } from "../selector/index.js";
4
4
  import { ModuleFilePath, SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -52,7 +52,7 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
52
52
  hidden(): ObjectSchema<Props, Src>;
53
53
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
54
54
  protected executeSerialize(): SerializedSchema;
55
- protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src): ReifiedRender;
55
+ protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: RenderScope): ReifiedRender;
56
56
  }
57
57
  export declare const object: <Props extends ObjectSchemaProps>(schema: Props) => ObjectSchema<Props, ObjectSchemaSrcOf<Props>>;
58
58
  export {};
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SelectorOfSchema, SerializedSchema } from "./index.js";
2
- import { RenderSelector, ReifiedRender } from "../render.js";
2
+ import { RenderSelector, ReifiedRender, RenderScope } from "../render.js";
3
3
  import { ValRouter } from "../router.js";
4
4
  import { SelectorSource } from "../selector/index.js";
5
5
  import { ImageSource } from "../source/image.js";
@@ -20,6 +20,8 @@ export type SerializedRecordSchema = {
20
20
  item: SerializedSchema;
21
21
  key?: SerializedSchema;
22
22
  opt: boolean;
23
+ /** Set when this schema declares a `render`. See `SerializedArraySchema`. */
24
+ render?: true;
23
25
  router?: string;
24
26
  customValidate?: boolean;
25
27
  mediaType?: "files" | "images";
@@ -113,7 +115,7 @@ export declare class RecordSchema<T extends Schema<SelectorSource>, K extends Sc
113
115
  * defers).
114
116
  */
115
117
  validateJsonEntryContent(path: SourcePath, content: SelectorSource): ValidationErrors;
116
- protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src): ReifiedRender;
118
+ protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: RenderScope): ReifiedRender;
117
119
  render(input: {
118
120
  as: "list";
119
121
  select: (input: {
@@ -1,5 +1,5 @@
1
1
  import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { CodeLanguage, ReifiedRender } from "../render.js";
2
+ import { CodeLanguage, ReifiedRender, RenderScope } from "../render.js";
3
3
  import { ModuleFilePath, SourcePath } from "../val/index.js";
4
4
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
5
5
  type StringOptions = {
@@ -10,6 +10,8 @@ type StringOptions = {
10
10
  };
11
11
  export type SerializedStringSchema = {
12
12
  type: "string";
13
+ /** Set when this schema declares a `render`. See `SerializedArraySchema`. */
14
+ render?: true;
13
15
  options?: {
14
16
  maxLength?: number;
15
17
  minLength?: number;
@@ -73,7 +75,7 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
73
75
  as: "code";
74
76
  language: CodeLanguage;
75
77
  }): StringSchema<Src>;
76
- protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src): ReifiedRender;
78
+ protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, _scope?: RenderScope): ReifiedRender;
77
79
  }
78
80
  export declare const string: <T extends string>(options?: Record<string, never>) => StringSchema<T>;
79
81
  export {};
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ReifiedRender } from "../render.js";
2
+ import { ReifiedRender, RenderScope } from "../render.js";
3
3
  import { SelectorSource } from "../selector/index.js";
4
4
  import { SourceObject } from "../source/index.js";
5
5
  import { ModuleFilePath, SourcePath } from "../val/index.js";
@@ -57,7 +57,7 @@ export declare class UnionSchema<Key extends string | Schema<string>, T extends
57
57
  private matchedVariant;
58
58
  protected executeSerialize(): SerializedSchema;
59
59
  constructor(key: Key, items: T, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined);
60
- protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src): ReifiedRender;
60
+ protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: RenderScope): ReifiedRender;
61
61
  }
62
62
  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>>;
63
63
  export {};