@valbuild/core 0.102.0 → 0.105.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 (48) hide show
  1. package/dist/declarations/src/index.d.ts +13 -11
  2. package/dist/declarations/src/initSchema.d.ts +27 -14
  3. package/dist/declarations/src/initVal.d.ts +0 -6
  4. package/dist/declarations/src/module.d.ts +1 -4
  5. package/dist/declarations/src/preview.d.ts +98 -0
  6. package/dist/declarations/src/render.d.ts +29 -93
  7. package/dist/declarations/src/schema/array.d.ts +22 -23
  8. package/dist/declarations/src/schema/boolean.d.ts +2 -2
  9. package/dist/declarations/src/schema/color.d.ts +2 -2
  10. package/dist/declarations/src/schema/date.d.ts +2 -2
  11. package/dist/declarations/src/schema/datetime.d.ts +2 -2
  12. package/dist/declarations/src/schema/file.d.ts +19 -19
  13. package/dist/declarations/src/schema/image.d.ts +17 -8
  14. package/dist/declarations/src/schema/index.d.ts +7 -7
  15. package/dist/declarations/src/schema/keyOf.d.ts +2 -2
  16. package/dist/declarations/src/schema/literal.d.ts +2 -2
  17. package/dist/declarations/src/schema/number.d.ts +2 -2
  18. package/dist/declarations/src/schema/object.d.ts +2 -2
  19. package/dist/declarations/src/schema/record.d.ts +19 -31
  20. package/dist/declarations/src/schema/remote.d.ts +0 -10
  21. package/dist/declarations/src/schema/richtext.d.ts +2 -2
  22. package/dist/declarations/src/schema/route.d.ts +2 -2
  23. package/dist/declarations/src/schema/string.d.ts +28 -17
  24. package/dist/declarations/src/schema/union.d.ts +2 -2
  25. package/dist/declarations/src/selector/SelectorProxy.d.ts +9 -2
  26. package/dist/declarations/src/selector/index.d.ts +3 -7
  27. package/dist/declarations/src/source/file.d.ts +6 -24
  28. package/dist/declarations/src/source/index.d.ts +3 -7
  29. package/dist/declarations/src/source/json.d.ts +1 -1
  30. package/dist/declarations/src/source/media.d.ts +98 -0
  31. package/dist/declarations/src/source/remote.d.ts +0 -14
  32. package/dist/declarations/src/source/richtext.d.ts +5 -7
  33. package/dist/declarations/src/val/index.d.ts +2 -5
  34. package/dist/{index-30ecb49c.cjs.dev.js → index-730a5254.cjs.dev.js} +980 -901
  35. package/dist/{index-796fd204.esm.js → index-d7cc2231.esm.js} +980 -900
  36. package/dist/{index-ca1bbeb2.cjs.prod.js → index-e990b6ad.cjs.prod.js} +980 -901
  37. package/dist/{result-4bd63123.esm.js → result-daff1cae.esm.js} +1 -1
  38. package/dist/valbuild-core.cjs.dev.js +3 -4
  39. package/dist/valbuild-core.cjs.prod.js +3 -4
  40. package/dist/valbuild-core.esm.js +2 -2
  41. package/fp/dist/valbuild-core-fp.esm.js +1 -1
  42. package/package.json +1 -1
  43. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  44. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  45. package/patch/dist/valbuild-core-patch.esm.js +3 -3
  46. package/dist/declarations/src/selector/file.d.ts +0 -10
  47. package/dist/declarations/src/selector/image.d.ts +0 -3
  48. package/dist/declarations/src/source/image.d.ts +0 -19
@@ -8,18 +8,18 @@ export type { ImageMetadata } from "./schema/image.js";
8
8
  export type { FileMetadata } from "./schema/file.js";
9
9
  export type { ValModule, SerializedModule, InferValModuleType } from "./module.js";
10
10
  export type { SourceObject, SourcePrimitive, Source } from "./source/index.js";
11
- export type { FileSource } from "./source/file.js";
11
+ export type { FileSource } from "./source/media.js";
12
12
  export type { JsonSource, JsonOf, JsonImportThunk } from "./source/json.js";
13
- export type { RemoteSource, RemoteRef } from "./source/remote.js";
13
+ export type { RemoteRef } from "./source/remote.js";
14
14
  export { DEFAULT_VAL_REMOTE_HOST } from "./schema/remote.js";
15
15
  export type { RawString } from "./schema/string.js";
