@valbuild/core 0.139.0 → 0.141.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 (28) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/declarations/src/extractValModules.d.ts +14 -0
  3. package/dist/declarations/src/getSha256.d.ts +55 -0
  4. package/dist/declarations/src/index.d.ts +21 -4
  5. package/dist/declarations/src/initSchema.d.ts +75 -55
  6. package/dist/declarations/src/initVal.d.ts +25 -2
  7. package/dist/declarations/src/remote/validationBasis.d.ts +3 -2
  8. package/dist/declarations/src/schema/file.d.ts +7 -0
  9. package/dist/declarations/src/schema/image.d.ts +9 -2
  10. package/dist/declarations/src/schema/index.d.ts +2 -1
  11. package/dist/declarations/src/schema/record.d.ts +16 -2
  12. package/dist/declarations/src/schema/richtext.d.ts +20 -1
  13. package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
  14. package/dist/declarations/src/schema/video.d.ts +249 -0
  15. package/dist/declarations/src/schema/videoset.d.ts +141 -0
  16. package/dist/declarations/src/source/galleries.d.ts +20 -0
  17. package/dist/declarations/src/source/media.d.ts +172 -12
  18. package/dist/declarations/src/val/index.d.ts +4 -2
  19. package/dist/{index-8920ba3f.cjs.prod.js → index-13cf45b1.cjs.prod.js} +1547 -134
  20. package/dist/{index-d949d13b.esm.js → index-a6c53286.esm.js} +1543 -135
  21. package/dist/{index-e13b725c.cjs.dev.js → index-fae161b5.cjs.dev.js} +1547 -134
  22. package/dist/valbuild-core.cjs.dev.js +6 -1
  23. package/dist/valbuild-core.cjs.prod.js +6 -1
  24. package/dist/valbuild-core.esm.js +1 -1
  25. package/package.json +1 -1
  26. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  27. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  28. package/patch/dist/valbuild-core-patch.esm.js +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,74 @@
1
1
  # @valbuild/core
2
2
 
