@valbuild/core 0.139.1 → 0.143.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.
- package/CHANGELOG.md +96 -0
- package/dist/declarations/src/getSha256.d.ts +55 -0
- package/dist/declarations/src/index.d.ts +25 -4
- package/dist/declarations/src/initSchema.d.ts +106 -55
- package/dist/declarations/src/initVal.d.ts +25 -2
- package/dist/declarations/src/mimeType/font.d.ts +36 -0
- package/dist/declarations/src/mimeType/index.d.ts +1 -0
- package/dist/declarations/src/remote/validationBasis.d.ts +3 -2
- package/dist/declarations/src/schema/file.d.ts +7 -0
- package/dist/declarations/src/schema/font.d.ts +16 -0
- package/dist/declarations/src/schema/fontset.d.ts +40 -0
- package/dist/declarations/src/schema/image.d.ts +9 -2
- package/dist/declarations/src/schema/index.d.ts +2 -1
- package/dist/declarations/src/schema/record.d.ts +16 -2
- package/dist/declarations/src/schema/richtext.d.ts +20 -1
- package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
- package/dist/declarations/src/schema/video.d.ts +249 -0
- package/dist/declarations/src/schema/videoset.d.ts +141 -0
- package/dist/declarations/src/source/galleries.d.ts +20 -0
- package/dist/declarations/src/source/media.d.ts +172 -12
- package/dist/declarations/src/val/index.d.ts +4 -2
- package/dist/{index-eb9f6480.esm.js → index-02a1ba06.esm.js} +1708 -150
- package/dist/{index-7d334094.cjs.prod.js → index-46c72f01.cjs.prod.js} +1713 -149
- package/dist/{index-90000f7a.cjs.dev.js → index-aac8c69c.cjs.dev.js} +1713 -149
- package/dist/valbuild-core.cjs.dev.js +7 -1
- package/dist/valbuild-core.cjs.prod.js +7 -1
- package/dist/valbuild-core.esm.js +1 -1
- package/package.json +1 -1
- package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
- package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
- package/patch/dist/valbuild-core-patch.esm.js +2 -2
|
@@ -4,6 +4,7 @@ import { SerializedArraySchema } from "./array.js";
|
|
|
4
4
|
import { SerializedBooleanSchema } from "./boolean.js";
|
|
5
5
|
import { SerializedFileSchema } from "./file.js";
|
|
6
6
|
import { SerializedImageSchema } from "./image.js";
|
|
7
|
+
import { SerializedVideoSchema } from "./video.js";
|
|
7
8
|
import { SerializedKeyOfSchema } from "./keyOf.js";
|
|
8
9
|
import { SerializedValViewSchema } from "./view.js";
|
|
9
10
|
import { SerializedLiteralSchema } from "./literal.js";
|
|
@@ -25,7 +26,7 @@ import { ValidationError, ValidationErrors } from "./validation/ValidationError.
|
|
|
25
26
|
import { FileSource } from "../source/media.js";
|
|
26
27
|
import { GenericRichTextSourceNode, RichTextSource } from "../source/richtext.js";
|
|
27
28
|
import { ReifiedPreview, PreviewScope, PreviewItem } from "../preview.js";
|
|
28
|
-
export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedDiscriminatedUnionSchema | SerializedEnumSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedLocaleSchema | SerializedSettingsSchema | SerializedImageSchema | SerializedValViewSchema;
|
|
29
|
+
export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedDiscriminatedUnionSchema | SerializedEnumSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedLocaleSchema | SerializedSettingsSchema | SerializedImageSchema | SerializedVideoSchema | SerializedValViewSchema;
|
|
29
30
|
type Primitives = number | string | boolean | null | FileSource;
|
|
30
31
|
export type AssertError = {
|
|
31
32
|
message: string;
|
|
@@ -8,15 +8,20 @@ import { ExternalRecordSrc } from "../source/external.js";
|
|
|
8
8
|
import { ModuleFilePath, SourcePath } from "../val/index.js";
|
|
9
9
|
import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
|
|
10
10
|
import type { ImageEncodeOption } from "./image.js";
|
|
11
|
+
import type { VideoStreamOption } from "./video.js";
|
|
11
12
|
type MediaOptions = {
|
|
12
|
-
type:
|
|
13
|
+
type: MediaCollectionType;
|
|
13
14
|
accept: string;
|
|
14
15
|
dir: string;
|
|
15
16
|
remote: boolean;
|
|
16
17
|
altSchema?: Schema<SelectorSource>;
|
|
17
18
|
/** Images only: how uploads are re-encoded in the browser. See `image.ts`. */
|
|
18
19
|
encode?: ImageEncodeOption;
|
|
20
|
+
/** Videos only: whether uploads become HLS streams. See `video.ts`. */
|
|
21
|
+
stream?: VideoStreamOption;
|
|
19
22
|
};
|
|
23
|
+
/** What a media collection (`s.imageset`, `s.fileset`, `s.videoset`) holds. */
|
|
24
|
+
export type MediaCollectionType = "files" | "images" | "videos";
|
|
20
25
|
export type SerializedRecordSchema = {
|
|
21
26
|
type: "record";
|
|
22
27
|
item: SerializedSchema;
|
|
@@ -44,11 +49,13 @@ export type SerializedRecordSchema = {
|
|
|
44
49
|
*/
|
|
45
50
|
routerSchemes?: string[];
|
|
46
51
|
customValidate?: boolean;
|
|
47
|
-
mediaType?:
|
|
52
|
+
mediaType?: MediaCollectionType;
|
|
48
53
|
accept?: string;
|
|
49
54
|
dir?: string;
|
|
50
55
|
remote?: boolean;
|
|
51
56
|
encode?: ImageEncodeOption;
|
|
57
|
+
/** Videos only: whether uploads become HLS streams. See `video.ts`. */
|
|
58
|
+
stream?: VideoStreamOption;
|
|
52
59
|
alt?: SerializedSchema;
|
|
53
60
|
jsonValues?: boolean;
|
|
54
61
|
/**
|
|
@@ -203,6 +210,13 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
203
210
|
private markKeyErrorsAtPath;
|
|
204
211
|
private validateMediaKey;
|
|
205
212
|
private validateMediaEntry;
|
|
213
|
+
/**
|
|
214
|
+
* The poster and caption files of a set's entry that are not where the set
|
|
215
|
+
* keeps its files. Asked only once the key itself is in place: a local key
|
|
216
|
+
* in a remote set is reported (and uploaded) as the entry, files and all,
|
|
217
|
+
* and a second error for its poster would be the same one twice.
|
|
218
|
+
*/
|
|
219
|
+
private misplacedEntryFiles;
|
|
206
220
|
private validateMediaMimeType;
|
|
207
221
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
208
222
|
nullable(): RecordSchema<T, K, Src | null>;
|
|
@@ -26,7 +26,21 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
|
|
|
26
26
|
private readonly isHidden;
|
|
27
27
|
private readonly description?;
|
|
28
28
|
private readonly previewInput;
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Whether `img: true` means a REMOTE image. Set by `remoteRichtext`, for a
|
|
31
|
+
* project whose media is all remote (`files.remote` in `val.config.ts`):
|
|
32
|
+
* `img: s.image()` is already remote there, because `s.image()` is, but
|
|
33
|
+
* `img: true` builds its image schema here and has to be told.
|
|
34
|
+
*/
|
|
35
|
+
private readonly imgRemote;
|
|
36
|
+
constructor(options: O & ValidationOptions, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null,
|
|
37
|
+
/**
|
|
38
|
+
* Whether `img: true` means a REMOTE image. Set by `remoteRichtext`, for a
|
|
39
|
+
* project whose media is all remote (`files.remote` in `val.config.ts`):
|
|
40
|
+
* `img: s.image()` is already remote there, because `s.image()` is, but
|
|
41
|
+
* `img: true` builds its image schema here and has to be told.
|
|
42
|
+
*/
|
|
43
|
+
imgRemote?: boolean);
|
|
30
44
|
/**
|
|
31
45
|
* Describe this field.
|
|
32
46
|
*
|
|
@@ -125,4 +139,9 @@ export declare class RichTextSchema<O extends RichTextOptions, Src extends RichT
|
|
|
125
139
|
protected executeSerialize(): SerializedSchema;
|
|
126
140
|
}
|
|
127
141
|
export declare const richtext: <O extends RichTextOptions>(options?: O) => RichTextSchema<O, RichTextSource<O>>;
|
|
142
|
+
/**
|
|
143
|
+
* `s.richtext()` for a project whose media is all remote (`files.remote` in
|
|
144
|
+
* `val.config.ts`): `img: true` is a remote image. See `initSchema`.
|
|
145
|
+
*/
|
|
146
|
+
export declare const remoteRichtext: <O extends RichTextOptions>(options?: O) => RichTextSchema<O, RichTextSource<O>>;
|
|
128
147
|
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const ValidationFix: readonly ["image:add-metadata", "image:check-metadata", "image:upload-remote", "image:download-remote", "image:check-remote", "images:check-remote", "images:upload-remote", "file:add-metadata", "file:check-metadata", "file:upload-remote", "file:download-remote", "file:check-remote", "files:check-remote", "files:upload-remote", "keyof:check-keys", "router:check-route", "locale:check-locale", "images:check-unique-folder", "files:check-unique-folder", "images:check-all-files", "files:check-all-files", "jsonValues:extract-entry", "record:fill-keys", "external:upload", "view:check-module"];
|
|
1
|
+
export declare const ValidationFix: readonly ["image:add-metadata", "image:check-metadata", "image:upload-remote", "image:download-remote", "image:check-remote", "images:check-remote", "images:upload-remote", "file:add-metadata", "file:check-metadata", "file:upload-remote", "file:download-remote", "file:check-remote", "files:check-remote", "files:upload-remote", "video:add-metadata", "video:upload-remote", "video:download-remote", "videos:add-metadata", "videos:check-remote", "videos:upload-remote", "keyof:check-keys", "router:check-route", "locale:check-locale", "images:check-unique-folder", "files:check-unique-folder", "images:check-all-files", "files:check-all-files", "videos:check-unique-folder", "videos:check-all-files", "jsonValues:extract-entry", "record:fill-keys", "external:upload", "view:check-module"];
|
|
2
2
|
export type ValidationFix = (typeof ValidationFix)[number];
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { GalleryVideoSource, VideoSource } from "../source/media.js";
|
|
2
|
+
import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
|
|
3
|
+
import { SourcePath } from "../val/index.js";
|
|
4
|
+
import type { ValModule } from "../module.js";
|
|
5
|
+
import type { AltSource } from "./imageset.js";
|
|
6
|
+
import type { VideosetEntryMetadata } from "./videoset.js";
|
|
7
|
+
import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
|
|
8
|
+
import { ItemPreviewInput, PreviewItem } from "../preview.js";
|
|
9
|
+
import { FieldRender } from "../render.js";
|
|
10
|
+
/**
|
|
11
|
+
* Turn an upload into an HLS stream, in the browser, before it is uploaded.
|
|
12
|
+
*
|
|
13
|
+
* The Studio transcodes the picked file with WebCodecs into one rendition per
|
|
14
|
+
* entry of `renditions` (never upscaled: a 720p upload gets no 1080p
|
|
15
|
+
* rendition), segments each into CMAF and writes the playlists. What is stored
|
|
16
|
+
* is a directory beside the other uploads; the field's `path` is its master
|
|
17
|
+
* playlist. See `architecture/media.md`.
|
|
18
|
+
*
|
|
19
|
+
* `type` is required for the same reason it is on `ImageEncodeOptions`: a
|
|
20
|
+
* second kind of stream would then be additive.
|
|
21
|
+
*/
|
|
22
|
+
export type VideoStreamOptions = {
|
|
23
|
+
type: "hls";
|
|
24
|
+
/**
|
|
25
|
+
* The heights of the renditions, in pixels. A rendition taller than the
|
|
26
|
+
* upload is skipped; if every one is, the upload's own height is used.
|
|
27
|
+
* @default [1080, 720, 480]
|
|
28
|
+
*/
|
|
29
|
+
renditions?: number[];
|
|
30
|
+
/** Target length of each segment, in seconds. @default 6 */
|
|
31
|
+
segmentDuration?: number;
|
|
32
|
+
};
|
|
33
|
+
/** `false` (or absent) uploads the file as it was picked. */
|
|
34
|
+
export type VideoStreamOption = false | VideoStreamOptions;
|
|
35
|
+
export type VideoOptions = {
|
|
36
|
+
/** Where uploads land. @default "/public/val" */
|
|
37
|
+
dir?: string;
|
|
38
|
+
/**
|
|
39
|
+
* What may be UPLOADED, in `<input accept>` syntax. @default "video/*"
|
|
40
|
+
*
|
|
41
|
+
* Not checked against an HLS stream: the playlist is what the Studio made of
|
|
42
|
+
* an upload that was accepted, not something an editor picked.
|
|
43
|
+
*/
|
|
44
|
+
accept?: string;
|
|
45
|
+
stream?: VideoStreamOption;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* What a SET-BACKED field (`s.video(videosVal)`) may say for itself.
|
|
49
|
+
*
|
|
50
|
+
* Not `VideoOptions`: `dir` and `accept` belong to the set. `stream` is the
|
|
51
|
+
* exception, as `encode` is for an image: it is about what happens to an
|
|
52
|
+
* upload on its way in, so a field that wants the file as it was picked,
|
|
53
|
+
* where its set streams, has to be able to say `stream: false`.
|
|
54
|
+
*/
|
|
55
|
+
export type GalleryVideoOptions = {
|
|
56
|
+
stream?: VideoStreamOption;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The set a field picks from: its entries as they were when the module was
|
|
60
|
+
* evaluated (the published set — see `GalleryImageSource` for why that is
|
|
61
|
+
* enough), and whether it stores its videos on Val Remote.
|
|
62
|
+
*/
|
|
63
|
+
export type VideoGallery = {
|
|
64
|
+
modulePath: string;
|
|
65
|
+
/**
|
|
66
|
+
* `null` for a schema that was DESERIALIZED: the serialized form names the
|
|
67
|
+
* set, not its contents, so whether the set has the entry is a question
|
|
68
|
+
* for the set's own module — which the Studio validates anyway.
|
|
69
|
+
*/
|
|
70
|
+
entries: Record<string, VideosetEntryMetadata<AltSource>> | null;
|
|
71
|
+
remote: boolean;
|
|
72
|
+
};
|
|
73
|
+
export type SerializedVideoSchema = {
|
|
74
|
+
type: "video";
|
|
75
|
+
/** Static layout config, carried whole in the serialized schema — see `render.ts`. */
|
|
76
|
+
render?: FieldRender;
|
|
77
|
+
/** Set when this schema declares a `preview`. The closure itself cannot serialize. */
|
|
78
|
+
preview?: true;
|
|
79
|
+
options?: VideoOptions;
|
|
80
|
+
opt: boolean;
|
|
81
|
+
remote?: boolean;
|
|
82
|
+
customValidate?: boolean;
|
|
83
|
+
/** The `s.videoset()` module this field picks from, if it is set-backed. */
|
|
84
|
+
referencedModule?: string;
|
|
85
|
+
readonly?: boolean;
|
|
86
|
+
hidden?: boolean;
|
|
87
|
+
description?: string;
|
|
88
|
+
};
|
|
89
|
+
export type VideoMetadata = {
|
|
90
|
+
mimeType?: string;
|
|
91
|
+
width?: number;
|
|
92
|
+
height?: number;
|
|
93
|
+
duration?: number;
|
|
94
|
+
};
|
|
95
|
+
export declare const DEFAULT_VIDEO_ACCEPT = "video/*";
|
|
96
|
+
export declare const DEFAULT_VIDEO_RENDITIONS: number[];
|
|
97
|
+
export declare const DEFAULT_VIDEO_SEGMENT_DURATION = 6;
|
|
98
|
+
export declare class VideoSchema<Src extends GalleryVideoSource | null> extends Schema<Src> {
|
|
99
|
+
private readonly options?;
|
|
100
|
+
private readonly opt;
|
|
101
|
+
protected readonly isRemote: boolean;
|
|
102
|
+
private readonly customValidateFunctions;
|
|
103
|
+
private readonly isReadonly;
|
|
104
|
+
private readonly isHidden;
|
|
105
|
+
private readonly description?;
|
|
106
|
+
private readonly renderInput;
|
|
107
|
+
private readonly previewInput;
|
|
108
|
+
private readonly gallery;
|
|
109
|
+
constructor(options?: VideoOptions | undefined, opt?: boolean, isRemote?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null, gallery?: VideoGallery | null);
|
|
110
|
+
/**
|
|
111
|
+
* Describe this field.
|
|
112
|
+
*
|
|
113
|
+
* The description is INPUT HELP: it is shown where this field's value is
|
|
114
|
+
* entered — beside its input in the Val editor, and for a record's key
|
|
115
|
+
* schema in every form that asks for a key — so it is where you say what an
|
|
116
|
+
* editor needs to know to fill it in RIGHT, which the field name cannot
|
|
117
|
+
* carry. It is not a name for the value: that is `.preview(...)`, and it is
|
|
118
|
+
* read somewhere else. The description also travels in the serialized
|
|
119
|
+
* schema, which is what the AI assistant and the MCP tools read.
|
|
120
|
+
*
|
|
121
|
+
* Pass `null` to clear a description set earlier.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* const schema = s.video().describe("Plays muted behind the headline");
|
|
125
|
+
* export default c.define("/example.val.ts", schema, {
|
|
126
|
+
* path: "/public/val/example.mp4",
|
|
127
|
+
* mimeType: "video/mp4",
|
|
128
|
+
* });
|
|
129
|
+
*/
|
|
130
|
+
describe(description: string | null): VideoSchema<Src>;
|
|
131
|
+
/**
|
|
132
|
+
* Store the video on Val's remote content host instead of in your
|
|
133
|
+
* repository.
|
|
134
|
+
*
|
|
135
|
+
* The bytes still go into the patch store when the video is uploaded — the
|
|
136
|
+
* push to the remote host happens at publish. What changes is where the
|
|
137
|
+
* published video lives: `path` (and the poster's, and each caption track's)
|
|
138
|
+
* becomes a remote URL rather than a path under `/public`, so the repository
|
|
139
|
+
* does not grow with every upload. Upload remote videos in the Studio.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* const schema = s.video().remote();
|
|
143
|
+
* export default c.define("/example.val.ts", schema, {
|
|
144
|
+
* path: "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/example.mp4",
|
|
145
|
+
* mimeType: "video/mp4",
|
|
146
|
+
* });
|
|
147
|
+
*/
|
|
148
|
+
remote(): VideoSchema<Src>;
|
|
149
|
+
/**
|
|
150
|
+
* Add a custom validation rule to this field.
|
|
151
|
+
*
|
|
152
|
+
* The function is called with the field's value and returns `false` when the
|
|
153
|
+
* value is fine, or a STRING with the message to show when it is not. Call it
|
|
154
|
+
* more than once to add more rules — they all run, and every message is
|
|
155
|
+
* reported.
|
|
156
|
+
*
|
|
157
|
+
* Write the check as a ternary, not as `ok || "message"`: that returns `true`
|
|
158
|
+
* when the value is fine, and `true` is not one of the two answers.
|
|
159
|
+
*
|
|
160
|
+
* Validation runs in the Studio as you type, in `npx val validate` and
|
|
161
|
+
* before a publish.
|
|
162
|
+
*
|
|
163
|
+
* The second argument carries the `path` of the field being validated, for
|
|
164
|
+
* when the message needs to say where the problem is.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* const schema = s.video().validate((val) =>
|
|
168
|
+
* (val.duration ?? 0) <= 30 ? false : "Keep it under 30 seconds",
|
|
169
|
+
* );
|
|
170
|
+
* export default c.define("/example.val.ts", schema, {
|
|
171
|
+
* path: "/public/val/example.mp4",
|
|
172
|
+
* mimeType: "video/mp4",
|
|
173
|
+
* duration: 12,
|
|
174
|
+
* });
|
|
175
|
+
*/
|
|
176
|
+
validate(validationFunction: CustomValidateFunction<Src>): VideoSchema<Src>;
|
|
177
|
+
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
178
|
+
/**
|
|
179
|
+
* A set-backed field: the file and what was read from it are the set's,
|
|
180
|
+
* so the field is checked for what is its own, and for pointing at an
|
|
181
|
+
* entry the set has.
|
|
182
|
+
*/
|
|
183
|
+
private validateFromGallery;
|
|
184
|
+
/** A video of its own: it carries the file, and what was read from it. */
|
|
185
|
+
private validateOwn;
|
|
186
|
+
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
187
|
+
nullable(): VideoSchema<Src | null>;
|
|
188
|
+
readonly(isReadonly?: boolean): VideoSchema<Src>;
|
|
189
|
+
hidden(isHidden?: boolean): VideoSchema<Src>;
|
|
190
|
+
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
191
|
+
/**
|
|
192
|
+
* How this field is laid out in the editor when it is the item of an array
|
|
193
|
+
* or record: `{ as: "inline" }` renders the field itself inside each row,
|
|
194
|
+
* instead of a preview row that navigates to it.
|
|
195
|
+
*
|
|
196
|
+
* Static configuration, not a callback — see `render.ts`.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* const schema = s.array(s.video().render({ as: "inline" }));
|
|
200
|
+
* export default c.define("/example.val.ts", schema, [
|
|
201
|
+
* { path: "/public/val/example.mp4", mimeType: "video/mp4" },
|
|
202
|
+
* ]);
|
|
203
|
+
*/
|
|
204
|
+
render(input: FieldRender): VideoSchema<Src>;
|
|
205
|
+
/**
|
|
206
|
+
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
207
|
+
* sortable list, a reference dropdown, a search hit. Never how the field
|
|
208
|
+
* itself is edited (that is `render`). See `preview.ts`.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* const schema = s.array(
|
|
212
|
+
* s.video().preview(({ val }) => ({
|
|
213
|
+
* title: val.alt ?? val.path,
|
|
214
|
+
* image: val.poster,
|
|
215
|
+
* })),
|
|
216
|
+
* );
|
|
217
|
+
* export default c.define("/example.val.ts", schema, [
|
|
218
|
+
* { path: "/public/val/example.mp4", mimeType: "video/mp4" },
|
|
219
|
+
* ]);
|
|
220
|
+
*/
|
|
221
|
+
preview(select: ItemPreviewInput<Src>): VideoSchema<Src>;
|
|
222
|
+
protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
|
|
223
|
+
protected declaresItemPreview(): boolean;
|
|
224
|
+
protected executeSerialize(): SerializedSchema;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Every file a video value names: the video, its poster and each caption
|
|
228
|
+
* track. An HLS stream's segments are not here — they are named by its
|
|
229
|
+
* playlists, not by the value.
|
|
230
|
+
*/
|
|
231
|
+
export declare function filesOfVideoSource(src: GalleryVideoSource): string[];
|
|
232
|
+
/**
|
|
233
|
+
* A video picked from a set (`s.videoset()`). What is true of the file —
|
|
234
|
+
* its type, size and length — lives in the set; the field carries what one
|
|
235
|
+
* page chose about it.
|
|
236
|
+
*/
|
|
237
|
+
export declare function video(videosetModule: ValModule<Record<string, VideosetEntryMetadata<AltSource>>>, galleryOptions?: GalleryVideoOptions): VideoSchema<GalleryVideoSource>;
|
|
238
|
+
/**
|
|
239
|
+
* Define a video: an `.mp4` / `.webm` file, or an HLS stream the Studio makes
|
|
240
|
+
* from an upload when `stream` is set.
|
|
241
|
+
*/
|
|
242
|
+
export declare function video(options?: VideoOptions): VideoSchema<VideoSource>;
|
|
243
|
+
/**
|
|
244
|
+
* `s.video()` for a project whose media is all remote (`files.remote` in
|
|
245
|
+
* `val.config.ts`): the same schema, with `.remote()` already applied. See
|
|
246
|
+
* `initSchema`.
|
|
247
|
+
*/
|
|
248
|
+
export declare function remoteVideo(videosetModule: ValModule<Record<string, VideosetEntryMetadata<AltSource>>>, galleryOptions?: GalleryVideoOptions): VideoSchema<GalleryVideoSource>;
|
|
249
|
+
export declare function remoteVideo(options?: VideoOptions): VideoSchema<VideoSource>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Schema } from "./index.js";
|
|
2
|
+
import type { SerializedRecordSchema } from "./record.js";
|
|
3
|
+
import { RecordSchema } from "./record.js";
|
|
4
|
+
import { ObjectSchema } from "./object.js";
|
|
5
|
+
import { StringSchema } from "./string.js";
|
|
6
|
+
import { NumberSchema } from "./number.js";
|
|
7
|
+
import type { AltSchema, AltSource, AltSourceOf } from "./imageset.js";
|
|
8
|
+
import type { SerializedVideoSchema, VideoStreamOption } from "./video.js";
|
|
9
|
+
import type { MediaHotspot, VideoCaptionSource, VideoPosterSource } from "../source/media.js";
|
|
10
|
+
/**
|
|
11
|
+
* Options for s.videoset()
|
|
12
|
+
*/
|
|
13
|
+
export type VideosetOptions<Accept extends `video/${string}`, Alt extends AltSchema = StringSchema<string | null>> = VideosetOptionsBase<Accept> & VideosetAltOption<Alt>;
|
|
14
|
+
/** See `ImagesetAltOption`: `alt` is optional only when it is the default. */
|
|
15
|
+
type VideosetAltOption<Alt extends AltSchema> = [
|
|
16
|
+
StringSchema<string | null>
|
|
17
|
+
] extends [Alt] ? {
|
|
18
|
+
/**
|
|
19
|
+
* Description schema. Can be:
|
|
20
|
+
* - s.string() for a required description
|
|
21
|
+
* - s.string().nullable() for an optional one (default)
|
|
22
|
+
* - s.record(s.string(), s.string()) for one per locale
|
|
23
|
+
*/
|
|
24
|
+
alt?: Alt;
|
|
25
|
+
} : {
|
|
26
|
+
/** Description schema — required, because it is not the default one. */
|
|
27
|
+
alt: Alt;
|
|
28
|
+
};
|
|
29
|
+
type VideosetOptionsBase<Accept extends `video/${string}`> = {
|
|
30
|
+
/**
|
|
31
|
+
* What may be UPLOADED, in `<input accept>` syntax. Must be a video type.
|
|
32
|
+
* Not checked against an HLS stream: see `VideoOptions.accept`.
|
|
33
|
+
* @default "video/*"
|
|
34
|
+
*/
|
|
35
|
+
accept?: Accept;
|
|
36
|
+
/**
|
|
37
|
+
* The directory where videos are stored. Must start with "/public".
|
|
38
|
+
*
|
|
39
|
+
* Required for the same reason it is on `s.imageset()`: it decides where
|
|
40
|
+
* every upload in the set lands, and a default is a directory shared with
|
|
41
|
+
* every other set that did not say.
|
|
42
|
+
*/
|
|
43
|
+
dir: "/public" | `/public/${string}`;
|
|
44
|
+
/**
|
|
45
|
+
* Turn every upload into an HLS stream, in the browser. See
|
|
46
|
+
* `VideoStreamOptions`. A field backed by this set (`s.video(videosVal)`)
|
|
47
|
+
* inherits it, as it inherits `accept` and `dir`.
|
|
48
|
+
*/
|
|
49
|
+
stream?: VideoStreamOption;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* One video of a set, keyed by its file path (an HLS stream by its master
|
|
53
|
+
* playlist).
|
|
54
|
+
*
|
|
55
|
+
* Two kinds of thing are here. What is TRUE OF THE FILE — what it is, how
|
|
56
|
+
* big, how long — is read from the bytes and is the set's alone. The rest —
|
|
57
|
+
* the description, focal point, poster, start and end, and captions — is what
|
|
58
|
+
* a page chooses about a video, and an entry holds it as the DEFAULT: every
|
|
59
|
+
* field picked from the set (`s.video(videosVal)`) starts from it and may
|
|
60
|
+
* override it key by key, so a video described and trimmed once is described
|
|
61
|
+
* and trimmed everywhere it is used. `fillFromGallery` is the one merge.
|
|
62
|
+
*
|
|
63
|
+
* The poster is also the set's thumbnail of the entry: there is no separate
|
|
64
|
+
* still for the gallery, because two stills of one video would disagree.
|
|
65
|
+
*/
|
|
66
|
+
export type VideosetEntryMetadata<Alt extends AltSource = string | null> = {
|
|
67
|
+
mimeType: string;
|
|
68
|
+
width: number;
|
|
69
|
+
height: number;
|
|
70
|
+
/** In seconds. */
|
|
71
|
+
duration: number;
|
|
72
|
+
alt: Alt;
|
|
73
|
+
hotspot?: MediaHotspot;
|
|
74
|
+
/** Seconds. The frame the poster was taken from. */
|
|
75
|
+
posterTime?: number;
|
|
76
|
+
poster?: VideoPosterSource;
|
|
77
|
+
/** Seconds. Where playback starts. */
|
|
78
|
+
startTime?: number;
|
|
79
|
+
/** Seconds. Where playback stops. */
|
|
80
|
+
endTime?: number;
|
|
81
|
+
/** Overridden as a whole: a field with its own tracks has none of these. */
|
|
82
|
+
captions?: readonly VideoCaptionSource[];
|
|
83
|
+
};
|
|
84
|
+
export type SerializedVideosetSchema = SerializedRecordSchema;
|
|
85
|
+
type VideosetItemProps<Alt extends AltSchema> = {
|
|
86
|
+
mimeType: StringSchema<string>;
|
|
87
|
+
width: NumberSchema<number>;
|
|
88
|
+
height: NumberSchema<number>;
|
|
89
|
+
duration: NumberSchema<number>;
|
|
90
|
+
alt: Alt;
|
|
91
|
+
};
|
|
92
|
+
type VideosetItemSrc<Alt extends AltSchema> = {
|
|
93
|
+
mimeType: string;
|
|
94
|
+
width: number;
|
|
95
|
+
height: number;
|
|
96
|
+
duration: number;
|
|
97
|
+
alt: AltSourceOf<Alt>;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Define a collection of videos — the video twin of `s.imageset()`.
|
|
101
|
+
*
|
|
102
|
+
* A field picks from it with `s.video(videosVal)`. An entry may hold the
|
|
103
|
+
* description, poster, start and end, focal point and captions as defaults,
|
|
104
|
+
* and a field overrides any of them by setting its own. Uploading in such a
|
|
105
|
+
* field adds to the set. Call `.remote()` on the result
|
|
106
|
+
* to store the set on Val Remote.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const schema = s.videoset({
|
|
111
|
+
* dir: "/public/val/videos",
|
|
112
|
+
* stream: { type: "hls" },
|
|
113
|
+
* });
|
|
114
|
+
* export default c.define("/content/videos.val.ts", schema, {
|
|
115
|
+
* "/public/val/videos/intro_51df2.mp4": {
|
|
116
|
+
* mimeType: "video/mp4",
|
|
117
|
+
* width: 1280,
|
|
118
|
+
* height: 720,
|
|
119
|
+
* duration: 12.5,
|
|
120
|
+
* alt: "The team, introducing itself",
|
|
121
|
+
* startTime: 1.5,
|
|
122
|
+
* },
|
|
123
|
+
* });
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare const videoset: <Accept extends `video/${string}`, Alt extends AltSchema = StringSchema<string | null>>(options: VideosetOptions<Accept, Alt>) => RecordSchema<ObjectSchema<VideosetItemProps<Alt>, VideosetItemSrc<Alt>>, Schema<string>, Record<string, VideosetEntryMetadata<AltSourceOf<Alt>>>>;
|
|
127
|
+
/**
|
|
128
|
+
* The video schema a set's entry is hashed against, for a remote ref.
|
|
129
|
+
*
|
|
130
|
+
* The set's item schema is an object, but a remote ref's validation hash is
|
|
131
|
+
* computed over a MEDIA schema, so one is synthesized from the set — the one
|
|
132
|
+
* definition the CLI's upload (`videos:upload-remote`) and the Studio's (the
|
|
133
|
+
* set's gallery) both use, because two copies are two hashes for the same
|
|
134
|
+
* file. `stream` is left out: it says what is done to an upload on its way
|
|
135
|
+
* in, not whether the bytes that arrived are valid.
|
|
136
|
+
*/
|
|
137
|
+
export declare function videosetEntryVideoSchema(set: {
|
|
138
|
+
accept?: string;
|
|
139
|
+
dir?: string;
|
|
140
|
+
}): SerializedVideoSchema;
|
|
141
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Schema } from "../schema/index.js";
|
|
2
|
+
import type { SelectorSource } from "../selector/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The PUBLISHED entries of every gallery a module's fields pick from — an
|
|
5
|
+
* `s.image(galleryVal)`, `s.file(filesVal)` or `s.video(videosVal)` — by the
|
|
6
|
+
* gallery's module path.
|
|
7
|
+
*
|
|
8
|
+
* A gallery-backed value names its file and nothing else that is the
|
|
9
|
+
* gallery's, so a reader has to look the entry up (`fillFromGallery`). In
|
|
10
|
+
* draft mode the reader has every module's draft source to look in; outside
|
|
11
|
+
* it, nothing — and the page got a set-backed video with no type, size,
|
|
12
|
+
* poster or description, and an image with no dimensions. But the field's
|
|
13
|
+
* schema was built from the gallery module (`s.video(videosVal)` reads it at
|
|
14
|
+
* definition), so the published entries are already here, on the schema
|
|
15
|
+
* instance. This is where they are read from.
|
|
16
|
+
*
|
|
17
|
+
* Stops at a view: the module it points at is read on its own, with its own
|
|
18
|
+
* galleries.
|
|
19
|
+
*/
|
|
20
|
+
export declare function galleriesOf(schema: Schema<SelectorSource> | undefined): Map<string, unknown>;
|