16
- export type { ImageSource } from "./source/image.js";
16
+ export type { ImageSource } from "./source/media.js";
17
+ export type { MediaHotspot, MediaSource, GalleryImageSource, GalleryFileSource, } from "./source/media.js";
17
18
  export type { AllRichTextOptions, Bold, Styles, HeadingNode, ImageNode, Italic, LineThrough, ListItemNode, LinkNode, OrderedListNode, ParagraphNode, BrNode, RichTextNode, RichTextOptions, SerializedRichTextOptions, RichTextSource, BlockNode, SpanNode, UnorderedListNode, } from "./source/richtext.js";
18
19
  export { type Val, type SerializedVal, type ModuleFilePath, type PatchId, type ModulePath, type SourcePath, type JsonOfSource, type ParentPatchId, } from "./val/index.js";
19
20
  export type { Json, JsonPrimitive, JsonArray, JsonObject } from "./Json.js";
20
21
  export type { ValidationError, ValidationErrors, } from "./schema/validation/ValidationError.js";
21
22
  export type { ValidationFix } from "./schema/validation/ValidationFix.js";
22
- export { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG } from "./source/file.js";
23
23
  export { VAL_EXTENSION, type SourceArray } from "./source/index.js";
24
24
  export { derefPatch } from "./patch/deref.js";
25
25
  export { type SelectorSource, type SelectorOf, GenericSelector, } from "./selector/index.js";
@@ -28,20 +28,18 @@ declare const ModuleFilePathSep = "?p=";
28
28
  export { ModuleFilePathSep };
29
29
  import { SelectorSource, getSchema } from "./selector/index.js";
30
30
  import { ModulePath, SourcePath, getValPath, isVal } from "./val/index.js";
31
- import { convertFileSource } from "./schema/file.js";
32
31
  import { createValPathOfItem } from "./selector/SelectorProxy.js";
33
32
  import { Operation } from "./patch/index.js";
34
33
  import { initSchema } from "./initSchema.js";
35
34
  import { getMimeType, mimeTypeToFileExt, filenameToMimeType } from "./mimeType/index.js";
36
35
  import { type ImageMetadata } from "./schema/image.js";
37
36
  import { type FileMetadata } from "./schema/file.js";
38
- import { isFile } from "./source/file.js";
39
37
  import { isJson, getJsonImport, resolveJsonValues } from "./source/json.js";
40
38
  import { createRemoteRef } from "./source/remote.js";
41
39
  import { getValidationBasis, getValidationHash } from "./remote/validationBasis.js";
42
40
  import { getFileHash, hashToRemoteFileHash } from "./remote/fileHash.js";
43
41
  import { splitRemoteRef } from "./remote/splitRemoteRef.js";
44
- import { convertRemoteSource } from "./schema/remote.js";
42
+ import { fillFromGallery, isRemoteMediaPath, mediaUrl, resolveMedia } from "./source/media.js";
45
43
  import { colorToHex, convertColor, detectColorFormat, formatColor, parseColor } from "./schema/colorFormat.js";
46
44
  export { type SerializedArraySchema, ArraySchema } from "./schema/array.js";
47
45
  export { type SerializedObjectSchema, ObjectSchema } from "./schema/object.js";
@@ -61,7 +59,8 @@ export { type SerializedRichTextSchema, RichTextSchema, } from "./schema/richtex
61
59
  export { type SerializedUnionSchema, UnionSchema, type SerializedStringUnionSchema, type SerializedObjectUnionSchema, } from "./schema/union.js";
62
60
  export { type SerializedLiteralSchema, LiteralSchema } from "./schema/literal.js";
63
61
  export { deserializeSchema } from "./schema/deserialize.js";
64
- export { type ListRecordRender, type ListArrayRender, type ReifiedRender, type RenderScope, type CodeLanguage, type CodeRender, renderScope, } from "./render.js";
62
+ export { type PreviewItem, type RecordPreview, type ArrayPreview, type ReifiedPreview, type PreviewScope, previewScope, } from "./preview.js";
63
+ export { type CodeLanguage, type StringRender, CODE_LANGUAGES } from "./render.js";
65
64
  export type { ValRouter, RouteValidationError } from "./router.js";
66
65
  export { getSourcePathFromRoute } from "./getSourcePathFromRoute.js";
67
66
  export declare const FATAL_ERROR_TYPES: readonly ["no-schema", "no-source", "invalid-id", "no-module", "invalid-patch"];
