@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.
Files changed (31) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/declarations/src/getSha256.d.ts +55 -0
  3. package/dist/declarations/src/index.d.ts +25 -4
  4. package/dist/declarations/src/initSchema.d.ts +106 -55
  5. package/dist/declarations/src/initVal.d.ts +25 -2
  6. package/dist/declarations/src/mimeType/font.d.ts +36 -0
  7. package/dist/declarations/src/mimeType/index.d.ts +1 -0
  8. package/dist/declarations/src/remote/validationBasis.d.ts +3 -2
  9. package/dist/declarations/src/schema/file.d.ts +7 -0
  10. package/dist/declarations/src/schema/font.d.ts +16 -0
  11. package/dist/declarations/src/schema/fontset.d.ts +40 -0
  12. package/dist/declarations/src/schema/image.d.ts +9 -2
  13. package/dist/declarations/src/schema/index.d.ts +2 -1
  14. package/dist/declarations/src/schema/record.d.ts +16 -2
  15. package/dist/declarations/src/schema/richtext.d.ts +20 -1
  16. package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
  17. package/dist/declarations/src/schema/video.d.ts +249 -0
  18. package/dist/declarations/src/schema/videoset.d.ts +141 -0
  19. package/dist/declarations/src/source/galleries.d.ts +20 -0
  20. package/dist/declarations/src/source/media.d.ts +172 -12
  21. package/dist/declarations/src/val/index.d.ts +4 -2
  22. package/dist/{index-eb9f6480.esm.js → index-02a1ba06.esm.js} +1708 -150
  23. package/dist/{index-7d334094.cjs.prod.js → index-46c72f01.cjs.prod.js} +1713 -149
  24. package/dist/{index-90000f7a.cjs.dev.js → index-aac8c69c.cjs.dev.js} +1713 -149
  25. package/dist/valbuild-core.cjs.dev.js +7 -1
  26. package/dist/valbuild-core.cjs.prod.js +7 -1
  27. package/dist/valbuild-core.esm.js +1 -1
  28. package/package.json +1 -1
  29. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  30. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  31. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -42,6 +42,119 @@ export type GalleryFileSource = {
42
42
  export type FileSource = GalleryFileSource & {
43
43
  readonly mimeType?: string;
44
44
  };
45
+ /**
46
+ * The still shown before a video plays — `<video poster>`.
47
+ *
48
+ * Derived, like a video's `width` and `height`: the Studio grabs the frame at
49
+ * {@link VideoSource.posterTime} and uploads it as an image, so a page can show
50
+ * it without decoding the video. It is a media object of its own (a `path`,
51
+ * and a `patch_id` while unpublished) because it is a file of its own.
52
+ */
53
+ export type VideoPosterSource = {
54
+ readonly path: string;
55
+ readonly width?: number;
56
+ readonly height?: number;
57
+ readonly mimeType?: string;
58
+ readonly patch_id?: string;
59
+ };
60
+ /**
61
+ * One text track of a video — `<track src srclang label kind default>`.
62
+ *
63
+ * `path` is a WebVTT file, uploaded like any other file.
64
+ */
65
+ export type VideoCaptionSource = {
66
+ readonly path: string;
67
+ /** BCP 47 language tag of the track, e.g. `en` or `nb-NO`. */
68
+ readonly srclang: string;
69
+ /** What a viewer picks from the player's caption menu, e.g. "English". */
70
+ readonly label?: string;
71
+ /**
72
+ * `subtitles` translate the dialogue; `captions` also describe the sound,
73
+ * for viewers who cannot hear it. @default "subtitles"
74
+ */
75
+ readonly kind?: "subtitles" | "captions";
76
+ /** Shown without the viewer turning it on. At most one track may say so. */
77
+ readonly default?: boolean;
78
+ readonly patch_id?: string;
79
+ };
80
+ /**
81
+ * What a video FIELD authors, on top of the file it names: the description,
82
+ * the crop, the poster, the trim and the caption tracks.
83
+ *
84
+ * This is the whole value of a field picked from an `s.videoset()`
85
+ * (`s.video(videosetVal)`) — the mime type, size and length live in the set,
86
+ * keyed by `path`, and repeating them here is how two copies of one fact get
87
+ * to disagree. The poster, captions and times stay with the FIELD: one video
88
+ * used in two places can be trimmed and captioned differently in each.
89
+ *
90
+ * Times are in seconds from the start of the file.
91
+ */
92
+ export type GalleryVideoSource = {
93
+ readonly path: string;
94
+ /** Describes the video for someone who cannot see it. */
95
+ readonly alt?: string;
96
+ /** What must stay in frame when a page crops the video (`object-position`). */
97
+ readonly hotspot?: MediaHotspot;
98
+ /** Seconds. The frame the poster was taken from. */
99
+ readonly posterTime?: number;
100
+ readonly poster?: VideoPosterSource;
101
+ /** Seconds. Where playback starts. */
102
+ readonly startTime?: number;
103
+ /** Seconds. Where playback stops. */
104
+ readonly endTime?: number;
105
+ readonly captions?: readonly VideoCaptionSource[];
106
+ /**
107
+ * Set on a source whose bytes are not committed yet. Injected server-side and
108
+ * consumed only by {@link mediaUrl} — never written to a `.val.ts`.
109
+ */
110
+ readonly patch_id?: string;
111
+ };
112
+ /**
113
+ * What one page chooses about a video — as opposed to what is true of the
114
+ * file. A field (`s.video()`) carries these for itself; the entry of a set
115
+ * (`s.videoset()`) carries them as DEFAULTS, which every field picked from the
116
+ * set starts from and may override key by key. See {@link fillFromGallery}.
117
+ */
118
+ export type VideoDefaults = Pick<GalleryVideoSource, "hotspot" | "posterTime" | "poster" | "startTime" | "endTime" | "captions">;
119
+ /**
120
+ * A video: a progressive file (`video/mp4`, `video/webm`) or an HLS stream
121
+ * (`application/vnd.apple.mpegurl`, whose `path` is the master playlist).
122
+ *
123
+ * `mimeType` is REQUIRED on a video of its own: a page has to know which of
124
+ * the two it is holding before it can play it at all — an `.m3u8` handed to a
125
+ * `<video src>` plays only in Safari. (What tells a video's TYPE apart from an
126
+ * image's is its declared keys, see {@link IsVideoSource}, because a field
127
+ * picked from a set has no `mimeType` of its own.)
128
+ *
129
+ * `width`, `height`, `duration` and `mimeType` are read from the bytes;
130
+ * everything else is authored.
131
+ */
132
+ export type VideoSource = GalleryVideoSource & {
133
+ readonly mimeType: string;
134
+ readonly width?: number;
135
+ readonly height?: number;
136
+ /** Seconds. */
137
+ readonly duration?: number;
138
+ };
139
+ /**
140
+ * Whether a source TYPE is a video's.
141
+ *
142
+ * Every media source is "a `path` plus optional fields", so assignability
143
+ * cannot tell an image from a video: an image is assignable to a set-backed
144
+ * video and the other way round. The keys a type DECLARES can — only a video
145
+ * declares `posterTime` — so the readers' conditional types ask this before
146
+ * they ask about images.
147
+ */
148
+ export type IsVideoSource<T> = T extends {
149
+ readonly path: string;
150
+ } ? "posterTime" extends keyof T ? true : false : false;
151
+ /** The mime type of an HLS master playlist. */
152
+ export declare const HLS_MIME_TYPE = "application/vnd.apple.mpegurl";
153
+ /** Whether a video is an HLS stream rather than a single progressive file. */
154
+ export declare function isHlsVideo(src: {
155
+ readonly path: string;
156
+ readonly mimeType?: string;
157
+ }): boolean;
45
158
  /**
46
159
  * The structural supertype of every media source.
47
160
  *
@@ -50,7 +163,7 @@ export type FileSource = GalleryFileSource & {
50
163
  * `Source` excludes `undefined` — an object with optional properties does not
51
164
  * satisfy it.
52
165
  */
53
- export type MediaSource = ImageSource;
166
+ export type MediaSource = ImageSource | VideoSource;
54
167
  /** A path is remote unless it is under `/public`. */
55
168
  export declare function isRemoteMediaPath(path: string): boolean;
56
169
  /**
@@ -76,23 +189,70 @@ export declare function resolveMedia<S extends {
76
189
  url: string;
77
190
  };
78
191
  /**
79
- * Fill in what a gallery-backed field does not carry itself.
192
+ * A video as a reader sees it: every file it names — the video itself, its
193
+ * poster and each caption track — carries the URL its bytes are served from.
80
194
  *
81
- * `s.image(galleryModule)` stores only `{path, alt?, hotspot?}` — the
82
- * dimensions and mime type live in the gallery module, keyed by path. This is
83
- * the single implementation of that lookup; core, stega/RSC and the Studio all
84
- * call it rather than each rolling their own.
195
+ * Each gets its own {@link mediaUrl} because each is its own file with its own
196
+ * `patch_id`: replacing only the poster drafts the poster, and the video keeps
197
+ * its published URL.
198
+ */
199
+ export type ResolvedVideo<S extends GalleryVideoSource = VideoSource> = Omit<S, "poster" | "captions"> & {
200
+ readonly url: string;
201
+ readonly poster?: VideoPosterSource & {
202
+ readonly url: string;
203
+ };
204
+ readonly captions?: readonly (VideoCaptionSource & {
205
+ readonly url: string;
206
+ })[];
207
+ };
208
+ /**
209
+ * The single implementation of {@link ResolvedVideo}. `url` is passed in as a
210
+ * function so the reader can tag the video's own URL (stega) without tagging
211
+ * the poster's and the captions', which are not where an edit lands.
212
+ */
213
+ export declare function resolveVideo<S extends GalleryVideoSource>(src: S, videoUrl?: (src: S) => string): ResolvedVideo<S>;
214
+ /**
215
+ * What a gallery entry holds as a DEFAULT for the fields picked from it, in
216
+ * the groups a field overrides together.
85
217
  *
86
- * `alt` is filled in too, because a gallery holds the alt text an editor typed
87
- * once for a file used in several places — but only when the field does not
88
- * have its own, so a per-image override wins. A gallery whose `alt` schema is a
89
- * locale record holds an object rather than a string; that is left alone rather
90
- * than copied into a field typed `string`, and making the override
91
- * locale-shaped is a separate change.
218
+ * Per key, not per value: a field that has set its own `startTime` still
219
+ * takes the gallery's `endTime`. The poster is the one pair — `poster` is the
220
+ * frame at `posterTime`, so a field with either has chosen its own still, and
221
+ * taking the other half from the gallery would describe a frame nobody chose.
222
+ */
223
+ export declare const GALLERY_DEFAULT_GROUPS: readonly (readonly string[])[];
224
+ /**
225
+ * The gallery entry a gallery-backed value names, or `null` when it is not
226
+ * gallery-backed or the gallery has no such entry.
92
227
  *
93
228
  * The double lookup is load-bearing: a remote gallery keys its entries by the
94
229
  * remote URL while the file itself stays on disk under its local path.
95
230
  */
231
+ export declare function galleryEntryOf(src: {
232
+ readonly path: string;
233
+ }, schema: unknown, getModuleSource: (modulePath: string) => unknown): Record<string, unknown> | null;
234
+ /**
235
+ * The value a gallery-backed field means: its own keys, and the gallery's
236
+ * where it has none.
237
+ *
238
+ * `s.image(galleryModule)` and `s.video(videosModule)` store only what one
239
+ * page chose — the rest lives in the gallery, keyed by path. Two things come
240
+ * from there:
241
+ *
242
+ * - **What is true of the file** — dimensions, mime type, length. The field
243
+ * never has these, so they are always the gallery's.
244
+ * - **The defaults** — description, focal point, a video's poster, start, end
245
+ * and captions ({@link GALLERY_DEFAULT_GROUPS}). An editor sets them once
246
+ * on the gallery entry; a field that sets its own wins, KEY BY KEY.
247
+ *
248
+ * The single implementation of that merge: core, stega/RSC and the Studio all
249
+ * call it rather than each rolling their own, so a page and the Studio's
250
+ * preview of it cannot disagree about which poster a video has.
251
+ *
252
+ * A gallery whose `alt` schema is a locale record holds an object rather than
253
+ * a string; that is left alone rather than copied into a field typed
254
+ * `string`, and making the override locale-shaped is a separate change.
255
+ */
96
256
  export declare function fillFromGallery<S extends {
97
257
  readonly path: string;
98
258
  }>(src: S, schema: unknown, getModuleSource: (modulePath: string) => unknown): S;
@@ -4,13 +4,15 @@ import { Val as ArrayVal } from "./array.js";
4
4
  import { Val as PrimitiveVal } from "./primitive.js";
5
5
  import { Json, JsonArray, JsonObject, JsonPrimitive } from "../Json.js";
6
6
  import { Path, Selector } from "../selector/index.js";
7
- import { MediaSource } from "../source/media.js";
7
+ import { GalleryVideoSource, IsVideoSource, MediaSource, ResolvedVideo } from "../source/media.js";
8
8
  export type SerializedVal = {
9
9
  val: SerializedVal | Json;
10
10
  valPath: SourcePath | undefined;
11
11
  };
12
12
  export declare function isSerializedVal(val: unknown): val is SerializedVal;
13
- export type JsonOfSource<T extends Source> = Json extends T ? Json : T extends MediaSource ? T & {
13
+ export type JsonOfSource<T extends Source> = Json extends T ? Json : T extends GalleryVideoSource ? IsVideoSource<T> extends true ? ResolvedVideo<T> : T & {
14
+ url: string;
15
+ } : T extends MediaSource ? T & {
14
16
  url: string;
15
17
  } : T extends SourceObject ? {
16
18
  [key in keyof T]: JsonOfSource<T[key]>;