3
+ ## 0.141.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#802](https://github.com/valbuild/val/pull/802) [`199e214`](https://github.com/valbuild/val/commit/199e21471d98806fb34c579cb876560078c928b9) Thanks [@freekh](https://github.com/freekh)! - New: `files: { remote: true }` in `val.config.ts` stores every image, file and video on Val's remote content host.
8
+
9
+ ```ts
10
+ const { s, c, val, config } = initVal({
11
+ project: "myorg/myproject",
12
+ files: { remote: true },
13
+ });
14
+ ```
15
+
16
+ With it, `s.image()`, `s.file()`, `s.video()`, `s.imageset()`, `s.fileset()`, `s.videoset()` and `s.richtext({ img: true })` behave as though `.remote()` had been written on them. Images already in the project are reported as `image:upload-remote` until they are uploaded; `npx val validate --fix` does that.
17
+
18
+ **Required in the Val app.** A project running there (`VAL_ENV=app`) refuses to start without it, with an error that says what to add, and `val publish` refuses before it builds.
19
+
20
+ **Breaking:** `files.directory` has been removed from `val.config.ts`. Local files that have no `dir` of their own are stored in `/public/val`, which was the default.
21
+
22
+ - [#779](https://github.com/valbuild/val/pull/779) [`ba3f330`](https://github.com/valbuild/val/commit/ba3f3300a3dd8649e154097cdfca3b64db209d89) Thanks [@freekh](https://github.com/freekh)! - New: `s.video()`, for mp4/WebM files and HLS streams.
23
+
24
+ ```ts
25
+ const schema = s.object({
26
+ intro: s.video({ stream: { type: "hls" } }),
27
+ });
28
+ ```
29
+
30
+ - **Upload and play in the Studio.** A video field shows a player, the file's size and length, and progress while it uploads.
31
+ - **HLS streaming without a video service.** With `stream: { type: "hls" }`, the Studio converts an upload to an HLS stream in the editor's browser before it uploads it. It makes one H.264 rendition per height (1080p, 720p and 480p by default, and never larger than the upload) and stores them as a folder of files next to your other uploads. Nothing is installed in your app for this: the converter ships inside the Studio and is only downloaded when someone uploads to a streaming field. A browser that can't convert (no WebCodecs, or a non-https address) uploads the original file and says so.
32
+ - **Poster:** pause on a frame and choose "Use current frame". The frame is saved as an image, and `posterTime` records where it was taken.
33
+ - **Start and end:** play part of a video without cutting the file.
34
+ - **Focal point:** marks what must stay in frame when the page crops the video.
35
+ - **Description:** text for people who can't see the video.
36
+ - **Captions:** one WebVTT file per language. `.srt` files are converted on upload.
37
+ - **`<ValVideo>`** in `@valbuild/next` and `@valbuild/tanstack` renders all of the above and is click-to-edit. For HLS in browsers that can't play it natively, pass `hls={() => import("hls.js")}`. It is only loaded when it's needed.
38
+ - **Rename** a video in the Studio. An HLS stream is renamed as a whole: every playlist and segment moves to the new folder.
39
+ - `npx val validate --fix` fills in `mimeType`, `width`, `height` and `duration` for a hand-written `.mp4`, `.mov`, `.webm`, `.mkv` or HLS video, on disk or on Val Remote. A remote video is not downloaded: only its headers are read, usually in one to three small requests.
40
+ - `.remote()` works for videos. `npx val validate --fix` moves a video to Val Remote, or back into the project, together with its poster, captions and every file of an HLS stream.
41
+
42
+ A video value always has a `mimeType`: it is how a page knows whether it has an `.mp4` or an `.m3u8`.
43
+
44
+ New: `s.videoset()`, a collection of videos, like `s.imageset()` for images.
45
+
46
+ ```ts
47
+ const videosVal = c.define(
48
+ "/content/videos.val.ts",
49
+ s.videoset({ dir: "/public/val/videos", stream: { type: "hls" } }),
50
+ {},
51
+ );
52
+ const schema = s.object({ intro: s.video(videosVal) });
53
+ ```
54
+
55
+ - **Upload once, set it up once, use anywhere.** A set's entry holds what is true of the file (its type, size and length) and, as defaults, a description, poster, start and end, focal point and captions. A field that picks from the set (`s.video(videosVal)`) gets all of those, and can override any of them on its own. For example, the same clip can open one page at 0:02 and every other page where the set says.
56
+ - **Media in the Studio.** A set is listed under Media and opens as a gallery of videos, with each video's poster as its thumbnail. Hovering a video previews it, and the selected video plays in a real player (HLS streams too) beside the poster, times, focal point and captions you edit for it. You can upload into the set (with `stream`, uploads become HLS streams, and each upload gets a poster), rename a video, and delete a video that nothing uses. Renaming a video updates every field that uses it. An HLS stream is one entry, and renaming or deleting it moves or removes all of its files.
57
+ - **Picking.** A set-backed field picks from the set, or uploads into it. Each value the set provides is marked "From gallery · Override", and once overridden, "Overridden · Use gallery's".
58
+ - `useVal` and `fetchVal` fill in what the set holds (`mimeType`, `width`, `height`, `duration`, and every default the field does not override), so `<ValVideo>` works the same either way.
59
+ - `.remote()` works for sets. `npx val validate --fix` fills in missing metadata for an entry, adds untracked videos found in the set's directory, and moves a set's videos to Val Remote.
60
+
61
+ Image and file galleries use the same new gallery layout: a grid or list of entries, with the selected one open in a panel beside it.
62
+
63
+ - An `s.imageset()` entry can hold a default focal point, as well as its description. An `s.image(galleryVal)` field shows both, and can override either.
64
+ - Fixed: outside draft mode, `useVal` and `fetchVal` now fill a gallery-backed image or file from its gallery (dimensions, mime type, description). Before, a published page got only the `path`.
65
+
66
+ ## 0.139.1
67
+
68
+ ### Patch Changes
69
+
70
+ - [#780](https://github.com/valbuild/val/pull/780) [`db13bed`](https://github.com/valbuild/val/commit/db13bed902f9f9147feb5be07ce47fe8b5b143f9) Thanks [@freekh](https://github.com/freekh)! - When a new version of your site with a changed schema is deployed while Val Studio is open, the Studio now asks you to reload before you keep editing, instead of carrying on with the old schema.
71
+
3
72
  ## 0.139.0
4
73
 
5
74
  ### Minor Changes
@@ -85,6 +85,20 @@ export declare function computeSourcesSha(entries: readonly {
85
85
  path: string;
86
86
  source: unknown;
87
87
  }[]): string;
88
+ /**
89
+ * The `schemaSha` fold on its own: path and serialized schema, in `val.modules`
90
+ * order.
91
+ *
92
+ * Separate for the same reason as {@link computeSourcesSha}: the Studio folds
93
+ * the schemas of the bundle it is running and compares the answer with the
94
+ * `schemaSha` on `/stat`, which is how it learns that a new schema has been
95
+ * deployed under it. One implementation, so that comparison is between two
96
+ * runs of the same code.
97
+ */
98
+ export declare function computeSchemaSha(entries: readonly {
99
+ path: string;
100
+ serializedSchema: unknown;
101
+ }[]): string;
88
102
  export declare function computeValModuleShas(config: ValModules["config"], entries: readonly ValModuleShaEntry[], moduleErrors: readonly ExtractedModuleError[]): ValModuleShas;
89
103
  /**
90
104
  * Extracts schemas and sources from a ValModules registry and computes the
@@ -0,0 +1,55 @@
1
+ /**
2
+ * From: https://github.com/kawanet/sha256-uint8array/commit/a035f83824c319d01ca1e7559fdcf1632c0cd6c4
3
+ *
4
+ * LICENSE:
5
+ *
6
+ * MIT License
7
+ * Copyright (c) 2020-2023 Yusuke Kawasaki
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ * of this software and associated documentation files (the "Software"), to deal
11
+ * in the Software without restriction, including without limitation the rights
12
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ * copies of the Software, and to permit persons to whom the Software is
14
+ * furnished to do so, subject to the following conditions:
15
+ *
16
+ * The above copyright notice and this permission notice shall be included in all
17
+ * copies or substantial portions of the Software.
18
+ *
19
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ * SOFTWARE.
26
+ *
27
+ * sha256-uint8array.ts
28
+ */
29
+ export declare function createHash(algorithm?: string): Hash;
30
+ export declare class Hash {
31
+ private A;
32
+ private B;
33
+ private C;
34
+ private D;
35
+ private E;
36
+ private F;
37
+ private G;
38
+ private H;
39
+ private _byte;
40
+ private _word;
41
+ private _size;
42
+ private _sp;
43
+ constructor();
44
+ update(data: string, encoding?: string): this;
45
+ update(data: Uint8Array): this;
46
+ update(data: ArrayBufferView): this;
47
+ private _uint8;
48
+ private _utf8;
49
+ private _int32;
50
+ digest(): Uint8Array;
51
+ digest(encoding: string): string;
52
+ private _hex;
53
+ private _bin;
54
+ }
55
+ export declare const getSHA256Hash: (bits: Uint8Array) => string;
@@ -1,11 +1,14 @@
1
1
  export { initVal, type ConfigDirectory } from "./initVal.js";
2
2
  export { modules, type ValModules } from "./modules.js";
3
- export { extractValModules, computeValModuleShas, computeSourcesSha, type ExtractedValModules, type ValModuleShaEntry, type ValModuleShas, type ExtractedModuleError, } from "./extractValModules.js";
3
+ export { extractValModules, computeValModuleShas, computeSourcesSha, computeSchemaSha, type ExtractedValModules, type ValModuleShaEntry, type ValModuleShas, 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
6
  export { hasRemoteFileSchema, hasMediaSchema, } from "./schema/hasRemoteFileSchema.js";
7
7
  export type { ImageMetadata, ImageEncodeOption, ImageEncodeOptions, } from "./schema/image.js";
8
8
  export type { FileMetadata } from "./schema/file.js";
9
+ export type { GalleryVideoOptions, VideoMetadata, VideoOptions, VideoStreamOption, VideoStreamOptions, } from "./schema/video.js";
10
+ import { videosetEntryVideoSchema } from "./schema/videoset.js";
11
+ export type { VideosetEntryMetadata, VideosetOptions, SerializedVideosetSchema, } from "./schema/videoset.js";
9
12
  export type { ValModule, SerializedModule, InferValModuleType } from "./module.js";
10
13
  export type { SourceObject, SourcePrimitive, Source } from "./source/index.js";
11
14
  export type { FileSource } from "./source/media.js";
@@ -15,7 +18,7 @@ export type { RemoteRef } from "./source/remote.js";
15
18
  export { DEFAULT_VAL_REMOTE_HOST } from "./schema/remote.js";
16
19
  export type { RawString } from "./schema/string.js";
17
20
  export type { ImageSource } from "./source/media.js";
18
- export type { MediaHotspot, MediaSource, GalleryImageSource, GalleryFileSource, } from "./source/media.js";
21
+ export type { MediaHotspot, MediaSource, GalleryImageSource, GalleryFileSource, GalleryVideoSource, VideoDefaults, IsVideoSource, VideoSource, VideoPosterSource, VideoCaptionSource, ResolvedVideo, } from "./source/media.js";
19
22
  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";
20
23
  export { type Val, type SerializedVal, type ModuleFilePath, type PatchId, type ModulePath, type SourcePath, type JsonOfSource, type ParentPatchId, } from "./val/index.js";
21
24
  export type { Json, JsonPrimitive, JsonArray, JsonObject } from "./Json.js";
@@ -43,16 +46,18 @@ import { createRenamedFilename } from "./source/filename.js";
43
46
  import { getValidationBasis, getValidationHash } from "./remote/validationBasis.js";
44
47
  import { getFileHash, hashToRemoteFileHash } from "./remote/fileHash.js";
45
48
  import { splitRemoteRef } from "./remote/splitRemoteRef.js";
46
- import { fillFromGallery, isRemoteMediaPath, mediaUrl, resolveMedia } from "./source/media.js";
49
+ import { galleriesOf } from "./source/galleries.js";
50
+ import { fillFromGallery, galleryEntryOf, isHlsVideo, isRemoteMediaPath, mediaUrl, resolveMedia, resolveVideo } from "./source/media.js";
47
51
  import { colorToHex, convertColor, detectColorFormat, formatColor, parseColor } from "./schema/colorFormat.js";
48
52
  export { type SerializedArraySchema, ArraySchema } from "./schema/array.js";
49
53
  export { type SerializedObjectSchema, ObjectSchema } from "./schema/object.js";
50
- export { type SerializedRecordSchema, RecordSchema } from "./schema/record.js";
54
+ export { type SerializedRecordSchema, type MediaCollectionType, RecordSchema, } from "./schema/record.js";
51
55
  export { type SerializedStringSchema, StringSchema } from "./schema/string.js";
52
56
  export { type SerializedNumberSchema, NumberSchema } from "./schema/number.js";
53
57
  export { type SerializedBooleanSchema, BooleanSchema } from "./schema/boolean.js";
54
58
  export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
55
59
  export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
60
+ export { type SerializedVideoSchema, VideoSchema, DEFAULT_VIDEO_ACCEPT, DEFAULT_VIDEO_RENDITIONS, DEFAULT_VIDEO_SEGMENT_DURATION, } from "./schema/video.js";
56
61
  export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
57
62
  export { type SerializedSettingsSchema, SettingsSchema, } from "./schema/settings.js";
58
63
  export { type SettingsSource, type AssistantSettingsSource, type LocalesSettingsSource, type AssistantAvailability, type ThemeSettingsSource, type StudioSettingsSource, type StudioCommitMessageMode, type ThemeRadius, ASSISTANT_SETTINGS_MAX_LENGTH, THEME_LOGO_DIRECTORY, THEME_RADIUS_STEPS, THEME_RADIUS_LENGTHS, assistantAvailability, } from "./source/settings.js";
@@ -128,6 +133,7 @@ declare const Internal: {
128
133
  VERSION: {
129
134
  core: string;
130
135
  };
136
+ videosetEntryVideoSchema: typeof videosetEntryVideoSchema;
131
137
  createViewHandle: typeof createViewHandle;
132
138
  isViewHandle: typeof isViewHandle;
133
139
  resolveViewedModule: typeof resolveViewedModule;
@@ -135,9 +141,15 @@ declare const Internal: {
135
141
  viewModulesOf: typeof viewModulesOf;
136
142
  mediaUrl: typeof mediaUrl;
137
143
  resolveMedia: typeof resolveMedia;
144
+ resolveVideo: typeof resolveVideo;
138
145
  isRemoteMediaPath: typeof isRemoteMediaPath;
139
146
  media: {
140
147
  fillFromGallery: typeof fillFromGallery;
148
+ galleriesOf: typeof galleriesOf;
149
+ galleryEntryOf: typeof galleryEntryOf;
150
+ GALLERY_DEFAULT_GROUPS: readonly (readonly string[])[];
151
+ isHlsVideo: typeof isHlsVideo;
152
+ HLS_MIME_TYPE: string;
141
153
  };
142
154
  getSchema: typeof getSchema;
143
155
  getValPath: typeof getValPath;
@@ -182,6 +194,11 @@ declare const Internal: {
182
194
  isExternalFileRef: typeof isExternalFileRef;
183
195
  createValPathOfItem: typeof createValPathOfItem;
184
196
  getSHA256Hash: (bits: Uint8Array) => string;
197
+ /**
198
+ * The same hash as `getSHA256Hash`, fed a chunk at a time: what a caller
199
+ * hashing hundreds of megabytes uses to give the thread back between chunks.
200
+ */
201
+ createSHA256Hash: () => import("./getSha256.js").Hash;
185
202
  initSchema: typeof initSchema;
186
203
  getMimeType: typeof getMimeType;
187
204
  mimeTypeToFileExt: typeof mimeTypeToFileExt;
@@ -13,6 +13,7 @@ import { keyOf } from "./schema/keyOf.js";
13
13
  import { view } from "./schema/view.js";
14
14
  import { record } from "./schema/record.js";
15
15
  import { file } from "./schema/file.js";
16
+ import { video } from "./schema/video.js";
16
17
  import { fileset } from "./schema/fileset.js";
17
18
  import { date } from "./schema/date.js";
18
19
  import { datetime } from "./schema/datetime.js";
@@ -22,6 +23,7 @@ import { locale } from "./schema/locale.js";
22
23
  import { route } from "./schema/route.js";
23
24
  import { router } from "./schema/router.js";
24
25
  import { imageset } from "./schema/imageset.js";
26
+ import { videoset } from "./schema/videoset.js";
25
27
  import { settings } from "./schema/settings.js";
26
28
  export type InitSchema = {
27
29
  /**
@@ -217,6 +219,41 @@ export type InitSchema = {
217
219
  *
218
220
  */
219
221
  readonly file: typeof file;
222
+ /**
223
+ * Define a video.
224
+ *
225
+ * A video is an object with a `path` and a `mimeType`: an `.mp4` / `.webm`
226
+ * file, or — with `stream: { type: "hls" }` — an HLS stream the Studio
227
+ * transcodes an upload into, in the browser. `width`, `height` and
228
+ * `duration` are read from the file (`npx val validate --fix` adds them);
229
+ * `posterTime`, `startTime`, `endTime`, `hotspot`, `alt` and `captions` are
230
+ * authored in the Studio.
231
+ *
232
+ * @example
233
+ * const schema = s.video({ stream: { type: "hls" } });
234
+ * export default c.define("/example.val.ts", schema, {
235
+ * path: "/public/val/intro_3b9d7/master.m3u8",
236
+ * mimeType: "application/vnd.apple.mpegurl",
237
+ * width: 1920,
238
+ * height: 1080,
239
+ * duration: 42.5,
240
+ * startTime: 2,
241
+ * alt: "The team walking into the office",
242
+ * captions: [
243
+ * { path: "/public/val/intro_en_8f2a1.vtt", srclang: "en", label: "English" },
244
+ * ],
245
+ * });
246
+ *
247
+ * @example
248
+ * // Picked from a set: mimeType, width, height and duration live there.
249
+ * import videosVal from "./videos.val"; // an s.videoset() module
250
+ * const schema = s.video(videosVal);
251
+ * export default c.define("/example.val.ts", schema, {
252
+ * path: "/public/val/videos/intro_51df2.mp4",
253
+ * startTime: 2,
254
+ * });
255
+ */
256
+ readonly video: typeof video;
220
257
  /**
221
258
  * Define a date.
222
259
  *
@@ -362,6 +399,28 @@ export type InitSchema = {
362
399
  * ```
363
400
  */
364
401
  readonly fileset: typeof fileset;
402
+ /**
403
+ * Define a collection of videos. A field picks from it with
404
+ * `s.video(videosVal)`.
405
+ *
406
+ * @example
407
+ * ```typescript
408
+ * const schema = s.videoset({
409
+ * dir: "/public/val/videos",
410
+ * stream: { type: "hls" },
411
+ * });
412
+ * export default c.define("/content/videos.val.ts", schema, {
413
+ * "/public/val/videos/intro_51df2.mp4": {
414
+ * mimeType: "video/mp4",
415
+ * width: 1280,
416
+ * height: 720,
417
+ * duration: 12.5,
418
+ * alt: "The team, introducing itself",
419
+ * },
420
+ * });
421
+ * ```
422
+ */
423
+ readonly videoset: typeof videoset;
365
424
  /**
366
425
  * Define the project's settings.
367
426
  *
@@ -388,59 +447,20 @@ export type InitSchema = {
388
447
  */
389
448
  readonly settings: typeof settings;
390
449
  };
391
- export declare function initSchema(): {
392
- string: <T extends string>(options?: Record<string, never>) => import("./index.js").StringSchema<T>;
393
- boolean: () => import("./index.js").BooleanSchema<boolean>;
394
- array: <S extends import("./schema/index.js").Schema<import("./selector/index.js").SelectorSource>>(schema: S) => import("./index.js").ArraySchema<S, import("./schema/index.js").SelectorOfSchema<S>[]>;
395
- object: <Props extends {
396
- [key: string]: import("./schema/index.js").Schema<import("./selector/index.js").SelectorSource>;
397
- } & {
398
- valPath?: never;
399
- val?: never;
400
- _type?: never;
401
- _ref?: never;
402
- view?: never;
403
- andThen?: never;
404
- assert?: never;
405
- fold?: never;
406
- patch_id?: never;
407
- }>(schema: Props) => import("./index.js").ObjectSchema<Props, { [key in keyof Props]: import("./schema/index.js").SelectorOfSchema<Props[key]>; }>;
408
- number: (options?: {
409
- max?: number;
410
- min?: number;
411
- }) => import("./index.js").NumberSchema<number>;
412
- discriminatedUnion: <Key extends string, T extends import("./schema/discriminatedUnion.js").DiscriminatedUnionItems<Key>>(key: Key, ...objects: T) => import("./index.js").DiscriminatedUnionSchema<Key, T, import("./index.js").DiscriminatedUnionSourceOf<Key, T>>;
413
- enum: <T extends string>(values_0: T, ...values: T[]) => import("./index.js").EnumSchema<T>;
414
- union: typeof union;
415
- richtext: <O extends import("./index.js").RichTextOptions>(options?: O) => import("./index.js").RichTextSchema<O, import("./index.js").RichTextSource<O>>;
416
- image: typeof image;
417
- literal: <T extends string>(value: T) => import("./index.js").LiteralSchema<T>;
418
- keyOf: <Src extends import("./selector/index.js").GenericSelector<import("./source/index.js").SourceObject> & import("./module.js").ValModuleBrand>(valModule: Src) => import("./index.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("./index.js").RawString : keyof S : S extends Record<string, import("./source/index.js").Source> ? import("./index.js").RawString : never : never>;
419
- view: <M extends import("./selector/index.js").GenericSelector<import("./source/index.js").Source> & import("./module.js").ValModuleBrand>(valModule: M) => import("./index.js").ValViewSchema<import("./module.js").ModuleIdOf<M>, M extends import("./selector/index.js").GenericSelector<infer S extends import("./source/index.js").Source, undefined> ? S : never>;
420
- record: typeof record;
421
- file: typeof file;
422
- fileset: (options: import("./schema/fileset.js").FilesetOptions) => import("./index.js").RecordSchema<import("./index.js").ObjectSchema<{
423
- mimeType: import("./index.js").StringSchema<string>;
424
- }, {
425
- mimeType: string;
426
- }>, import("./schema/index.js").Schema<string>, Record<string, import("./schema/fileset.js").FilesetEntryMetadata>>;
427
- date: (options?: Record<string, never>) => import("./index.js").DateSchema<import("./index.js").RawString>;
428
- datetime: (options?: Record<string, never>) => import("./index.js").DateTimeSchema<import("./index.js").RawString>;
429
- color: (options?: import("./index.js").ColorOptions) => import("./index.js").ColorSchema<import("./index.js").RawString>;
430
- code: (options?: import("./index.js").CodeOptions) => import("./index.js").CodeSchema<import("./index.js").RawString>;
431
- locale: () => import("./index.js").LocaleSchema<string>;
432
- route: <T extends string>(options?: Record<string, never>) => import("./index.js").RouteSchema<T>;
433
- router: typeof router;
434
- imageset: <Accept extends `image/${string}`, Alt extends import("./schema/imageset.js").AltSchema = import("./index.js").StringSchema<string | null>>(options: import("./schema/imageset.js").ImagesetOptions<Accept, Alt>) => import("./index.js").RecordSchema<import("./index.js").ObjectSchema<{
435
- width: import("./index.js").NumberSchema<number>;
436
- height: import("./index.js").NumberSchema<number>;
437
- mimeType: import("./index.js").StringSchema<string>;
438
- alt: Alt;
439
- }, {
440
- width: number;
441
- height: number;
442
- mimeType: string;
443
- alt: import("./schema/index.js").SelectorOfSchema<Alt>;
444
- }>, import("./schema/index.js").Schema<string>, Record<string, import("./schema/imageset.js").ImagesetEntryMetadata<import("./schema/imageset.js").AltSourceOf<Alt>>>>;
445
- settings: typeof settings;
450
+ export type InitSchemaOptions = {
451
+ /**
452
+ * Every media schema is remote: `files.remote` in `val.config.ts`.
453
+ *
454
+ * `s.image()`, `s.file()`, `s.video()`, `s.imageset()`, `s.fileset()` and
455
+ * `s.videoset()` come back with `.remote()` already applied, and so does the
456
+ * image `s.richtext({ img: true })` builds for itself. Nothing downstream
457
+ * needs to know: the Studio, the MCP tools, validation and publishing all
458
+ * read `remote` off the schema, exactly as they do for a `.remote()` written
459
+ * by hand.
460
+ *
461
+ * It is the factories that change, not the schemas, so a schema built here
462
+ * is indistinguishable from one written with `.remote()`.
463
+ */
464
+ remote?: boolean;
446
465
  };
466
+ export declare function initSchema(options?: InitSchemaOptions): InitSchema;
@@ -76,7 +76,30 @@ export type ValConfig = {
76
76
  project?: string;
77
77
  root?: string;
78
78
  files?: {
79
- directory: ConfigDirectory;
79
+ /**
80
+ * Store every image, file and video on Val's remote content host instead
81
+ * of in the repository.
82
+ *
83
+ * `s.image()`, `s.file()`, `s.video()`, `s.imageset()`, `s.fileset()`,
84
+ * `s.videoset()` and `s.richtext({ img: true })` all behave as though
85
+ * `.remote()` had been written on them. Required for a project in the Val
86
+ * app, which has no repository for the bytes to go into: there the Val
87
+ * server refuses to start without it.
88
+ *
89
+ * It is set here, in the project, rather than by the environment, so that
90
+ * every tool reads the project the same way. `val validate`, the VS Code
91
+ * extension and `pnpm dev` evaluate this file too, and a switch only the
92
+ * app could see would have them calling every remote image a mistake --
93
+ * and `val validate --fix` downloading it back into the repository.
94
+ *
95
+ * @example
96
+ * import { initVal } from "@valbuild/core";
97
+ * const { s, c, val, config } = initVal({
98
+ * project: "myorg/myproject",
99
+ * files: { remote: true },
100
+ * });
101
+ */
102
+ remote?: boolean;
80
103
  };
81
104
  gitCommit?: string;
82
105
  gitBranch?: string;
@@ -146,7 +169,7 @@ export type InitVal = {
146
169
  * import { initVal } from "@valbuild/core";
147
170
  * const { s, c, val, config } = initVal({
148
171
  * project: "myorg/myproject",
149
- * files: { directory: "/public/val" },
172
+ * files: { remote: true },
150
173
  * });
151
174
  * export { s, c, val, config };
152
175
  */
@@ -1,5 +1,6 @@
1
1
  import { SerializedFileSchema } from "../schema/file.js";
2
2
  import { SerializedImageSchema } from "../schema/image.js";
3
+ import { SerializedVideoSchema } from "../schema/video.js";
3
4
  /**
4
5
  * The validation basis is used in remote refs to determine if the remote content needs to be re-validated.
5
6
  *
@@ -9,5 +10,5 @@ import { SerializedImageSchema } from "../schema/image.js";
9
10
  * This way we can rename a file, without having to re-validate it.
10
11
  * The version is outside of the validation hash, so that it is possible to manually fix the version without having to re-validate.
11
12
  */
12
- export declare function getValidationBasis(coreVersion: string, schema: SerializedImageSchema | SerializedFileSchema, fileExt: string, metadata: Record<string, unknown> | undefined, fileHash: string): string;
13
- export declare function getValidationHash(coreVersion: string, schema: SerializedImageSchema | SerializedFileSchema, fileExt: string, metadata: Record<string, unknown> | undefined, fileHash: string, textEncoder: TextEncoder): string;
13
+ export declare function getValidationBasis(coreVersion: string, schema: SerializedImageSchema | SerializedFileSchema | SerializedVideoSchema, fileExt: string, metadata: Record<string, unknown> | undefined, fileHash: string): string;
14
+ export declare function getValidationHash(coreVersion: string, schema: SerializedImageSchema | SerializedFileSchema | SerializedVideoSchema, fileExt: string, metadata: Record<string, unknown> | undefined, fileHash: string, textEncoder: TextEncoder): string;
@@ -138,3 +138,10 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
138
138
  export declare function file(galleryModule: ValModule<Record<string, FilesetEntryMetadata>>): FileSchema<GalleryFileSource>;
139
139
  /** A file of its own, carrying its own mime type. */
140
140
  export declare function file(options?: FileOptions): FileSchema<FileSource>;
141
+ /**
142
+ * `s.file()` for a project whose media is all remote (`files.remote` in
143
+ * `val.config.ts`): the same schema, with `.remote()` already applied. See
144
+ * `initSchema`.
145
+ */
146
+ export declare function remoteFile(galleryModule: ValModule<Record<string, FilesetEntryMetadata>>): FileSchema<GalleryFileSource>;
147
+ export declare function remoteFile(options?: FileOptions): FileSchema<FileSource>;
@@ -1,4 +1,4 @@
1
- import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
1
+ import { CustomValidateFunction, Schema, SchemaAssertResult } from "./index.js";
2
2
  import { GalleryImageSource, ImageSource } from "../source/media.js";
3
3
  import { ModulePath, SourcePath } from "../val/index.js";
4
4
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
@@ -198,7 +198,7 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
198
198
  preview(select: ItemPreviewInput<Src>): ImageSchema<Src>;
199
199
  protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
200
200
  protected declaresItemPreview(): boolean;
201
- protected executeSerialize(): SerializedSchema;
201
+ protected executeSerialize(): SerializedImageSchema;
202
202
  }
203
203
  /**
204
204
  * An image picked from a gallery. Its dimensions and mime type live in the
@@ -207,3 +207,10 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
207
207
  export declare function image(galleryModule: ValModule<Record<string, ImagesetEntryMetadata<AltSource>>>, galleryOptions?: GalleryImageOptions): ImageSchema<GalleryImageSource>;
208
208
  /** An image of its own, carrying its own dimensions and mime type. */
209
209
  export declare function image(options?: ImageOptions): ImageSchema<ImageSource>;
210
+ /**
211
+ * `s.image()` for a project whose media is all remote (`files.remote` in
212
+ * `val.config.ts`): the same schema, with `.remote()` already applied. See
213
+ * `initSchema`.
214
+ */
215
+ export declare function remoteImage(galleryModule: ValModule<Record<string, ImagesetEntryMetadata<AltSource>>>, galleryOptions?: GalleryImageOptions): ImageSchema<GalleryImageSource>;
216
+ export declare function remoteImage(options?: ImageOptions): ImageSchema<ImageSource>;
@@ -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: "files" | "images";
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?: "files" | "images";
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
- constructor(options: O & ValidationOptions, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null);
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];