@@ -72,8 +71,12 @@ declare const Internal: {
72
71
  VERSION: {
73
72
  core: string | null;
74
73
  };
75
- convertFileSource: typeof convertFileSource;
76
- convertRemoteSource: typeof convertRemoteSource;
74
+ mediaUrl: typeof mediaUrl;
75
+ resolveMedia: typeof resolveMedia;
76
+ isRemoteMediaPath: typeof isRemoteMediaPath;
77
+ media: {
78
+ fillFromGallery: typeof fillFromGallery;
79
+ };
77
80
  getSchema: typeof getSchema;
78
81
  getValPath: typeof getValPath;
79
82
  getSource: typeof getSource;
@@ -100,7 +103,6 @@ declare const Internal: {
100
103
  };
101
104
  validate: (val: ValModule<SelectorSource>, path: SourcePath, src: unknown) => import("./schema/validation/ValidationError.js").ValidationErrors | undefined;
102
105
  isVal: typeof isVal;
103
- isFile: typeof isFile;
104
106
  isJson: typeof isJson;
105
107
  getJsonImport: typeof getJsonImport;
106
108
  resolveJsonValues: typeof resolveJsonValues;
@@ -99,19 +99,28 @@ export type InitSchema = {
99
99
  /**
100
100
  * Define an image.
101
101
  *
102
- * Use c.image to create an image source.
102
+ * An image is an object with a `path`. `width`, `height` and `mimeType` are
103
+ * read from the file — the VS Code extension fills them in as you type the
104
+ * path, and `npx val validate --fix` adds any that are missing. `alt` and
105
+ * `hotspot` are yours to set.
103
106
  *
104
107
  * @example
105
108
  * const schema = s.image();
106
- * export default c.define("/example.val.ts", schema, c.image("/public/val/example.png", {
107
- * width: 100,
108
- * height: 100,
109
- * mimeType: "image/png",
110
- * hotspot: {
111
- * x: 0.5,
112
- * y: 0.5
113
- * }
114
- * }));
109
+ * export default c.define("/example.val.ts", schema, {
110
+ * path: "/public/val/example.png",
111
+ * width: 100,
112
+ * height: 100,
113
+ * mimeType: "image/png",
114
+ * alt: "An example",
115
+ * hotspot: { x: 0.5, y: 0.5 },
116
+ * });
117
+ *
118
+ * @example
119
+ * // Backed by a gallery: width, height and mimeType live there.
120
+ * const schema = s.image(galleryVal);
121
+ * export default c.define("/example.val.ts", schema, {
122
+ * path: "/public/val/example.png",
123
+ * });
115
124
  *
116
125
  */
117
126
  readonly image: typeof image;
@@ -146,11 +155,15 @@ export type InitSchema = {
146
155
  /**
147
156
  * Define a file.
148
157
  *
149
- * Use `c.file` to create a file source.
158
+ * A file is an object with a `path`. `mimeType` comes from the extension —
159
+ * `npx val validate --fix` adds it.
150
160
  *
151
161
  * @example
152
162
  * const schema = s.file();
153
- * export default c.define("/example.val.ts", schema, c.file("/public/val/example.png"));
163
+ * export default c.define("/example.val.ts", schema, {
164
+ * path: "/public/val/example.pdf",
165
+ * mimeType: "application/pdf",
166
+ * });
154
167
  *
155
168
  */
156
169
  readonly file: typeof file;
@@ -291,11 +304,11 @@ export declare function initSchema(): {
291
304
  }) => import("./schema/number.js").NumberSchema<number>;
292
305
  union: <Key extends string | import("./schema/index.js").Schema<string>, T extends import("./schema/index.js").Schema<Key extends string ? import("./source/index.js").SourceObject & { [k in Key]: string; } : Key extends import("./schema/index.js").Schema<string> ? string : unknown>[]>(key: Key, ...objects: T) => import("./schema/union.js").UnionSchema<Key, T, T extends import("./schema/index.js").Schema<infer S extends import("./selector/index.js").SelectorSource>[] ? S extends import("./selector/index.js").SelectorSource ? S | (Key extends import("./schema/index.js").Schema<infer K extends import("./selector/index.js").SelectorSource> ? K : never) : never : never>;
293
306
  richtext: <O extends import("./index.js").RichTextOptions>(options?: O) => import("./schema/richtext.js").RichTextSchema<O, import("./index.js").RichTextSource<O>>;
294
- image: (options?: import("./schema/image.js").ImageOptions | import("./module.js").ValModule<Record<string, import("./schema/images.js").ImagesEntryMetadata>>) => import("./schema/image.js").ImageSchema<import("./index.js").ImageSource | import("./index.js").RemoteSource<import("./schema/image.js").ImageMetadata | undefined>>;
307
+ image: typeof image;
295
308
  literal: <T extends string>(value: T) => import("./schema/literal.js").LiteralSchema<T>;
296
309
  keyOf: <Src extends import("./selector/index.js").GenericSelector<import("./source/index.js").SourceObject> & import("./module.js").ValModuleBrand>(valModule: Src) => import("./schema/keyOf.js").KeyOfSchema<Src, Src extends import("./selector/index.js").GenericSelector<infer S extends import("./source/index.js").Source, undefined> ? S extends readonly import("./source/index.js").Source[] ? number : S extends import("./source/index.js").SourceObject ? string extends keyof S ? import("./schema/string.js").RawString : keyof S : S extends Record<string, import("./source/index.js").Source> ? import("./schema/string.js").RawString : never : never>;
297
310
  record: typeof record;
298
- file: (options?: import("./schema/file.js").FileOptions | import("./module.js").ValModule<Record<string, import("./schema/files.js").FilesEntryMetadata>>) => import("./schema/file.js").FileSchema<import("./index.js").FileSource | import("./index.js").RemoteSource<import("./schema/file.js").FileMetadata | undefined>>;
311
+ file: typeof file;
299
312
  files: (options: import("./schema/files.js").FilesOptions) => import("./schema/record.js").RecordSchema<import("./schema/object.js").ObjectSchema<{
300
313
  mimeType: import("./schema/string.js").StringSchema<string>;
301
314
  }, {
@@ -1,15 +1,9 @@
1
1
  import { define } from "./module.js";
2
2
  import { InitSchema } from "./initSchema.js";
3
3
  import { getValPath as getPath } from "./val/index.js";
4
- import { initFile } from "./source/file.js";
5
- import { initImage } from "./source/image.js";
6
4
  import { json } from "./source/json.js";
7
- import { initRemote } from "./source/remote.js";
8
5
  export type ContentConstructor = {
9
6
  define: typeof define;
10
- file: ReturnType<typeof initFile>;
11
- image: ReturnType<typeof initImage>;
12
- remote: ReturnType<typeof initRemote>;
13
7
  json: typeof json;
14
8
  };
15
9
  export type ValConstructor = {
@@ -4,22 +4,19 @@ import { Source } from "./source/index.js";
4
4
  import { ModuleFilePath, ModulePath, SourcePath } from "./val/index.js";
5
5
  import { Json } from "./Json.js";
6
6
  import { RawString } from "./schema/string.js";
7
- import { ImageSelector } from "./selector/image.js";
8
- import { ImageSource } from "./source/image.js";
9
7
  declare const brand: unique symbol;
10
8
  export type ValModule<T extends SelectorSource> = SelectorOf<T> & ValModuleBrand;
11
9
  export type ValModuleBrand = {
12
10
  [brand]: "ValModule";
13
11
  };
14
12
  export type InferValModuleType<T extends ValModule<SelectorSource>> = T extends GenericSelector<infer S> ? S : never;
15
- export type ReplaceRawStringWithString<T extends SelectorSource> = SelectorSource extends T ? T : T extends RawString ? string : T extends ImageSelector ? ImageSource : T extends {
13
+ export type ReplaceRawStringWithString<T extends SelectorSource> = SelectorSource extends T ? T : T extends RawString ? string : T extends {
16
14
  [key in string]: SelectorSource;
17
15
  } ? {
18
16
  [key in keyof T]: ReplaceRawStringWithString<T[key]>;
19
17
  } : T extends SelectorSource[] ? ReplaceRawStringWithString<T[number]>[] : T;
20
18
  export declare function define<T extends Schema<SelectorSource>>(id: string, // TODO: `/${string}`
21
19
  schema: T, source: ReplaceRawStringWithString<SelectorOfSchema<T>>): ValModule<SelectorOfSchema<T>>;
22
- export declare function enrichFileImageRemoteSourceWithMetadata<T extends SelectorSource>(source: T, schema: Schema<SelectorSource>): T;
23
20
  export declare function getSource<T extends Source>(valModule: ValModule<T>): T;
24
21
  export declare function splitModuleFilePathAndModulePath(path: SourcePath | ModuleFilePath): [moduleId: ModuleFilePath, path: ModulePath];
25
22
  export declare function joinModuleFilePathAndModulePath(moduleFilePath: ModuleFilePath, modulePath: ModulePath): SourcePath;
@@ -0,0 +1,98 @@
1
+ import { Schema } from "./schema/index.js";
2
+ import { SelectorSource } from "./selector/index.js";
3
+ import { ImageSource } from "./source/media.js";
4
+ import { ModuleFilePath, SourcePath } from "./val/index.js";
5
+ /**
6
+ * What a container shows for ONE of its items: the user's `preview` callback
7
+ * returns this, and the Studio draws a row from it.
8
+ *
9
+ * NB: {@link ArrayPreview} / {@link RecordPreview} name the DATA a container
10
+ * previews with. The Studio also has React components called `ArrayPreview` /
11
+ * `RecordPreview` (the fallback rendering of an array / record field, part of
12
+ * the `<Type>Preview` convention in `components/Preview.tsx`). They do not
13
+ * collide today because no file needs both - if one ever does, alias this type
14
+ * at the import rather than renaming that convention.
15
+ */
16
+ export type PreviewItem = {
17
+ title: string;
18
+ subtitle?: string | null;
19
+ image?: ImageSource | null;
20
+ };
21
+ export type RecordPreview = {
22
+ parent: "record";
23
+ items: [key: string, value: PreviewItem][];
24
+ };
25
+ export type ArrayPreview = {
26
+ parent: "array";
27
+ /**
28
+ * The previewed items, each PAIRED WITH ITS INDEX - the same
29
+ * `[key, value][]` shape {@link RecordPreview} uses.
30
+ *
31
+ * Not a positionally-indexed array, and the reason is
32
+ * {@link PreviewScope}: a preview computed for a subset of paths carries only
33
+ * those items, so `items[i]` would silently read the wrong row. Carrying the
34
+ * index makes a windowed preview impossible to misread - a consumer looks its
35
+ * index up rather than trusting a position - and makes the array and record
36
+ * shapes symmetric.
37
+ */
38
+ items: [index: number, value: PreviewItem][];
39
+ };
40
+ /**
41
+ * Main preview type.
42
+ *
43
+ * Deliberately not exported: `parent` is the discriminant, and every consumer
44
+ * narrows on it rather than on the union as a whole. There is no `layout` here
45
+ * on purpose - how a preview is laid out is the editor's business, not the
46
+ * schema's.
47
+ */
48
+ type PreviewTypes = RecordPreview | ArrayPreview;
49
+ type WithStatus<T> = {
50
+ status: "error";
51
+ message: string;
52
+ } | {
53
+ status: "loading";
54
+ data?: T;
55
+ } | {
56
+ status: "success";
57
+ data: T;
58
+ };
59
+ export type ReifiedPreview = Record<SourcePath | ModuleFilePath, WithStatus<PreviewTypes>>;
60
+ /**
61
+ * Which paths a preview is being computed FOR.
62
+ *
63
+ * `executePreview` takes a whole module, so one request has always walked every
64
+ * node in it and run every `preview` closure - for `handboka`, with `preview` at
65
+ * two nested array levels, that is every chapter and every section to serve one
66
+ * visible row. A scope is what makes the walk proportional to what is being
67
+ * looked at instead of to the project.
68
+ *
69
+ * Two questions, because a container and its items need different answers:
70
+ *
71
+ * - {@link PreviewScope.wants} - is a preview AT this exact path wanted? A
72
+ * container answers `true` here when the whole of it is being shown, and its
73
+ * preview is then computed in full.
74
+ * - {@link PreviewScope.wantsUnder} - could anything at or below this path be
75
+ * wanted? Recursion is pruned where this is `false`, and a container whose own
76
+ * path is not wanted but which has wanted descendants previews a WINDOW: only
77
+ * the items that were asked for. That is the case a single visible row is, and
78
+ * it is why {@link ArrayPreview} carries indices.
79
+ *
80
+ * Absent scope means the whole module, which is what every existing caller
81
+ * passes and what every existing caller got.
82
+ */
83
+ export type PreviewScope = {
84
+ wants(path: SourcePath | ModuleFilePath): boolean;
85
+ wantsUnder(path: SourcePath | ModuleFilePath): boolean;
86
+ };
87
+ /**
88
+ * A scope covering exactly `paths` and their subtrees.
89
+ *
90
+ * Compares SEGMENTS rather than string prefixes. A source path's module path is
91
+ * a quoted, dot-joined encoding (`?p="a".1."b"`), so `startsWith` gets the
92
+ * ancestor test wrong wherever a key is a prefix of a sibling key or contains a
93
+ * dot or a quote - `"title"` against `"titles"` being the cheapest example.
94
+ * Segments are parsed once here, not per comparison.
95
+ */
96
+ export declare function previewScope(paths: readonly SourcePath[]): PreviewScope;
97
+ export type PreviewSelector<T extends Schema<SelectorSource>> = T extends Schema<infer S> ? S : never;
98
+ export {};
@@ -1,100 +1,36 @@
1
- import { Schema } from "./schema/index.js";
2
- import { ImageMetadata } from "./schema/image.js";
3
- import { SelectorSource } from "./selector/index.js";
4
- import { ImageSource } from "./source/image.js";
5
- import { RemoteSource } from "./source/remote.js";
6
- import { ModuleFilePath, SourcePath } from "./val/index.js";
7
- export type ListRecordRender = {
8
- layout: "list";
9
- parent: "record";
10
- items: [
11
- key: string,
12
- value: {
13
- title: string;
14
- subtitle?: string | null;
15
- image?: ImageSource | RemoteSource<ImageMetadata> | null;
16
- }
17
- ][];
18
- };
19
- export type ListArrayRender = {
20
- layout: "list";
21
- parent: "array";
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
- ][];
41
- };
42
- export type TextareaRender = {
43
- layout: "textarea";
44
- };
45
- export type CodeLanguage = "typescript" | "javascript" | "javascriptreact" | "typescriptreact" | "json" | "java" | "html" | "css" | "xml" | "markdown" | "sql" | "python" | "rust" | "php" | "go" | "cpp" | "sass" | "vue" | "angular";
46
- export type CodeRender = {
47
- layout: "code";
48
- language: CodeLanguage;
49
- };
50
- type RenderTypes = ListRecordRender | ListArrayRender | TextareaRender | CodeRender;
51
- type WithStatus<T> = {
52
- status: "error";
53
- message: string;
54
- } | {
55
- status: "loading";
56
- data?: T;
57
- } | {
58
- status: "success";
59
- data: T;
60
- };
61
- export type ReifiedRender = Record<SourcePath | ModuleFilePath, WithStatus<RenderTypes>>;
62
1
  /**
63
- * Which paths a render is being computed FOR.
2
+ * A RENDER is how one field is laid out in the editor: `s.string().render({ as:
3
+ * "textarea" })`, `.render({ as: "code", language })`.
64
4
  *
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.
5
+ * It is static configuration - plain data, with no closure behind it and no
6
+ * dependency on source - which is why it lives in the SERIALIZED schema
7
+ * (`SerializedStringSchema.render`) and is read straight off it where the field
8
+ * is drawn. There is no render pipeline, no store and no host round-trip.
70
9
  *
71
- * Two questions, because a container and its items need different answers:
10
+ * That "static" is an ASSUMPTION we are taking deliberately, for simplicity,
11
+ * not a law. A future render could plausibly want to depend on source - a
12
+ * layout that varies with the value, a callback of its own. If that day comes,
13
+ * the shape it needs back is `executePreview`'s, and the honest move is to give
14
+ * `render` its own execute/store pair again. It is NOT to re-merge the two:
15
+ * conflating them is what this file was split up to undo.
72
16
  *
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.
17
+ * The dynamic, source-dependent, demand-scoped thing - what a container shows
18
+ * for its items - is a PREVIEW. See `preview.ts`.
84
19
  */
85
- export type RenderScope = {
86
- wants(path: SourcePath | ModuleFilePath): boolean;
87
- wantsUnder(path: SourcePath | ModuleFilePath): boolean;
88
- };
89
20
  /**
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.
21
+ * The list is the declaration and {@link CodeLanguage} is derived from it, so
22
+ * that a validator elsewhere (`shared`'s zod schema) can enumerate the same
23
+ * languages without a second copy that drifts. Same shape as `COLOR_FORMATS`.
24
+ */
25
+ export declare const CODE_LANGUAGES: readonly ["typescript", "javascript", "javascriptreact", "typescriptreact", "json", "java", "html", "css", "xml", "markdown", "sql", "python", "rust", "php", "go", "cpp", "sass", "vue", "angular"];
26
+ export type CodeLanguage = (typeof CODE_LANGUAGES)[number];
27
+ /**
28
+ * What `s.string().render(...)` takes, and what the serialized schema carries
29
+ * verbatim.
97
30
  */
98
- export declare function renderScope(paths: readonly SourcePath[]): RenderScope;
99
- export type RenderSelector<T extends Schema<SelectorSource>> = T extends Schema<infer S> ? S : never;
100
- export {};
31
+ export type StringRender = {
32
+ as: "textarea";
33
+ } | {
34
+ as: "code";
35
+ language: CodeLanguage;
36
+ };
@@ -1,7 +1,6 @@
1
1
  import { Schema, SchemaAssertResult, SelectorOfSchema, SerializedSchema } from "./index.js";
2
- import { RenderSelector, ReifiedRender, RenderScope } from "../render.js";
2
+ import { PreviewItem, PreviewSelector, ReifiedPreview, PreviewScope } from "../preview.js";
3
3
  import { SelectorSource } from "../selector/index.js";
4
- import { ImageSource } from "../source/image.js";
5
4
  import { ModuleFilePath, SourcePath } from "../val/index.js";
6
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
7
6
  export type SerializedArraySchema = {
@@ -9,30 +8,23 @@ export type SerializedArraySchema = {
9
8
  item: SerializedSchema;
10
9
  opt: boolean;
11
10
  /**
12
- * Set when this schema declares a `render`.
11
+ * Set when this schema declares a `preview`.
13
12
  *
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.
13
+ * The preview itself cannot be serialized — it is a user closure — but whether
14
+ * one EXISTS can be, and that is worth carrying: it lets the non-host side
15
+ * skip asking the host to preview a module that cannot produce one. Measured
16
+ * in a browser: mounting 260 fields across 141 modules spent ~2.3ms of 3.1ms
17
+ * calling `executePreview` on modules that returned nothing.
19
18
  */
20
- render?: true;
19
+ preview?: true;
21
20
  customValidate?: boolean;
22
21
  readonly?: boolean;
23
22
  hidden?: boolean;
24
23
  description?: string;
25
24
  };
26
- type ArrayRenderInput<T extends Schema<SelectorSource>> = {
27
- as: "list";
28
- select: (input: {
29
- val: RenderSelector<T>;
30
- }) => {
31
- title: string;
32
- subtitle?: string | null;
33
- image?: ImageSource | null;
34
- };
35
- };
25
+ type ArrayPreviewInput<T extends Schema<SelectorSource>> = (input: {
26
+ val: PreviewSelector<T>;
27
+ }) => PreviewItem;
36
28
  export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends SelectorOfSchema<T>[] | null> extends Schema<Src> {
37
29
  private readonly item;
38
30
  private readonly opt;
@@ -40,8 +32,8 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
40
32
  private readonly isReadonly;
41
33
  private readonly isHidden;
42
34
  private readonly description?;
43
- private readonly renderInput;
44
- constructor(item: T, opt?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: ArrayRenderInput<T> | null);
35
+ private readonly previewInput;
36
+ constructor(item: T, opt?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ArrayPreviewInput<T> | null);
45
37
  describe(description: string | null): ArraySchema<T, Src>;
46
38
  validate(validationFunction: (src: Src) => false | string): ArraySchema<T, Src>;
47
39
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
@@ -51,8 +43,15 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
51
43
  hidden(): ArraySchema<T, Src>;
52
44
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
53
45
  protected executeSerialize(): SerializedArraySchema;
54
- protected executeRender(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: RenderScope): ReifiedRender;
55
- render(input: ArrayRenderInput<T>): ArraySchema<T, Src>;
46
+ protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
47
+ /**
48
+ * What the editor shows for each ITEM of this array: a title, and optionally a
49
+ * subtitle and an image.
50
+ *
51
+ * `select` is your own code over the item's source, so it is run on demand for
52
+ * the rows that are actually being looked at — see `PreviewScope`.
53
+ */
54
+ preview(select: ArrayPreviewInput<T>): ArraySchema<T, Src>;
56
55
  }
57
56
  export declare const array: <S extends Schema<SelectorSource>>(schema: S) => ArraySchema<S, SelectorOfSchema<S>[]>;
58
57
  export {};
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ReifiedRender } from "../render.js";
2
+ import { ReifiedPreview } from "../preview.js";
3
3
  import { SourcePath } from "../val/index.js";
4
4
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
5
5
  export type SerializedBooleanSchema = {
@@ -26,6 +26,6 @@ export declare class BooleanSchema<Src extends boolean | null> extends Schema<Sr
26
26
  hidden(): BooleanSchema<Src>;
27
27
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
28
28
  protected executeSerialize(): SerializedSchema;
29
- protected executeRender(): ReifiedRender;
29
+ protected executePreview(): ReifiedPreview;
30
30
  }
31
31
  export declare const boolean: () => BooleanSchema<boolean>;
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ReifiedRender } from "../render.js";
2
+ import { ReifiedPreview } from "../preview.js";
3
3
  import { SourcePath } from "../val/index.js";
4
4
  import { ColorFormat } from "./colorFormat.js";
5
5
  import { RawString } from "./string.js";
@@ -52,6 +52,6 @@ export declare class ColorSchema<Src extends string | null> extends Schema<Src>
52
52
  readonly(): ColorSchema<Src>;
53
53
  hidden(): ColorSchema<Src>;
54
54
  protected executeSerialize(): SerializedSchema;
55
- protected executeRender(): ReifiedRender;
55
+ protected executePreview(): ReifiedPreview;
56
56
  }
57
57
  export declare const color: (options?: ColorOptions) => ColorSchema<RawString>;
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ReifiedRender } from "../render.js";
2
+ import { ReifiedPreview } from "../preview.js";
3
3
  import { SourcePath } from "../val/index.js";
4
4
  import { RawString } from "./string.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -47,7 +47,7 @@ export declare class DateSchema<Src extends string | null> extends Schema<Src> {
47
47
  hidden(): DateSchema<Src>;
48
48
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
49
49
  protected executeSerialize(): SerializedSchema;
50
- protected executeRender(): ReifiedRender;
50
+ protected executePreview(): ReifiedPreview;
51
51
  }
52
52
  export declare const date: (options?: Record<string, never>) => DateSchema<RawString>;
53
53
  export {};
@@ -1,5 +1,5 @@
1
1
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
- import { ReifiedRender } from "../render.js";
2
+ import { ReifiedPreview } from "../preview.js";
3
3
  import { SourcePath } from "../val/index.js";
4
4
  import { RawString } from "./string.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -51,7 +51,7 @@ export declare class DateTimeSchema<Src extends string | null> extends Schema<Sr
51
51
  hidden(): DateTimeSchema<Src>;
52
52
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
53
53
  protected executeSerialize(): SerializedSchema;
54
- protected executeRender(): ReifiedRender;
54
+ protected executePreview(): ReifiedPreview;
55
55
  }
56
56
  export declare const datetime: (options?: Record<string, never>) => DateTimeSchema<RawString>;
57
57
  export {};
@@ -1,10 +1,10 @@
1
- import { Json } from "../Json.js";
2
- import { FileSource } from "../source/file.js";
1
+ import { FileSource, GalleryFileSource } from "../source/media.js";
2
+ export type { FileMetadata } from "../source/file.js";
3
3
  import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
4
4
  import { ModulePath, SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
6
- import { RemoteSource, ValModule } from "../index.js";
7
- import { ReifiedRender } from "../render.js";
6
+ import { ValModule } from "../index.js";
7
+ import { ReifiedPreview } from "../preview.js";
8
8
  import { FilesEntryMetadata } from "./files.js";
9
9
  export type FileOptions = {
10
10
  accept?: string;
@@ -20,10 +20,7 @@ export type SerializedFileSchema = {
20
20
  hidden?: boolean;
21
21
  description?: string;
22
22
  };
23
- export type FileMetadata = {
24
- mimeType?: string;
25
- };
26
- export declare class FileSchema<Src extends FileSource<FileMetadata | undefined> | RemoteSource<FileMetadata | undefined> | null> extends Schema<Src> {
23
+ export declare class FileSchema<Src extends FileSource | null> extends Schema<Src> {
27
24
  private readonly options?;
28
25
  private readonly opt;
29
26
  protected readonly isRemote: boolean;
@@ -34,23 +31,26 @@ export declare class FileSchema<Src extends FileSource<FileMetadata | undefined>
34
31
  private readonly description?;
35
32
  constructor(options?: FileOptions | undefined, opt?: boolean, isRemote?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], moduleMetadata?: Record<ModulePath, Record<string, FilesEntryMetadata>>, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined);
36
33
  describe(description: string | null): FileSchema<Src>;
37
- remote(): FileSchema<Src | RemoteSource<FileMetadata | undefined>>;
34
+ remote(): FileSchema<Src>;
38
35
  validate(validationFunction: CustomValidateFunction<Src>): FileSchema<Src>;
39
36
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
37
+ /**
38
+ * The entries of the gallery this field points at, or null when it is a
39
+ * standalone field.
40
+ */
41
+ private galleryEntries;
40
42
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
41
43
  nullable(): FileSchema<Src | null>;
42
44
  readonly(): FileSchema<Src>;
43
45
  hidden(): FileSchema<Src>;
44
46
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
45
47
  protected executeSerialize(): SerializedSchema;
46
- protected executeRender(): ReifiedRender;
48
+ protected executePreview(): ReifiedPreview;
47
49
  }
48
- export declare const file: (options?: FileOptions | ValModule<Record<string, FilesEntryMetadata>>) => FileSchema<FileSource | RemoteSource<FileMetadata | undefined>>;
49
- export declare function convertFileSource<Metadata extends {
50
- readonly [key: string]: Json;
51
- } | undefined = {
52
- readonly [key: string]: Json;
53
- } | undefined>(src: FileSource<Metadata>): {
54
- url: string;
55
- metadata?: Metadata;
56
- };
50
+ /**
51
+ * A file picked from a gallery. Its mime type lives in the gallery, so the
52
+ * field carries only the path.
53
+ */
54
+ export declare function file(galleryModule: ValModule<Record<string, FilesEntryMetadata>>): FileSchema<GalleryFileSource>;
55
+ /** A file of its own, carrying its own mime type. */
56
+ export declare function file(options?: FileOptions): FileSchema<FileSource>;