@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
@@ -2752,6 +2752,28 @@ var discriminatedUnion = function discriminatedUnion(key) {
2752
2752
  return new DiscriminatedUnionSchema(key, objects);
2753
2753
  };
2754
2754
 
2755
+ function _objectWithoutPropertiesLoose(r, e) {
2756
+ if (null == r) return {};
2757
+ var t = {};
2758
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
2759
+ if (-1 !== e.indexOf(n)) continue;
2760
+ t[n] = r[n];
2761
+ }
2762
+ return t;
2763
+ }
2764
+
2765
+ function _objectWithoutProperties(e, t) {
2766
+ if (null == e) return {};
2767
+ var o,
2768
+ r,
2769
+ i = _objectWithoutPropertiesLoose(e, t);
2770
+ if (Object.getOwnPropertySymbols) {
2771
+ var n = Object.getOwnPropertySymbols(e);
2772
+ for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
2773
+ }
2774
+ return i;
2775
+ }
2776
+
2755
2777
  var RegEx$1 = /^(https?:\/\/[^/]+)\/file\/p\/([^/]+)\/b\/([^/]+)\/v\/([^/]+)\/h\/([^/]+)\/f\/([^/]+)\/p\/(.+)$/;
2756
2778
  function splitRemoteRef(ref) {
2757
2779
  if (ref[0] === "/") {
@@ -2793,6 +2815,8 @@ function splitRemoteRef(ref) {
2793
2815
  };
2794
2816
  }
2795
2817
 
2818
+ var _excluded = ["poster", "captions"];
2819
+
2796
2820
  /** What must stay in frame when a page crops an image. */
2797
2821
 
2798
2822
  /**
@@ -2801,6 +2825,76 @@ function splitRemoteRef(ref) {
2801
2825
  * here. One place per fact.
2802
2826
  */
2803
2827
 
2828
+ /**
2829
+ * The still shown before a video plays — `<video poster>`.
2830
+ *
2831
+ * Derived, like a video's `width` and `height`: the Studio grabs the frame at
2832
+ * {@link VideoSource.posterTime} and uploads it as an image, so a page can show
2833
+ * it without decoding the video. It is a media object of its own (a `path`,
2834
+ * and a `patch_id` while unpublished) because it is a file of its own.
2835
+ */
2836
+
2837
+ /**
2838
+ * One text track of a video — `<track src srclang label kind default>`.
2839
+ *
2840
+ * `path` is a WebVTT file, uploaded like any other file.
2841
+ */
2842
+
2843
+ /**
2844
+ * What a video FIELD authors, on top of the file it names: the description,
2845
+ * the crop, the poster, the trim and the caption tracks.
2846
+ *
2847
+ * This is the whole value of a field picked from an `s.videoset()`
2848
+ * (`s.video(videosetVal)`) — the mime type, size and length live in the set,
2849
+ * keyed by `path`, and repeating them here is how two copies of one fact get
2850
+ * to disagree. The poster, captions and times stay with the FIELD: one video
2851
+ * used in two places can be trimmed and captioned differently in each.
2852
+ *
2853
+ * Times are in seconds from the start of the file.
2854
+ */
2855
+
2856
+ /**
2857
+ * What one page chooses about a video — as opposed to what is true of the
2858
+ * file. A field (`s.video()`) carries these for itself; the entry of a set
2859
+ * (`s.videoset()`) carries them as DEFAULTS, which every field picked from the
2860
+ * set starts from and may override key by key. See {@link fillFromGallery}.
2861
+ */
2862
+
2863
+ /**
2864
+ * A video: a progressive file (`video/mp4`, `video/webm`) or an HLS stream
2865
+ * (`application/vnd.apple.mpegurl`, whose `path` is the master playlist).
2866
+ *
2867
+ * `mimeType` is REQUIRED on a video of its own: a page has to know which of
2868
+ * the two it is holding before it can play it at all — an `.m3u8` handed to a
2869
+ * `<video src>` plays only in Safari. (What tells a video's TYPE apart from an
2870
+ * image's is its declared keys, see {@link IsVideoSource}, because a field
2871
+ * picked from a set has no `mimeType` of its own.)
2872
+ *
2873
+ * `width`, `height`, `duration` and `mimeType` are read from the bytes;
2874
+ * everything else is authored.
2875
+ */
2876
+
2877
+ /**
2878
+ * Whether a source TYPE is a video's.
2879
+ *
2880
+ * Every media source is "a `path` plus optional fields", so assignability
2881
+ * cannot tell an image from a video: an image is assignable to a set-backed
2882
+ * video and the other way round. The keys a type DECLARES can — only a video
2883
+ * declares `posterTime` — so the readers' conditional types ask this before
2884
+ * they ask about images.
2885
+ */
2886
+
2887
+ /** The mime type of an HLS master playlist. */
2888
+ var HLS_MIME_TYPE = "application/vnd.apple.mpegurl";
2889
+
2890
+ /** Whether a video is an HLS stream rather than a single progressive file. */
2891
+ function isHlsVideo(src) {
2892
+ if (src.mimeType !== undefined) {
2893
+ return src.mimeType === HLS_MIME_TYPE || src.mimeType.toLowerCase() === "application/x-mpegurl";
2894
+ }
2895
+ return src.path.split("?")[0].toLowerCase().endsWith(".m3u8");
2896
+ }
2897
+
2804
2898
  /**
2805
2899
  * The structural supertype of every media source.
2806
2900
  *
@@ -2854,66 +2948,181 @@ function resolveMedia(src) {
2854
2948
  url: mediaUrl(src)
2855
2949
  });
2856
2950
  }
2951
+
2952
+ /**
2953
+ * A video as a reader sees it: every file it names — the video itself, its
2954
+ * poster and each caption track — carries the URL its bytes are served from.
2955
+ *
2956
+ * Each gets its own {@link mediaUrl} because each is its own file with its own
2957
+ * `patch_id`: replacing only the poster drafts the poster, and the video keeps
2958
+ * its published URL.
2959
+ */
2960
+
2961
+ /**
2962
+ * The single implementation of {@link ResolvedVideo}. `url` is passed in as a
2963
+ * function so the reader can tag the video's own URL (stega) without tagging
2964
+ * the poster's and the captions', which are not where an edit lands.
2965
+ */
2966
+ function resolveVideo(src) {
2967
+ var videoUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : mediaUrl;
2968
+ var poster = src.poster,
2969
+ captions = src.captions,
2970
+ rest = _objectWithoutProperties(src, _excluded);
2971
+ var resolved = _objectSpread2(_objectSpread2({}, rest), {}, {
2972
+ url: videoUrl(src)
2973
+ });
2974
+ if (poster && _typeof(poster) === "object" && typeof poster.path === "string") {
2975
+ return withCaptions(_objectSpread2(_objectSpread2({}, resolved), {}, {
2976
+ poster: _objectSpread2(_objectSpread2({}, poster), {}, {
2977
+ url: mediaUrl(poster)
2978
+ })
2979
+ }), captions);
2980
+ }
2981
+ return withCaptions(resolved, captions);
2982
+ }
2983
+ function withCaptions(resolved, captions) {
2984
+ if (!Array.isArray(captions)) {
2985
+ return resolved;
2986
+ }
2987
+ return _objectSpread2(_objectSpread2({}, resolved), {}, {
2988
+ captions: captions.filter(function (track) {
2989
+ return !!track && _typeof(track) === "object" && typeof track.path === "string";
2990
+ }).map(function (track) {
2991
+ return _objectSpread2(_objectSpread2({}, track), {}, {
2992
+ url: mediaUrl(track)
2993
+ });
2994
+ })
2995
+ });
2996
+ }
2857
2997
  function isMediaSchema(schema) {
2858
- return _typeof(schema) === "object" && schema !== null && "type" in schema && (schema.type === "image" || schema.type === "file");
2998
+ return _typeof(schema) === "object" && schema !== null && "type" in schema && (schema.type === "image" || schema.type === "file" || schema.type === "video");
2859
2999
  }
2860
3000
 
2861
3001
  /**
2862
- * Fill in what a gallery-backed field does not carry itself.
2863
- *
2864
- * `s.image(galleryModule)` stores only `{path, alt?, hotspot?}` — the
2865
- * dimensions and mime type live in the gallery module, keyed by path. This is
2866
- * the single implementation of that lookup; core, stega/RSC and the Studio all
2867
- * call it rather than each rolling their own.
3002
+ * What a gallery entry holds as a DEFAULT for the fields picked from it, in
3003
+ * the groups a field overrides together.
2868
3004
  *
2869
- * `alt` is filled in too, because a gallery holds the alt text an editor typed
2870
- * once for a file used in several places — but only when the field does not
2871
- * have its own, so a per-image override wins. A gallery whose `alt` schema is a
2872
- * locale record holds an object rather than a string; that is left alone rather
2873
- * than copied into a field typed `string`, and making the override
2874
- * locale-shaped is a separate change.
3005
+ * Per key, not per value: a field that has set its own `startTime` still
3006
+ * takes the gallery's `endTime`. The poster is the one pair — `poster` is the
3007
+ * frame at `posterTime`, so a field with either has chosen its own still, and
3008
+ * taking the other half from the gallery would describe a frame nobody chose.
3009
+ */
3010
+ var GALLERY_DEFAULT_GROUPS = [["alt"], ["hotspot"], ["poster", "posterTime"], ["startTime"], ["endTime"], ["captions"]];
3011
+
3012
+ /** What is true of the file, and the gallery's alone. */
3013
+ var GALLERY_FILE_FACTS = ["width", "height", "mimeType", "duration"];
3014
+
3015
+ /**
3016
+ * The gallery entry a gallery-backed value names, or `null` when it is not
3017
+ * gallery-backed or the gallery has no such entry.
2875
3018
  *
2876
3019
  * The double lookup is load-bearing: a remote gallery keys its entries by the
2877
3020
  * remote URL while the file itself stays on disk under its local path.
2878
3021
  */
2879
- function fillFromGallery(src, schema, getModuleSource) {
3022
+ function galleryEntryOf(src, schema, getModuleSource) {
2880
3023
  if (!isMediaSchema(schema) || !schema.referencedModule) {
2881
- return src;
3024
+ return null;
2882
3025
  }
2883
3026
  var moduleSource = getModuleSource(schema.referencedModule);
2884
- if (!moduleSource || _typeof(moduleSource) !== "object" || Array.isArray(moduleSource)) {
2885
- return src;
3027
+ if (!isPlainObject(moduleSource)) {
3028
+ return null;
2886
3029
  }
2887
- var entries = moduleSource;
2888
- var key = src.path in entries ? src.path : null;
3030
+ var key = src.path in moduleSource ? src.path : null;
2889
3031
  if (key === null) {
2890
3032
  var splitRemoteRefRes = splitRemoteRef(src.path);
2891
- if (splitRemoteRefRes.status === "success" && splitRemoteRefRes.filePath in entries) {
3033
+ if (splitRemoteRefRes.status === "success" && splitRemoteRefRes.filePath in moduleSource) {
2892
3034
  key = splitRemoteRefRes.filePath;
2893
3035
  }
2894
3036
  }
2895
3037
  if (key === null) {
2896
- return src;
3038
+ return null;
2897
3039
  }
2898
- var entry = entries[key];
2899
- if (!entry || _typeof(entry) !== "object" || Array.isArray(entry)) {
3040
+ var entry = moduleSource[key];
3041
+ return isPlainObject(entry) ? entry : null;
3042
+ }
3043
+
3044
+ /**
3045
+ * The value a gallery-backed field means: its own keys, and the gallery's
3046
+ * where it has none.
3047
+ *
3048
+ * `s.image(galleryModule)` and `s.video(videosModule)` store only what one
3049
+ * page chose — the rest lives in the gallery, keyed by path. Two things come
3050
+ * from there:
3051
+ *
3052
+ * - **What is true of the file** — dimensions, mime type, length. The field
3053
+ * never has these, so they are always the gallery's.
3054
+ * - **The defaults** — description, focal point, a video's poster, start, end
3055
+ * and captions ({@link GALLERY_DEFAULT_GROUPS}). An editor sets them once
3056
+ * on the gallery entry; a field that sets its own wins, KEY BY KEY.
3057
+ *
3058
+ * The single implementation of that merge: core, stega/RSC and the Studio all
3059
+ * call it rather than each rolling their own, so a page and the Studio's
3060
+ * preview of it cannot disagree about which poster a video has.
3061
+ *
3062
+ * A gallery whose `alt` schema is a locale record holds an object rather than
3063
+ * a string; that is left alone rather than copied into a field typed
3064
+ * `string`, and making the override locale-shaped is a separate change.
3065
+ */
3066
+ function fillFromGallery(src, schema, getModuleSource) {
3067
+ var entry = galleryEntryOf(src, schema, getModuleSource);
3068
+ if (entry === null) {
2900
3069
  return src;
2901
3070
  }
2902
- var _ref = entry,
2903
- width = _ref.width,
2904
- height = _ref.height,
2905
- mimeType = _ref.mimeType,
2906
- alt = _ref.alt;
2907
- var hasOwnAlt = typeof src.alt === "string";
2908
- return _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, src), width !== undefined ? {
2909
- width: width
2910
- } : {}), height !== undefined ? {
2911
- height: height
2912
- } : {}), mimeType !== undefined ? {
2913
- mimeType: mimeType
2914
- } : {}), !hasOwnAlt && typeof alt === "string" ? {
2915
- alt: alt
2916
- } : {});
3071
+ var own = _objectSpread2({}, src);
3072
+ var filled = {};
3073
+ var _iterator = _createForOfIteratorHelper(GALLERY_FILE_FACTS),
3074
+ _step;
3075
+ try {
3076
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
3077
+ var key = _step.value;
3078
+ if (entry[key] !== undefined) {
3079
+ filled[key] = entry[key];
3080
+ }
3081
+ }
3082
+ } catch (err) {
3083
+ _iterator.e(err);
3084
+ } finally {
3085
+ _iterator.f();
3086
+ }
3087
+ var _iterator2 = _createForOfIteratorHelper(GALLERY_DEFAULT_GROUPS),
3088
+ _step2;
3089
+ try {
3090
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
3091
+ var group = _step2.value;
3092
+ if (group.some(function (key) {
3093
+ return own[key] !== undefined;
3094
+ })) {
3095
+ continue;
3096
+ }
3097
+ var _iterator3 = _createForOfIteratorHelper(group),
3098
+ _step3;
3099
+ try {
3100
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
3101
+ var _key = _step3.value;
3102
+ var value = entry[_key];
3103
+ if (value === undefined || value === null) {
3104
+ continue;
3105
+ }
3106
+ if (_key === "alt" && typeof value !== "string") {
3107
+ continue;
3108
+ }
3109
+ filled[_key] = value;
3110
+ }
3111
+ } catch (err) {
3112
+ _iterator3.e(err);
3113
+ } finally {
3114
+ _iterator3.f();
3115
+ }
3116
+ }
3117
+ } catch (err) {
3118
+ _iterator2.e(err);
3119
+ } finally {
3120
+ _iterator2.f();
3121
+ }
3122
+ return _objectSpread2(_objectSpread2({}, src), filled);
3123
+ }
3124
+ function isPlainObject(value) {
3125
+ return _typeof(value) === "object" && value !== null && !Array.isArray(value);
2917
3126
  }
2918
3127
 
2919
3128
  var EXT_TO_MIME_TYPES = {
@@ -3393,7 +3602,7 @@ var EXT_TO_MIME_TYPES = {
3393
3602
  sxw: "application/vnd.sun.xml.writer",
3394
3603
  sxg: "application/vnd.sun.xml.writer.global",
3395
3604
  stw: "application/vnd.sun.xml.writer.template",
3396
- otf: "application/x-font-otf",
3605
+ otf: "font/otf",
3397
3606
  osfpvg: "application/vnd.yamaha.openscoreformat.osfpvg+xml",
3398
3607
  dp: "application/vnd.osgi.dp",
3399
3608
  pdb: "application/vnd.palm",
@@ -3444,6 +3653,8 @@ var EXT_TO_MIME_TYPES = {
3444
3653
  qam: "application/vnd.epson.quickanime",
3445
3654
  qfx: "application/vnd.intu.qfx",
3446
3655
  mov: "video/quicktime",
3656
+ vtt: "text/vtt",
3657
+ mkv: "video/x-matroska",
3447
3658
  rar: "application/x-rar-compressed",
3448
3659
  ram: "audio/x-pn-realaudio",
3449
3660
  rmp: "audio/x-pn-realaudio-plugin",
@@ -3541,7 +3752,8 @@ var EXT_TO_MIME_TYPES = {
3541
3752
  mxs: "application/vnd.triscape.mxs",
3542
3753
  t: "text/troff",
3543
3754
  tra: "application/vnd.trueapp",
3544
- ttf: "application/x-font-ttf",
3755
+ ttf: "font/ttf",
3756
+ ttc: "font/collection",
3545
3757
  ttl: "text/turtle",
3546
3758
  umj: "application/vnd.umajin",
3547
3759
  uoml: "application/vnd.uoml+xml",
@@ -3568,7 +3780,8 @@ var EXT_TO_MIME_TYPES = {
3568
3780
  wbmp: "image/vnd.wap.wbmp",
3569
3781
  wav: "audio/x-wav",
3570
3782
  davmount: "application/davmount+xml",
3571
- woff: "application/x-font-woff",
3783
+ woff: "font/woff",
3784
+ woff2: "font/woff2",
3572
3785
  wspolicy: "application/wspolicy+xml",
3573
3786
  webp: "image/webp",
3574
3787
  wtb: "application/vnd.webturbo",
@@ -3670,6 +3883,93 @@ function mimeTypeMatchesAccept(mimeType, accept) {
3670
3883
  });
3671
3884
  }
3672
3885
 
3886
+ /**
3887
+ * Fonts, as far as a mime type can tell.
3888
+ *
3889
+ * The four web formats have IANA types under `font/` (RFC 8081), and those are
3890
+ * what Val stores. Browsers and operating systems disagree about them: a file
3891
+ * picker reports `.woff2` as `font/woff2`, `application/font-woff2` or nothing
3892
+ * at all depending on the platform, and nothing at all reads as
3893
+ * `application/octet-stream` once the file is a data URL. That is why an upload
3894
+ * asks the BYTES ({@link sniffFontMimeType}) rather than trusting the picker.
3895
+ */
3896
+
3897
+ /** The formats `s.fontset()` accepts unless told otherwise, best first. */
3898
+ var FONT_MIME_TYPES = ["font/woff2", "font/woff", "font/ttf", "font/otf"];
3899
+
3900
+ /** `s.fontset()`'s `accept` when none is given. */
3901
+ var DEFAULT_FONT_ACCEPT = FONT_MIME_TYPES.join(",");
3902
+
3903
+ /**
3904
+ * The extension of each font type, for a file input: a picker matches an
3905
+ * `accept` of mime types through the operating system's own table, and where
3906
+ * that table does not know `font/woff2` the file is greyed out.
3907
+ */
3908
+ var FONT_MIME_TYPE_EXTENSIONS = {
3909
+ "font/woff2": ".woff2",
3910
+ "font/woff": ".woff",
3911
+ "font/ttf": ".ttf",
3912
+ "font/otf": ".otf",
3913
+ "font/collection": ".ttc"
3914
+ };
3915
+
3916
+ /**
3917
+ * The names the extension table used for fonts before it used RFC 8081's.
3918
+ * Content written then still carries them, and it is the same file.
3919
+ */
3920
+ var LEGACY_FONT_MIME_TYPES = {
3921
+ "application/x-font-ttf": "font/ttf",
3922
+ "application/x-font-otf": "font/otf",
3923
+ "application/x-font-woff": "font/woff"
3924
+ };
3925
+
3926
+ /** `mimeType`, with a legacy font name read as its `font/` type. */
3927
+ function canonicalFontMimeType(mimeType) {
3928
+ var _LEGACY_FONT_MIME_TYP;
3929
+ return (_LEGACY_FONT_MIME_TYP = LEGACY_FONT_MIME_TYPES[mimeType]) !== null && _LEGACY_FONT_MIME_TYP !== void 0 ? _LEGACY_FONT_MIME_TYP : mimeType;
3930
+ }
3931
+
3932
+ /**
3933
+ * Whether a file of `mimeType` is a font a browser can draw: the `font/` types,
3934
+ * and the pre-RFC 8081 names still found in older data.
3935
+ */
3936
+ function isFontMimeType(mimeType) {
3937
+ if (!mimeType) {
3938
+ return false;
3939
+ }
3940
+ return mimeType.startsWith("font/") || mimeType.startsWith("application/x-font-") || mimeType === "application/font-woff" || mimeType === "application/font-woff2" || mimeType === "application/font-sfnt" || mimeType === "application/vnd.ms-opentype";
3941
+ }
3942
+
3943
+ /**
3944
+ * The font type of `bytes`, read from the signature every font format starts
3945
+ * with, or `null` when they are not a font.
3946
+ *
3947
+ * Only the first four bytes are needed: the WOFF signatures (`wOFF`, `wOF2`),
3948
+ * the SFNT versions of TrueType (`0x00010000`, or `true` from old Apple fonts)
3949
+ * and of CFF-flavoured OpenType (`OTTO`), and a collection's tag (`ttcf`).
3950
+ */
3951
+ function sniffFontMimeType(bytes) {
3952
+ if (bytes.length < 4) {
3953
+ return null;
3954
+ }
3955
+ var tag = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3]);
3956
+ switch (tag) {
3957
+ case "wOF2":
3958
+ return "font/woff2";
3959
+ case "wOFF":
3960
+ return "font/woff";
3961
+ case "OTTO":
3962
+ return "font/otf";
3963
+ case "true":
3964
+ case "\0\x01\0\0":
3965
+ return "font/ttf";
3966
+ case "ttcf":
3967
+ return "font/collection";
3968
+ default:
3969
+ return null;
3970
+ }
3971
+ }
3972
+
3673
3973
  /**
3674
3974
  * How an uploaded image is re-encoded in the browser, before it is uploaded.
3675
3975
  *
@@ -4095,6 +4395,19 @@ var ImageSchema = /*#__PURE__*/function (_Schema) {
4095
4395
  /** An image of its own, carrying its own dimensions and mime type. */
4096
4396
 
4097
4397
  function image(options, galleryOptions) {
4398
+ return imageOf(false, options, galleryOptions);
4399
+ }
4400
+
4401
+ /**
4402
+ * `s.image()` for a project whose media is all remote (`files.remote` in
4403
+ * `val.config.ts`): the same schema, with `.remote()` already applied. See
4404
+ * `initSchema`.
4405
+ */
4406
+
4407
+ function remoteImage(options, galleryOptions) {
4408
+ return imageOf(true, options, galleryOptions);
4409
+ }
4410
+ function imageOf(remote, options, galleryOptions) {
4098
4411
  var isModule = !!options && !!Internal.getValPath(options);
4099
4412
  if (isModule) {
4100
4413
  var allModules = {};
@@ -4108,9 +4421,9 @@ function image(options, galleryOptions) {
4108
4421
  }
4109
4422
  return new ImageSchema((galleryOptions === null || galleryOptions === void 0 ? void 0 : galleryOptions.encode) !== undefined ? {
4110
4423
  encode: galleryOptions.encode
4111
- } : {}, false, false, [], allModules);
4424
+ } : {}, false, remote, [], allModules);
4112
4425
  }
4113
- return new ImageSchema(options);
4426
+ return new ImageSchema(options, false, remote);
4114
4427
  }
4115
4428
 
4116
4429
  var RouteSchema = /*#__PURE__*/function (_Schema) {
@@ -4369,6 +4682,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4369
4682
  var isHidden = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
4370
4683
  var description = arguments.length > 5 ? arguments[5] : undefined;
4371
4684
  var previewInput = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;
4685
+ var imgRemote = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;
4372
4686
  _classCallCheck(this, RichTextSchema);
4373
4687
  _this = _callSuper(this, RichTextSchema);
4374
4688
  _this.options = options;
@@ -4378,6 +4692,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4378
4692
  _this.isHidden = isHidden;
4379
4693
  _this.description = description;
4380
4694
  _this.previewInput = previewInput;
4695
+ _this.imgRemote = imgRemote;
4381
4696
  return _this;
4382
4697
  }
4383
4698
 
@@ -4404,7 +4719,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4404
4719
  return _createClass(RichTextSchema, [{
4405
4720
  key: "describe",
4406
4721
  value: function describe(description) {
4407
- return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.previewInput);
4722
+ return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.previewInput, this.imgRemote);
4408
4723
  }
4409
4724
 
4410
4725
  /**
@@ -4424,7 +4739,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4424
4739
  value: function maxLength(max) {
4425
4740
  return new RichTextSchema(_objectSpread2(_objectSpread2({}, this.options), {}, {
4426
4741
  maxLength: max
4427
- }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.previewInput);
4742
+ }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.previewInput, this.imgRemote);
4428
4743
  }
4429
4744
 
4430
4745
  /**
@@ -4444,7 +4759,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4444
4759
  value: function minLength(min) {
4445
4760
  return new RichTextSchema(_objectSpread2(_objectSpread2({}, this.options), {}, {
4446
4761
  minLength: min
4447
- }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.previewInput);
4762
+ }), this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.previewInput, this.imgRemote);
4448
4763
  }
4449
4764
 
4450
4765
  /**
@@ -4472,7 +4787,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4472
4787
  }, {
4473
4788
  key: "validate",
4474
4789
  value: function validate(validationFunction) {
4475
- return new RichTextSchema(this.options, this.opt, [].concat(_toConsumableArray(this.customValidateFunctions), [validationFunction]), this.isReadonly, this.isHidden, this.description, this.previewInput);
4790
+ return new RichTextSchema(this.options, this.opt, [].concat(_toConsumableArray(this.customValidateFunctions), [validationFunction]), this.isReadonly, this.isHidden, this.description, this.previewInput, this.imgRemote);
4476
4791
  }
4477
4792
  }, {
4478
4793
  key: "executeValidate",
@@ -4647,7 +4962,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4647
4962
  }
4648
4963
  var srcPath = unsafeCreateSourcePath(path, "src");
4649
4964
  var imgSchema = _this2.options.img;
4650
- var imageValidationErrors = _typeof(imgSchema) === "object" ? imgSchema["executeValidate"](srcPath, node.src) : new ImageSchema({}, false, false)["executeValidate"](srcPath, node.src);
4965
+ var imageValidationErrors = _typeof(imgSchema) === "object" ? imgSchema["executeValidate"](srcPath, node.src) : new ImageSchema({}, false, _this2.imgRemote)["executeValidate"](srcPath, node.src);
4651
4966
  if (imageValidationErrors) {
4652
4967
  for (var _validationErrorPathS in imageValidationErrors) {
4653
4968
  var _current$_validationE;
@@ -4916,19 +5231,19 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4916
5231
  }, {
4917
5232
  key: "nullable",
4918
5233
  value: function nullable() {
4919
- return new RichTextSchema(this.options, true, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.previewInput);
5234
+ return new RichTextSchema(this.options, true, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.previewInput, this.imgRemote);
4920
5235
  }
4921
5236
  }, {
4922
5237
  key: "readonly",
4923
5238
  value: function readonly() {
4924
5239
  var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
4925
- return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.previewInput);
5240
+ return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.previewInput, this.imgRemote);
4926
5241
  }
4927
5242
  }, {
4928
5243
  key: "hidden",
4929
5244
  value: function hidden() {
4930
5245
  var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
4931
- return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.previewInput);
5246
+ return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.previewInput, this.imgRemote);
4932
5247
  }
4933
5248
  }, {
4934
5249
  key: "executeCustomValidateAt",
@@ -4956,7 +5271,7 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
4956
5271
  }, {
4957
5272
  key: "preview",
4958
5273
  value: function preview(select) {
4959
- return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, select);
5274
+ return new RichTextSchema(this.options, this.opt, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, select, this.imgRemote);
4960
5275
  }
4961
5276
  }, {
4962
5277
  key: "executePreviewItem",
@@ -5002,7 +5317,10 @@ var RichTextSchema = /*#__PURE__*/function (_Schema) {
5002
5317
  ul: this.options.ul,
5003
5318
  ol: this.options.ol,
5004
5319
  a: serializeAnchorSchema(),
5005
- img: this.options.img && _typeof(this.options.img) === "object" ? this.options.img["executeSerialize"]() : this.options.img
5320
+ img: this.options.img && _typeof(this.options.img) === "object" ? this.options.img["executeSerialize"]() :
5321
+ // The Studio decides local or remote by the serialized image
5322
+ // schema, and `true` carries no such answer.
5323
+ this.options.img && this.imgRemote ? new ImageSchema(undefined, false, true)["executeSerialize"]() : this.options.img
5006
5324
  };
5007
5325
  return {
5008
5326
  type: "richtext",
@@ -5021,6 +5339,14 @@ var richtext = function richtext(options) {
5021
5339
  return new RichTextSchema(options !== null && options !== void 0 ? options : {});
5022
5340
  };
5023
5341
 
5342
+ /**
5343
+ * `s.richtext()` for a project whose media is all remote (`files.remote` in
5344
+ * `val.config.ts`): `img: true` is a remote image. See `initSchema`.
5345
+ */
5346
+ var remoteRichtext = function remoteRichtext(options) {
5347
+ return new RichTextSchema(options !== null && options !== void 0 ? options : {}, false, [], false, false, undefined, null, true);
5348
+ };
5349
+
5024
5350
  function _regeneratorDefine(e, r, n, t) {
5025
5351
  var i = Object.defineProperty;
5026
5352
  try {
@@ -5469,6 +5795,207 @@ function missingDeclaredKeys(declared, present) {
5469
5795
  });
5470
5796
  }
5471
5797
 
5798
+ /**
5799
+ * The checks of what one page chose about a video — its times, focal point,
5800
+ * poster and captions — shared by the two places those choices are made: a
5801
+ * field (`s.video()`) and the entry of a set (`s.videoset()`), which holds the
5802
+ * same choices as the defaults every field picked from it starts from.
5803
+ */
5804
+
5805
+ function isRecord(value) {
5806
+ return _typeof(value) === "object" && value !== null && !Array.isArray(value);
5807
+ }
5808
+ function stripQuery$1(path) {
5809
+ return path.split("?")[0];
5810
+ }
5811
+
5812
+ /**
5813
+ * What is checked, typed as what it IS rather than what it should be: a
5814
+ * field's value and a set's entry both reach here from hand-written JSON
5815
+ * that never saw the type.
5816
+ */
5817
+
5818
+ function isNonNegativeNumber(value) {
5819
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
5820
+ }
5821
+ function validateTimes(src, duration) {
5822
+ var errors = [];
5823
+ var _iterator = _createForOfIteratorHelper(["posterTime", "startTime", "endTime"]),
5824
+ _step;
5825
+ try {
5826
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
5827
+ var key = _step.value;
5828
+ var value = src[key];
5829
+ if (value === undefined) {
5830
+ continue;
5831
+ }
5832
+ if (!isNonNegativeNumber(value)) {
5833
+ errors.push({
5834
+ message: "'".concat(key, "' must be a number of seconds, 0 or more. Got: ").concat(JSON.stringify(value)),
5835
+ value: src
5836
+ });
5837
+ } else if (duration !== undefined && value > duration) {
5838
+ errors.push({
5839
+ message: "'".concat(key, "' (").concat(value, "s) is after the end of the video (").concat(duration, "s)."),
5840
+ value: src
5841
+ });
5842
+ }
5843
+ }
5844
+ } catch (err) {
5845
+ _iterator.e(err);
5846
+ } finally {
5847
+ _iterator.f();
5848
+ }
5849
+ var startTime = src.startTime,
5850
+ endTime = src.endTime;
5851
+ if (isNonNegativeNumber(startTime) && isNonNegativeNumber(endTime) && startTime >= endTime) {
5852
+ errors.push({
5853
+ message: "'startTime' (".concat(startTime, "s) must be before 'endTime' (").concat(endTime, "s)."),
5854
+ value: src
5855
+ });
5856
+ }
5857
+ return errors;
5858
+ }
5859
+ function validateHotspot(src) {
5860
+ if (src.hotspot === undefined) {
5861
+ return [];
5862
+ }
5863
+ var hotspot = src.hotspot;
5864
+ if (!isRecord(hotspot) || typeof hotspot.x !== "number" || typeof hotspot.y !== "number" || hotspot.x < 0 || hotspot.x > 1 || hotspot.y < 0 || hotspot.y > 1) {
5865
+ return [{
5866
+ message: "'hotspot' must be { x, y } with both between 0 and 1.",
5867
+ value: src
5868
+ }];
5869
+ }
5870
+ return [];
5871
+ }
5872
+ function validatePoster(src) {
5873
+ if (src.poster === undefined) {
5874
+ return [];
5875
+ }
5876
+ var poster = src.poster;
5877
+ if (!isRecord(poster) || typeof poster.path !== "string") {
5878
+ return [{
5879
+ message: "'poster' must be an image object with a 'path'.",
5880
+ value: src
5881
+ }];
5882
+ }
5883
+ var mimeType = filenameToMimeType(stripQuery$1(poster.path));
5884
+ if (!mimeType || !mimeType.startsWith("image/")) {
5885
+ return [{
5886
+ message: "The poster must be an image. Got: ".concat(poster.path),
5887
+ value: src
5888
+ }];
5889
+ }
5890
+ return [];
5891
+ }
5892
+ var CAPTION_KINDS = ["subtitles", "captions"];
5893
+ function validateCaptions(src) {
5894
+ if (src.captions === undefined) {
5895
+ return [];
5896
+ }
5897
+ if (!Array.isArray(src.captions)) {
5898
+ return [{
5899
+ message: "'captions' must be an array.",
5900
+ value: src
5901
+ }];
5902
+ }
5903
+ var errors = [];
5904
+ var defaults = 0;
5905
+ var seen = new Set();
5906
+ src.captions.forEach(function (track, i) {
5907
+ var at = "Caption track ".concat(i + 1);
5908
+ if (!isRecord(track)) {
5909
+ errors.push({
5910
+ message: "".concat(at, " must be an object."),
5911
+ value: src
5912
+ });
5913
+ return;
5914
+ }
5915
+ var t = track;
5916
+ if (typeof t.path !== "string") {
5917
+ errors.push({
5918
+ message: "".concat(at, " has no 'path'."),
5919
+ value: src
5920
+ });
5921
+ } else if (filenameToMimeType(stripQuery$1(t.path)) !== "text/vtt") {
5922
+ errors.push({
5923
+ message: "".concat(at, " must be a WebVTT (.vtt) file. Got: ").concat(t.path),
5924
+ value: src
5925
+ });
5926
+ }
5927
+ if (typeof t.srclang !== "string" || t.srclang.trim() === "") {
5928
+ errors.push({
5929
+ message: "".concat(at, " needs a language ('srclang'), e.g. \"en\"."),
5930
+ value: src
5931
+ });
5932
+ }
5933
+ if (t.label !== undefined && typeof t.label !== "string") {
5934
+ errors.push({
5935
+ message: "".concat(at, ": 'label' must be a string."),
5936
+ value: src
5937
+ });
5938
+ }
5939
+ if (t.kind !== undefined && (typeof t.kind !== "string" || !CAPTION_KINDS.includes(t.kind))) {
5940
+ errors.push({
5941
+ message: "".concat(at, ": 'kind' must be \"subtitles\" or \"captions\"."),
5942
+ value: src
5943
+ });
5944
+ }
5945
+ if (t["default"] !== undefined && typeof t["default"] !== "boolean") {
5946
+ errors.push({
5947
+ message: "".concat(at, ": 'default' must be true or false."),
5948
+ value: src
5949
+ });
5950
+ }
5951
+ if (t["default"] === true) {
5952
+ defaults++;
5953
+ }
5954
+ if (typeof t.srclang === "string") {
5955
+ var _t$kind;
5956
+ var key = "".concat((_t$kind = t.kind) !== null && _t$kind !== void 0 ? _t$kind : "subtitles", ":").concat(t.srclang);
5957
+ if (seen.has(key)) {
5958
+ errors.push({
5959
+ message: "".concat(at, " repeats the language '").concat(t.srclang, "'."),
5960
+ value: src
5961
+ });
5962
+ }
5963
+ seen.add(key);
5964
+ }
5965
+ });
5966
+ if (defaults > 1) {
5967
+ errors.push({
5968
+ message: "At most one caption track can be the default. ".concat(defaults, " are."),
5969
+ value: src
5970
+ });
5971
+ }
5972
+ return errors;
5973
+ }
5974
+
5975
+ function isObject(value) {
5976
+ return _typeof(value) === "object" && value !== null && !Array.isArray(value);
5977
+ }
5978
+
5979
+ /** What a media collection (`s.imageset`, `s.fileset`, `s.videoset`) holds. */
5980
+
5981
+ /**
5982
+ * The fix code a media collection of `type` reports for `check`.
5983
+ *
5984
+ * One function rather than a ternary at each site: there were two kinds of
5985
+ * collection when every site was written as `type === "images" ? … : …`, and
5986
+ * a third kind silently took the files branch.
5987
+ */
5988
+ function mediaCollectionFix(type, check) {
5989
+ return "".concat(type, ":").concat(check);
5990
+ }
5991
+
5992
+ /** The singular a message uses for one entry of a collection of `type`. */
5993
+ var MEDIA_ENTRY_LABEL = {
5994
+ files: "file",
5995
+ images: "image",
5996
+ videos: "video"
5997
+ };
5998
+
5472
5999
  /**
5473
6000
  * `.render(...)` is for records whose entries the generic record editor draws.
5474
6001
  * A router's entries are pages and a gallery's are media, and both have UIs of
@@ -5660,7 +6187,7 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5660
6187
  _iterator.f();
5661
6188
  }
5662
6189
  if (this.mediaOptions) {
5663
- var checkFix = this.mediaOptions.type === "images" ? "images:check-unique-folder" : "files:check-unique-folder";
6190
+ var checkFix = mediaCollectionFix(this.mediaOptions.type, "check-unique-folder");
5664
6191
  var uniqueCheckError = {
5665
6192
  message: "Gallery directory '".concat(this.mediaOptions.dir, "' must be unique across all galleries"),
5666
6193
  value: {
@@ -5678,7 +6205,7 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5678
6205
  } else {
5679
6206
  error = _defineProperty({}, path, [uniqueCheckError]);
5680
6207
  }
5681
- var allFilesCheckFix = this.mediaOptions.type === "images" ? "images:check-all-files" : "files:check-all-files";
6208
+ var allFilesCheckFix = mediaCollectionFix(this.mediaOptions.type, "check-all-files");
5682
6209
  var allFilesCheckError = {
5683
6210
  message: "Directory '".concat(this.mediaOptions.dir, "' may have files not tracked by this gallery"),
5684
6211
  value: {
@@ -5732,7 +6259,7 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5732
6259
  _this2.markKeyErrorsAtPath(keyErr, subPath);
5733
6260
  }
5734
6261
  error = _this2.mergeValidationErrors(error, keyErr);
5735
- var entryErr = _this2.validateMediaEntry(subPath, elem);
6262
+ var entryErr = _this2.validateMediaEntry(subPath, key, elem);
5736
6263
  if (entryErr) {
5737
6264
  _this2.markKeyErrorsAtPath(entryErr, subPath);
5738
6265
  }
@@ -5846,8 +6373,8 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5846
6373
  dir = _this$mediaOptions.dir,
5847
6374
  isRemote = _this$mediaOptions.remote,
5848
6375
  type = _this$mediaOptions.type;
5849
- var mediaLabel = type === "images" ? "images" : "files";
5850
- var checkRemoteFix = type === "images" ? "images:check-remote" : "files:check-remote";
6376
+ var mediaLabel = type;
6377
+ var checkRemoteFix = mediaCollectionFix(type, "check-remote");
5851
6378
  var isRemoteUrl = this.isRemoteUrl(key);
5852
6379
  var isLocalPath = key === dir || key.startsWith(dir + "/");
5853
6380
  if (isRemote) {
@@ -5880,9 +6407,9 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5880
6407
  }]);
5881
6408
  }
5882
6409
  // Local path in a remote gallery: needs to be uploaded to remote.
5883
- var uploadRemoteFix = type === "images" ? "images:upload-remote" : "files:upload-remote";
6410
+ var uploadRemoteFix = mediaCollectionFix(type, "upload-remote");
5884
6411
  return _defineProperty({}, path, [{
5885
- message: "Expected a remote ".concat(type === "images" ? "image" : "file", ", but got a local path. Use Val tooling (CLI --fix, VS Code extension, or Val Studio) to upload it. Got: ").concat(key),
6412
+ message: "Expected a remote ".concat(MEDIA_ENTRY_LABEL[type], ", but got a local path. Use Val tooling (CLI --fix, VS Code extension, or Val Studio) to upload it. Got: ").concat(key),
5886
6413
  value: key,
5887
6414
  fixes: [uploadRemoteFix]
5888
6415
  }]);
@@ -5906,7 +6433,7 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5906
6433
  }
5907
6434
  }, {
5908
6435
  key: "validateMediaEntry",
5909
- value: function validateMediaEntry(path, entry) {
6436
+ value: function validateMediaEntry(path, key, entry) {
5910
6437
  if (!this.mediaOptions) {
5911
6438
  return false;
5912
6439
  }
@@ -5922,14 +6449,48 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5922
6449
  }
5923
6450
  var entryObj = entry;
5924
6451
  var errors = [];
5925
- if (type === "images") {
5926
- // Validate width
5927
- if (typeof entryObj.width !== "number" || entryObj.width <= 0) {
5928
- errors.push({
5929
- message: "Expected 'width' to be a positive number, got '".concat(entryObj.width, "'"),
5930
- value: entry
5931
- });
5932
- }
6452
+ if (type === "videos") {
6453
+ // What is read from the bytes. Missing is a fix (the CLI reads them,
6454
+ // from disk or — for a remote entry — over HTTP with Range requests);
6455
+ // present and wrong is an error, because a fix that overwrote an
6456
+ // authored number would be guessing which of the two was right.
6457
+ var missing = ["mimeType", "width", "height", "duration"].filter(function (k) {
6458
+ return entryObj[k] === undefined;
6459
+ });
6460
+ if (missing.length > 0) {
6461
+ return _defineProperty({}, path, [{
6462
+ message: "Video metadata is missing: ".concat(missing.join(", "), "."),
6463
+ value: entry,
6464
+ fixes: ["videos:add-metadata"]
6465
+ }]);
6466
+ }
6467
+ var _iterator3 = _createForOfIteratorHelper(["width", "height", "duration"]),
6468
+ _step3;
6469
+ try {
6470
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
6471
+ var k = _step3.value;
6472
+ var value = entryObj[k];
6473
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
6474
+ errors.push({
6475
+ message: "Expected '".concat(k, "' to be a positive number, got '").concat(JSON.stringify(value), "'"),
6476
+ value: entry
6477
+ });
6478
+ }
6479
+ }
6480
+ } catch (err) {
6481
+ _iterator3.e(err);
6482
+ } finally {
6483
+ _iterator3.f();
6484
+ }
6485
+ }
6486
+ if (type === "images") {
6487
+ // Validate width
6488
+ if (typeof entryObj.width !== "number" || entryObj.width <= 0) {
6489
+ errors.push({
6490
+ message: "Expected 'width' to be a positive number, got '".concat(entryObj.width, "'"),
6491
+ value: entry
6492
+ });
6493
+ }
5933
6494
 
5934
6495
  // Validate height
5935
6496
  if (typeof entryObj.height !== "number" || entryObj.height <= 0) {
@@ -5946,6 +6507,15 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5946
6507
  message: "Expected 'mimeType' to be a string, got '".concat(_typeof(entryObj.mimeType), "'"),
5947
6508
  value: entry
5948
6509
  });
6510
+ } else if (type === "videos" && entryObj.mimeType === HLS_MIME_TYPE) {
6511
+ // A stream is what the Studio made of an upload that WAS accepted, so
6512
+ // `accept` (which is about what may be picked) is not asked about it.
6513
+ if (!key.split("?")[0].toLowerCase().endsWith(".m3u8")) {
6514
+ errors.push({
6515
+ message: "An HLS video must be keyed by its master playlist (.m3u8). Got: ".concat(key),
6516
+ value: entry
6517
+ });
6518
+ }
5949
6519
  } else {
5950
6520
  var mimeTypeError = this.validateMediaMimeType(entryObj.mimeType, accept);
5951
6521
  if (mimeTypeError) {
@@ -5955,6 +6525,26 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5955
6525
  });
5956
6526
  }
5957
6527
  }
6528
+ if (type === "videos") {
6529
+ // The defaults every field picked from the set starts from, checked as
6530
+ // a field's own would be: they are the same choices, made once.
6531
+ var duration = typeof entryObj.duration === "number" && entryObj.duration > 0 ? entryObj.duration : undefined;
6532
+ errors.push.apply(errors, _toConsumableArray(validateTimes(entryObj, duration)).concat(_toConsumableArray(validateHotspot(entryObj)), _toConsumableArray(validatePoster(entryObj)), _toConsumableArray(validateCaptions(entryObj))));
6533
+ var misplaced = this.misplacedEntryFiles(key, entryObj);
6534
+ if (misplaced.length > 0) {
6535
+ var these = misplaced.length === 1 ? "this file is" : "".concat(misplaced.length, " files are");
6536
+ errors.push(this.mediaOptions.remote ? {
6537
+ message: "Expected the entry's files on Val Remote, but ".concat(these, " stored locally: ").concat(misplaced.join(", ")),
6538
+ // The key, as a local key's upload error carries: the fix
6539
+ // rewrites the entry under it.
6540
+ value: key,
6541
+ fixes: ["videos:upload-remote"]
6542
+ } : {
6543
+ message: "Expected the entry's files under ".concat(this.mediaOptions.dir, ", but ").concat(these, " remote: ").concat(misplaced.join(", ")),
6544
+ value: entry
6545
+ });
6546
+ }
6547
+ }
5958
6548
  if (type === "images") {
5959
6549
  // Validate hotspot if present
5960
6550
  if (entryObj.hotspot !== undefined) {
@@ -5966,7 +6556,8 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5966
6556
  });
5967
6557
  }
5968
6558
  }
5969
-
6559
+ }
6560
+ if (type === "images" || type === "videos") {
5970
6561
  // Validate alt using the alt schema
5971
6562
  var altPath = createValPathOfItem(path, "alt");
5972
6563
  if (altPath && altSchema) {
@@ -5981,6 +6572,49 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
5981
6572
  }
5982
6573
  return false;
5983
6574
  }
6575
+
6576
+ /**
6577
+ * The poster and caption files of a set's entry that are not where the set
6578
+ * keeps its files. Asked only once the key itself is in place: a local key
6579
+ * in a remote set is reported (and uploaded) as the entry, files and all,
6580
+ * and a second error for its poster would be the same one twice.
6581
+ */
6582
+ }, {
6583
+ key: "misplacedEntryFiles",
6584
+ value: function misplacedEntryFiles(key, entry) {
6585
+ var _this3 = this;
6586
+ if (!this.mediaOptions) {
6587
+ return [];
6588
+ }
6589
+ var remote = this.mediaOptions.remote;
6590
+ if (this.isRemoteUrl(key) !== remote) {
6591
+ return [];
6592
+ }
6593
+ var files = [];
6594
+ var poster = entry.poster;
6595
+ if (isObject(poster) && typeof poster.path === "string") {
6596
+ files.push(poster.path);
6597
+ }
6598
+ if (Array.isArray(entry.captions)) {
6599
+ var _iterator4 = _createForOfIteratorHelper(entry.captions),
6600
+ _step4;
6601
+ try {
6602
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
6603
+ var track = _step4.value;
6604
+ if (isObject(track) && typeof track.path === "string") {
6605
+ files.push(track.path);
6606
+ }
6607
+ }
6608
+ } catch (err) {
6609
+ _iterator4.e(err);
6610
+ } finally {
6611
+ _iterator4.f();
6612
+ }
6613
+ }
6614
+ return files.filter(function (file) {
6615
+ return _this3.isRemoteUrl(file) !== remote;
6616
+ });
6617
+ }
5984
6618
  }, {
5985
6619
  key: "validateMediaMimeType",
5986
6620
  value: function validateMediaMimeType(mimeType, accept) {
@@ -6344,6 +6978,9 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
6344
6978
  if (this.mediaOptions.encode !== undefined) {
6345
6979
  result.encode = this.mediaOptions.encode;
6346
6980
  }
6981
+ if (this.mediaOptions.stream !== undefined) {
6982
+ result.stream = this.mediaOptions.stream;
6983
+ }
6347
6984
  if (this.mediaOptions.altSchema) {
6348
6985
  result.alt = this.mediaOptions.altSchema["executeSerialize"]();
6349
6986
  }
@@ -10347,49 +10984,548 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10347
10984
  value: src
10348
10985
  }]));
10349
10986
  }
10350
-
10351
- // Guarded on `mimeType` being set, as `ImageSchema` guards the same check.
10352
- // Without it, an absent mimeType reported "Mime type and file extension not
10353
- // matching. Mime type is 'undefined'" -- with no fix attached, so no quick
10354
- // fix was offered -- and the `file:add-metadata` branch below could never be
10355
- // reached for any file whose extension is recognized, which is all of them
10356
- // that get this far.
10357
- if (mimeType && fileMimeType !== mimeType) {
10358
- return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
10359
- message: "Mime type and file extension not matching. Mime type is '".concat(mimeType, "' but file extension is '").concat(fileMimeType, "'"),
10360
- value: src
10361
- }]));
10987
+
10988
+ // Guarded on `mimeType` being set, as `ImageSchema` guards the same check.
10989
+ // Without it, an absent mimeType reported "Mime type and file extension not
10990
+ // matching. Mime type is 'undefined'" -- with no fix attached, so no quick
10991
+ // fix was offered -- and the `file:add-metadata` branch below could never be
10992
+ // reached for any file whose extension is recognized, which is all of them
10993
+ // that get this far.
10994
+ // A legacy font name (`application/x-font-ttf`) is what the extension
10995
+ // table itself used to say, so it is not a mismatch.
10996
+ if (mimeType && fileMimeType !== canonicalFontMimeType(mimeType)) {
10997
+ return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
10998
+ message: "Mime type and file extension not matching. Mime type is '".concat(mimeType, "' but file extension is '").concat(fileMimeType, "'"),
10999
+ value: src
11000
+ }]));
11001
+ }
11002
+
11003
+ // The mime type read so far came from the filename. Whether it matches the
11004
+ // bytes can only be answered by reading the file, which this package
11005
+ // deliberately cannot do — so it is always handed on as a fix.
11006
+ if (mimeType !== undefined) {
11007
+ return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
11008
+ message: "File mimeType has not been checked against the file.",
11009
+ value: src,
11010
+ fixes: ["file:check-metadata"]
11011
+ }]));
11012
+ }
11013
+ return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
11014
+ message: "File metadata is missing: mimeType.",
11015
+ value: src,
11016
+ fixes: ["file:add-metadata"]
11017
+ }]));
11018
+ }
11019
+
11020
+ /**
11021
+ * The entries of the gallery this field points at, or null when it is a
11022
+ * standalone field.
11023
+ */
11024
+ }, {
11025
+ key: "galleryEntries",
11026
+ value: function galleryEntries() {
11027
+ var modulePaths = Object.keys(this.moduleMetadata);
11028
+ if (modulePaths.length === 0) {
11029
+ return null;
11030
+ }
11031
+ return this.moduleMetadata[modulePaths[0]];
11032
+ }
11033
+ }, {
11034
+ key: "executeAssert",
11035
+ value: function executeAssert(path, src) {
11036
+ if (this.opt && src === null) {
11037
+ return {
11038
+ success: true,
11039
+ data: src
11040
+ };
11041
+ }
11042
+ if (src === null) {
11043
+ return {
11044
+ success: false,
11045
+ errors: _defineProperty({}, path, [{
11046
+ message: "Expected 'object', got 'null'",
11047
+ typeError: true
11048
+ }])
11049
+ };
11050
+ }
11051
+ if (_typeof(src) !== "object") {
11052
+ return {
11053
+ success: false,
11054
+ errors: _defineProperty({}, path, [{
11055
+ message: "Expected object, got '".concat(_typeof(src), "'"),
11056
+ typeError: true
11057
+ }])
11058
+ };
11059
+ }
11060
+ if (!("path" in src) || typeof src.path !== "string") {
11061
+ return {
11062
+ success: false,
11063
+ errors: _defineProperty({}, path, [{
11064
+ message: "A file must be an object with a 'path' (error type: missing_path)",
11065
+ typeError: true
11066
+ }])
11067
+ };
11068
+ }
11069
+ return {
11070
+ success: true,
11071
+ data: src
11072
+ };
11073
+ }
11074
+ }, {
11075
+ key: "nullable",
11076
+ value: function nullable() {
11077
+ return new FileSchema(this.options, true, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, this.description, this.previewInput);
11078
+ }
11079
+ }, {
11080
+ key: "readonly",
11081
+ value: function readonly() {
11082
+ var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
11083
+ return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, isReadonly, this.isHidden, this.description, this.previewInput);
11084
+ }
11085
+ }, {
11086
+ key: "hidden",
11087
+ value: function hidden() {
11088
+ var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
11089
+ return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, isHidden, this.description, this.previewInput);
11090
+ }
11091
+ }, {
11092
+ key: "executeCustomValidateAt",
11093
+ value: function executeCustomValidateAt(path, src) {
11094
+ return this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
11095
+ path: path
11096
+ });
11097
+ }
11098
+
11099
+ /**
11100
+ * How this VALUE is shown where a preview of it is needed — a row in a
11101
+ * sortable list, a reference dropdown, a search hit. Never how the field
11102
+ * itself is edited (that is `render`). See `preview.ts`.
11103
+ *
11104
+ * @example
11105
+ * const schema = s.array(
11106
+ * s.file().preview(({ val }) => ({ title: val.path })),
11107
+ * );
11108
+ * export default c.define("/example.val.ts", schema, [
11109
+ * { path: "/public/val/example.pdf", mimeType: "application/pdf" },
11110
+ * ]);
11111
+ */
11112
+ }, {
11113
+ key: "preview",
11114
+ value: function preview(select) {
11115
+ return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, this.description, select);
11116
+ }
11117
+ }, {
11118
+ key: "executePreviewItem",
11119
+ value: function executePreviewItem(src) {
11120
+ if (this.previewInput === null) {
11121
+ return null;
11122
+ }
11123
+ return this.previewInput({
11124
+ val: src
11125
+ });
11126
+ }
11127
+ }, {
11128
+ key: "declaresItemPreview",
11129
+ value: function declaresItemPreview() {
11130
+ return this.previewInput !== null;
11131
+ }
11132
+ }, {
11133
+ key: "executeSerialize",
11134
+ value: function executeSerialize() {
11135
+ var _this$customValidateF;
11136
+ var modulePaths = this.moduleMetadata ? Object.keys(this.moduleMetadata) : [];
11137
+ return {
11138
+ type: "file",
11139
+ preview: this.previewInput ? true : undefined,
11140
+ options: this.options,
11141
+ opt: this.opt,
11142
+ remote: this.isRemote,
11143
+ customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
11144
+ referencedModule: modulePaths.length > 0 ? modulePaths[0] : undefined,
11145
+ readonly: this.isReadonly,
11146
+ hidden: this.isHidden,
11147
+ description: this.description
11148
+ };
11149
+ }
11150
+ }]);
11151
+ }(Schema);
11152
+
11153
+ /**
11154
+ * A file picked from a gallery. Its mime type lives in the gallery, so the
11155
+ * field carries only the path.
11156
+ */
11157
+
11158
+ /** A file of its own, carrying its own mime type. */
11159
+
11160
+ function file(options) {
11161
+ return fileOf(false, options);
11162
+ }
11163
+
11164
+ /**
11165
+ * `s.file()` for a project whose media is all remote (`files.remote` in
11166
+ * `val.config.ts`): the same schema, with `.remote()` already applied. See
11167
+ * `initSchema`.
11168
+ */
11169
+
11170
+ function remoteFile(options) {
11171
+ return fileOf(true, options);
11172
+ }
11173
+ function fileOf(remote, options) {
11174
+ var isModule = !!options && !!Internal.getValPath(options);
11175
+ if (isModule) {
11176
+ var allModules = {};
11177
+ for (var _i = 0, _arr = [options]; _i < _arr.length; _i++) {
11178
+ var valModule = _arr[_i];
11179
+ var modulePath = getValPath(valModule);
11180
+ if (modulePath === undefined) {
11181
+ throw new Error("Invalid argument passed to s.file(). Expected a ValModule constructed through c.define, but got an object without a valid module path.");
11182
+ }
11183
+ allModules[modulePath] = getSource(valModule);
11184
+ }
11185
+ return new FileSchema({}, false, remote, [], allModules);
11186
+ }
11187
+ return new FileSchema(options, false, remote);
11188
+ }
11189
+
11190
+ /**
11191
+ * A font picked from an `s.fontset()`.
11192
+ *
11193
+ * The same field `s.file(fontsVal)` is — it stores `{ path }` and serializes
11194
+ * identically — with one thing more: it refuses a set that is not a font set,
11195
+ * at definition, so a field called a font cannot end up offering PDFs. What the
11196
+ * Studio does with it (a specimen in its parent, the set to pick from when
11197
+ * opened) it does for any file field backed by a font set.
11198
+ */
11199
+ function font(fontsetModule) {
11200
+ assertFontset(fontsetModule);
11201
+ return file(fontsetModule);
11202
+ }
11203
+
11204
+ /** `s.font()` with `.remote()` applied: see `files.remote` in `initSchema`. */
11205
+ function remoteFont(fontsetModule) {
11206
+ assertFontset(fontsetModule);
11207
+ return remoteFile(fontsetModule);
11208
+ }
11209
+ function assertFontset(fontsetModule) {
11210
+ var _getSchema;
11211
+ var modulePath = getValPath(fontsetModule);
11212
+ var serialized = (_getSchema = getSchema(fontsetModule)) === null || _getSchema === void 0 ? void 0 : _getSchema["executeSerialize"]();
11213
+ var accept = (serialized === null || serialized === void 0 ? void 0 : serialized.type) === "record" && serialized.mediaType === "files" ? serialized.accept : undefined;
11214
+ var isFontset = !!accept && accept.split(",").every(function (type) {
11215
+ return isFontMimeType(type.trim());
11216
+ });
11217
+ if (!isFontset) {
11218
+ throw new Error("s.font(".concat(modulePath !== null && modulePath !== void 0 ? modulePath : "?", "): the module must be an s.fontset(). Use s.file() to pick from a set of other files."));
11219
+ }
11220
+ }
11221
+
11222
+ /**
11223
+ * Turn an upload into an HLS stream, in the browser, before it is uploaded.
11224
+ *
11225
+ * The Studio transcodes the picked file with WebCodecs into one rendition per
11226
+ * entry of `renditions` (never upscaled: a 720p upload gets no 1080p
11227
+ * rendition), segments each into CMAF and writes the playlists. What is stored
11228
+ * is a directory beside the other uploads; the field's `path` is its master
11229
+ * playlist. See `architecture/media.md`.
11230
+ *
11231
+ * `type` is required for the same reason it is on `ImageEncodeOptions`: a
11232
+ * second kind of stream would then be additive.
11233
+ */
11234
+
11235
+ /** `false` (or absent) uploads the file as it was picked. */
11236
+
11237
+ /**
11238
+ * What a SET-BACKED field (`s.video(videosVal)`) may say for itself.
11239
+ *
11240
+ * Not `VideoOptions`: `dir` and `accept` belong to the set. `stream` is the
11241
+ * exception, as `encode` is for an image: it is about what happens to an
11242
+ * upload on its way in, so a field that wants the file as it was picked,
11243
+ * where its set streams, has to be able to say `stream: false`.
11244
+ */
11245
+
11246
+ /**
11247
+ * The set a field picks from: its entries as they were when the module was
11248
+ * evaluated (the published set — see `GalleryImageSource` for why that is
11249
+ * enough), and whether it stores its videos on Val Remote.
11250
+ */
11251
+
11252
+ var DEFAULT_VIDEO_ACCEPT = "video/*";
11253
+ var DEFAULT_VIDEO_RENDITIONS = [1080, 720, 480];
11254
+ var DEFAULT_VIDEO_SEGMENT_DURATION = 6;
11255
+ var VideoSchema = /*#__PURE__*/function (_Schema) {
11256
+ function VideoSchema(options) {
11257
+ var _this;
11258
+ var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
11259
+ var isRemote = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
11260
+ var customValidateFunctions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
11261
+ var isReadonly = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
11262
+ var isHidden = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
11263
+ var description = arguments.length > 6 ? arguments[6] : undefined;
11264
+ var renderInput = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
11265
+ var previewInput = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
11266
+ var gallery = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : null;
11267
+ _classCallCheck(this, VideoSchema);
11268
+ _this = _callSuper(this, VideoSchema);
11269
+ _this.options = options;
11270
+ _this.opt = opt;
11271
+ _this.isRemote = isRemote;
11272
+ _this.customValidateFunctions = customValidateFunctions;
11273
+ _this.isReadonly = isReadonly;
11274
+ _this.isHidden = isHidden;
11275
+ _this.description = description;
11276
+ _this.renderInput = renderInput;
11277
+ _this.previewInput = previewInput;
11278
+ _this.gallery = gallery;
11279
+ return _this;
11280
+ }
11281
+
11282
+ /**
11283
+ * Describe this field.
11284
+ *
11285
+ * The description is INPUT HELP: it is shown where this field's value is
11286
+ * entered — beside its input in the Val editor, and for a record's key
11287
+ * schema in every form that asks for a key — so it is where you say what an
11288
+ * editor needs to know to fill it in RIGHT, which the field name cannot
11289
+ * carry. It is not a name for the value: that is `.preview(...)`, and it is
11290
+ * read somewhere else. The description also travels in the serialized
11291
+ * schema, which is what the AI assistant and the MCP tools read.
11292
+ *
11293
+ * Pass `null` to clear a description set earlier.
11294
+ *
11295
+ * @example
11296
+ * const schema = s.video().describe("Plays muted behind the headline");
11297
+ * export default c.define("/example.val.ts", schema, {
11298
+ * path: "/public/val/example.mp4",
11299
+ * mimeType: "video/mp4",
11300
+ * });
11301
+ */
11302
+ _inherits(VideoSchema, _Schema);
11303
+ return _createClass(VideoSchema, [{
11304
+ key: "describe",
11305
+ value: function describe(description) {
11306
+ return new VideoSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput, this.previewInput, this.gallery);
11307
+ }
11308
+
11309
+ /**
11310
+ * Store the video on Val's remote content host instead of in your
11311
+ * repository.
11312
+ *
11313
+ * The bytes still go into the patch store when the video is uploaded — the
11314
+ * push to the remote host happens at publish. What changes is where the
11315
+ * published video lives: `path` (and the poster's, and each caption track's)
11316
+ * becomes a remote URL rather than a path under `/public`, so the repository
11317
+ * does not grow with every upload. Upload remote videos in the Studio.
11318
+ *
11319
+ * @example
11320
+ * const schema = s.video().remote();
11321
+ * export default c.define("/example.val.ts", schema, {
11322
+ * path: "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/example.mp4",
11323
+ * mimeType: "video/mp4",
11324
+ * });
11325
+ */
11326
+ }, {
11327
+ key: "remote",
11328
+ value: function remote() {
11329
+ return new VideoSchema(this.options, this.opt, true, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput, this.gallery);
11330
+ }
11331
+
11332
+ /**
11333
+ * Add a custom validation rule to this field.
11334
+ *
11335
+ * The function is called with the field's value and returns `false` when the
11336
+ * value is fine, or a STRING with the message to show when it is not. Call it
11337
+ * more than once to add more rules — they all run, and every message is
11338
+ * reported.
11339
+ *
11340
+ * Write the check as a ternary, not as `ok || "message"`: that returns `true`
11341
+ * when the value is fine, and `true` is not one of the two answers.
11342
+ *
11343
+ * Validation runs in the Studio as you type, in `npx val validate` and
11344
+ * before a publish.
11345
+ *
11346
+ * The second argument carries the `path` of the field being validated, for
11347
+ * when the message needs to say where the problem is.
11348
+ *
11349
+ * @example
11350
+ * const schema = s.video().validate((val) =>
11351
+ * (val.duration ?? 0) <= 30 ? false : "Keep it under 30 seconds",
11352
+ * );
11353
+ * export default c.define("/example.val.ts", schema, {
11354
+ * path: "/public/val/example.mp4",
11355
+ * mimeType: "video/mp4",
11356
+ * duration: 12,
11357
+ * });
11358
+ */
11359
+ }, {
11360
+ key: "validate",
11361
+ value: function validate(validationFunction) {
11362
+ return new VideoSchema(this.options, this.opt, this.isRemote, [].concat(_toConsumableArray(this.customValidateFunctions), [validationFunction]), this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput, this.gallery);
11363
+ }
11364
+ }, {
11365
+ key: "executeValidate",
11366
+ value: function executeValidate(path, src) {
11367
+ var customValidationErrors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
11368
+ path: path
11369
+ });
11370
+ var report = function report(errors) {
11371
+ var all = [].concat(_toConsumableArray(customValidationErrors), _toConsumableArray(errors));
11372
+ return all.length > 0 ? _defineProperty({}, path, all) : false;
11373
+ };
11374
+ if (src === null || src === undefined) {
11375
+ if (this.opt) {
11376
+ return report([]);
11377
+ }
11378
+ return report([{
11379
+ message: "Non-optional video was null or undefined.",
11380
+ value: src
11381
+ }]);
11382
+ }
11383
+ if (_typeof(src) !== "object" || typeof src.path !== "string") {
11384
+ return report([{
11385
+ message: "A video must be an object with a 'path' string.",
11386
+ value: src
11387
+ }]);
11388
+ }
11389
+ if (this.gallery) {
11390
+ return report(this.validateFromGallery(src, this.gallery));
11391
+ }
11392
+ return report(this.validateOwn(src));
11393
+ }
11394
+
11395
+ /**
11396
+ * A set-backed field: the file and what was read from it are the set's,
11397
+ * so the field is checked for what is its own, and for pointing at an
11398
+ * entry the set has.
11399
+ */
11400
+ }, {
11401
+ key: "validateFromGallery",
11402
+ value: function validateFromGallery(src, gallery) {
11403
+ var own = ownMetadataOf(src);
11404
+ var repeated = ["mimeType", "width", "height", "duration"].filter(function (key) {
11405
+ return own[key] !== undefined;
11406
+ });
11407
+ if (repeated.length > 0) {
11408
+ return [{
11409
+ message: "A video from a set must not carry its own ".concat(repeated.join(", "), ": ").concat(repeated.length === 1 ? "it is" : "they are", " stored in the set."),
11410
+ value: src
11411
+ }];
11412
+ }
11413
+ var entry = gallery.entries === null ? null : gallery.entries[src.path];
11414
+ if (entry === undefined) {
11415
+ return [{
11416
+ message: "The set does not have a video at '".concat(src.path, "'."),
11417
+ value: src
11418
+ }];
11419
+ }
11420
+ // The poster and the captions are the field's own files, and live where
11421
+ // the set keeps its videos. The video itself is the set's to move.
11422
+ var misplaced = filesOfVideoSource(src).slice(1).filter(function (file) {
11423
+ return isRemoteMediaPath(file) !== gallery.remote;
11424
+ });
11425
+ if (misplaced.length > 0) {
11426
+ return [misplacedError(src, misplaced, gallery.remote)];
11427
+ }
11428
+ var duration = entry !== null && typeof entry.duration === "number" && entry.duration > 0 ? entry.duration : undefined;
11429
+ return [].concat(_toConsumableArray(validateTimes(src, duration)), _toConsumableArray(validateMixedTimes(src, entry)), _toConsumableArray(validateHotspot(src)), _toConsumableArray(validatePoster(src)), _toConsumableArray(validateCaptions(src)));
11430
+ }
11431
+
11432
+ /** A video of its own: it carries the file, and what was read from it. */
11433
+ }, {
11434
+ key: "validateOwn",
11435
+ value: function validateOwn(src) {
11436
+ var _this2 = this;
11437
+ var own = ownMetadataOf(src);
11438
+ // Every file the video names has to be where the schema says, and one fix
11439
+ // moves them all — a video on the content host with its poster still in
11440
+ // the repository is half a migration, not a choice. The error is reported
11441
+ // once for the whole video, never per file.
11442
+ var misplaced = filesOfVideoSource(src).filter(function (file) {
11443
+ return isRemoteMediaPath(file) !== _this2.isRemote;
11444
+ });
11445
+ if (misplaced.length > 0) {
11446
+ return [misplacedError(src, misplaced, this.isRemote)];
11447
+ }
11448
+ var errors = [];
11449
+ var isHls = isHlsVideo({
11450
+ path: src.path,
11451
+ mimeType: stringOrUndefined(own.mimeType)
11452
+ });
11453
+ var extensionMimeType = filenameToMimeType(stripQuery(src.path));
11454
+ if (isHls) {
11455
+ if (!stripQuery(src.path).toLowerCase().endsWith(".m3u8")) {
11456
+ errors.push({
11457
+ message: "An HLS video's path must be its master playlist (.m3u8). Got: ".concat(src.path),
11458
+ value: src
11459
+ });
11460
+ }
11461
+ } else if (!extensionMimeType || !extensionMimeType.startsWith("video/")) {
11462
+ errors.push({
11463
+ message: "Could not tell this is a video from its file extension. Got: ".concat(src.path),
11464
+ value: src
11465
+ });
11466
+ } else if (own.mimeType !== undefined && extensionMimeType !== own.mimeType) {
11467
+ errors.push({
11468
+ message: "Mime type and file extension not matching. Mime type is '".concat(String(own.mimeType), "' but file extension is '").concat(extensionMimeType, "'"),
11469
+ value: src
11470
+ });
11471
+ }
11472
+ if (own.mimeType !== undefined && typeof own.mimeType !== "string") {
11473
+ errors.push({
11474
+ message: "'mimeType' must be a string.",
11475
+ value: src
11476
+ });
11477
+ } else if (own.mimeType !== undefined && !isHls) {
11478
+ var _this$options$accept, _this$options;
11479
+ var accept = (_this$options$accept = (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.accept) !== null && _this$options$accept !== void 0 ? _this$options$accept : DEFAULT_VIDEO_ACCEPT;
11480
+ if (!mimeTypeMatchesAccept(own.mimeType, accept)) {
11481
+ errors.push({
11482
+ message: "Mime type mismatch. Found '".concat(own.mimeType, "' but schema accepts '").concat(accept, "'"),
11483
+ value: src
11484
+ });
11485
+ }
11486
+ }
11487
+ var _iterator = _createForOfIteratorHelper(["width", "height", "duration"]),
11488
+ _step;
11489
+ try {
11490
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
11491
+ var key = _step.value;
11492
+ var value = own[key];
11493
+ if (value !== undefined && (typeof value !== "number" || !Number.isFinite(value) || value <= 0)) {
11494
+ errors.push({
11495
+ message: "'".concat(key, "' must be a positive number. Got: ").concat(JSON.stringify(value)),
11496
+ value: src
11497
+ });
11498
+ }
11499
+ }
11500
+ } catch (err) {
11501
+ _iterator.e(err);
11502
+ } finally {
11503
+ _iterator.f();
11504
+ }
11505
+ var duration = typeof own.duration === "number" && own.duration > 0 ? own.duration : undefined;
11506
+ errors.push.apply(errors, _toConsumableArray(validateTimes(src, duration)));
11507
+ errors.push.apply(errors, _toConsumableArray(validateHotspot(src)));
11508
+ errors.push.apply(errors, _toConsumableArray(validatePoster(src)));
11509
+ errors.push.apply(errors, _toConsumableArray(validateCaptions(src)));
11510
+ if (errors.length > 0) {
11511
+ return errors;
10362
11512
  }
10363
11513
 
10364
- // The mime type read so far came from the filename. Whether it matches the
10365
- // bytes can only be answered by reading the file, which this package
10366
- // deliberately cannot do — so it is always handed on as a fix.
10367
- if (mimeType !== undefined) {
10368
- return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
10369
- message: "File mimeType has not been checked against the file.",
11514
+ // Everything authored is fine. What is left is what is read from the
11515
+ // bytes, which this package cannot do — so it is handed on as a fix. The
11516
+ // CLI reads a local file's headers from disk and a remote one's over
11517
+ // HTTP, with Range requests, so neither is downloaded whole.
11518
+ var missing = ["mimeType", "width", "height", "duration"].filter(function (key) {
11519
+ return own[key] === undefined;
11520
+ });
11521
+ if (missing.length > 0) {
11522
+ return [{
11523
+ message: "Video metadata is missing: ".concat(missing.join(", "), "."),
10370
11524
  value: src,
10371
- fixes: ["file:check-metadata"]
10372
- }]));
10373
- }
10374
- return _defineProperty({}, path, [].concat(_toConsumableArray(customValidationErrors), [{
10375
- message: "File metadata is missing: mimeType.",
10376
- value: src,
10377
- fixes: ["file:add-metadata"]
10378
- }]));
10379
- }
10380
-
10381
- /**
10382
- * The entries of the gallery this field points at, or null when it is a
10383
- * standalone field.
10384
- */
10385
- }, {
10386
- key: "galleryEntries",
10387
- value: function galleryEntries() {
10388
- var modulePaths = Object.keys(this.moduleMetadata);
10389
- if (modulePaths.length === 0) {
10390
- return null;
11525
+ fixes: ["video:add-metadata"]
11526
+ }];
10391
11527
  }
10392
- return this.moduleMetadata[modulePaths[0]];
11528
+ return [];
10393
11529
  }
10394
11530
  }, {
10395
11531
  key: "executeAssert",
@@ -10422,7 +11558,7 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10422
11558
  return {
10423
11559
  success: false,
10424
11560
  errors: _defineProperty({}, path, [{
10425
- message: "A file must be an object with a 'path' (error type: missing_path)",
11561
+ message: "A video must be an object with a 'path' (error type: missing_path)",
10426
11562
  typeError: true
10427
11563
  }])
10428
11564
  };
@@ -10435,19 +11571,19 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10435
11571
  }, {
10436
11572
  key: "nullable",
10437
11573
  value: function nullable() {
10438
- return new FileSchema(this.options, true, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, this.description, this.previewInput);
11574
+ return new VideoSchema(this.options, true, this.isRemote, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput, this.gallery);
10439
11575
  }
10440
11576
  }, {
10441
11577
  key: "readonly",
10442
11578
  value: function readonly() {
10443
11579
  var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
10444
- return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, isReadonly, this.isHidden, this.description, this.previewInput);
11580
+ return new VideoSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, isReadonly, this.isHidden, this.description, this.renderInput, this.previewInput, this.gallery);
10445
11581
  }
10446
11582
  }, {
10447
11583
  key: "hidden",
10448
11584
  value: function hidden() {
10449
11585
  var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
10450
- return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, isHidden, this.description, this.previewInput);
11586
+ return new VideoSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.isReadonly, isHidden, this.description, this.renderInput, this.previewInput, this.gallery);
10451
11587
  }
10452
11588
  }, {
10453
11589
  key: "executeCustomValidateAt",
@@ -10457,6 +11593,25 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10457
11593
  });
10458
11594
  }
10459
11595
 
11596
+ /**
11597
+ * How this field is laid out in the editor when it is the item of an array
11598
+ * or record: `{ as: "inline" }` renders the field itself inside each row,
11599
+ * instead of a preview row that navigates to it.
11600
+ *
11601
+ * Static configuration, not a callback — see `render.ts`.
11602
+ *
11603
+ * @example
11604
+ * const schema = s.array(s.video().render({ as: "inline" }));
11605
+ * export default c.define("/example.val.ts", schema, [
11606
+ * { path: "/public/val/example.mp4", mimeType: "video/mp4" },
11607
+ * ]);
11608
+ */
11609
+ }, {
11610
+ key: "render",
11611
+ value: function render(input) {
11612
+ return new VideoSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, input, this.previewInput, this.gallery);
11613
+ }
11614
+
10460
11615
  /**
10461
11616
  * How this VALUE is shown where a preview of it is needed — a row in a
10462
11617
  * sortable list, a reference dropdown, a search hit. Never how the field
@@ -10464,16 +11619,19 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10464
11619
  *
10465
11620
  * @example
10466
11621
  * const schema = s.array(
10467
- * s.file().preview(({ val }) => ({ title: val.path })),
11622
+ * s.video().preview(({ val }) => ({
11623
+ * title: val.alt ?? val.path,
11624
+ * image: val.poster,
11625
+ * })),
10468
11626
  * );
10469
11627
  * export default c.define("/example.val.ts", schema, [
10470
- * { path: "/public/val/example.pdf", mimeType: "application/pdf" },
11628
+ * { path: "/public/val/example.mp4", mimeType: "video/mp4" },
10471
11629
  * ]);
10472
11630
  */
10473
11631
  }, {
10474
11632
  key: "preview",
10475
11633
  value: function preview(select) {
10476
- return new FileSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.moduleMetadata, this.isReadonly, this.isHidden, this.description, select);
11634
+ return new VideoSchema(this.options, this.opt, this.isRemote, this.customValidateFunctions, this.isReadonly, this.isHidden, this.description, this.renderInput, select, this.gallery);
10477
11635
  }
10478
11636
  }, {
10479
11637
  key: "executePreviewItem",
@@ -10493,16 +11651,16 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10493
11651
  }, {
10494
11652
  key: "executeSerialize",
10495
11653
  value: function executeSerialize() {
10496
- var _this$customValidateF;
10497
- var modulePaths = this.moduleMetadata ? Object.keys(this.moduleMetadata) : [];
11654
+ var _this$renderInput, _this$customValidateF, _this$gallery;
10498
11655
  return {
10499
- type: "file",
11656
+ type: "video",
11657
+ render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
10500
11658
  preview: this.previewInput ? true : undefined,
10501
11659
  options: this.options,
10502
11660
  opt: this.opt,
10503
11661
  remote: this.isRemote,
10504
11662
  customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
10505
- referencedModule: modulePaths.length > 0 ? modulePaths[0] : undefined,
11663
+ referencedModule: (_this$gallery = this.gallery) === null || _this$gallery === void 0 ? void 0 : _this$gallery.modulePath,
10506
11664
  readonly: this.isReadonly,
10507
11665
  hidden: this.isHidden,
10508
11666
  description: this.description
@@ -10512,27 +11670,137 @@ var FileSchema = /*#__PURE__*/function (_Schema) {
10512
11670
  }(Schema);
10513
11671
 
10514
11672
  /**
10515
- * A file picked from a gallery. Its mime type lives in the gallery, so the
10516
- * field carries only the path.
11673
+ * Every file a video value names: the video, its poster and each caption
11674
+ * track. An HLS stream's segments are not here — they are named by its
11675
+ * playlists, not by the value.
10517
11676
  */
10518
-
10519
- /** A file of its own, carrying its own mime type. */
10520
-
10521
- function file(options) {
10522
- var isModule = !!options && !!Internal.getValPath(options);
10523
- if (isModule) {
10524
- var allModules = {};
10525
- for (var _i = 0, _arr = [options]; _i < _arr.length; _i++) {
10526
- var valModule = _arr[_i];
10527
- var modulePath = getValPath(valModule);
10528
- if (modulePath === undefined) {
10529
- throw new Error("Invalid argument passed to s.file(). Expected a ValModule constructed through c.define, but got an object without a valid module path.");
11677
+ function filesOfVideoSource(src) {
11678
+ var files = [src.path];
11679
+ if (src.poster && _typeof(src.poster) === "object" && typeof src.poster.path === "string") {
11680
+ files.push(src.poster.path);
11681
+ }
11682
+ if (Array.isArray(src.captions)) {
11683
+ var _iterator2 = _createForOfIteratorHelper(src.captions),
11684
+ _step2;
11685
+ try {
11686
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
11687
+ var track = _step2.value;
11688
+ if (track && _typeof(track) === "object" && typeof track.path === "string") {
11689
+ files.push(track.path);
11690
+ }
10530
11691
  }
10531
- allModules[modulePath] = getSource(valModule);
11692
+ } catch (err) {
11693
+ _iterator2.e(err);
11694
+ } finally {
11695
+ _iterator2.f();
10532
11696
  }
10533
- return new FileSchema({}, false, false, [], allModules);
10534
11697
  }
10535
- return new FileSchema(options);
11698
+ return files;
11699
+ }
11700
+
11701
+ /**
11702
+ * What the value says about its file, typed as what it IS rather than what
11703
+ * it should be: a set-backed field's type has none of these, and is checked
11704
+ * for carrying them anyway, because hand-written JSON never saw the type.
11705
+ */
11706
+ function ownMetadataOf(src) {
11707
+ var value = _objectSpread2({}, src);
11708
+ return {
11709
+ mimeType: value.mimeType,
11710
+ width: value.width,
11711
+ height: value.height,
11712
+ duration: value.duration
11713
+ };
11714
+ }
11715
+
11716
+ /**
11717
+ * A start the field set against an end the set holds, or the reverse: each is
11718
+ * fine alone, and the field's own times and the set's are each checked where
11719
+ * they are written, so what is left is the pair the page will actually play.
11720
+ */
11721
+ function validateMixedTimes(src, entry) {
11722
+ var _src$startTime, _src$endTime;
11723
+ if (entry === null || src.startTime === undefined === (src.endTime === undefined)) {
11724
+ return [];
11725
+ }
11726
+ var start = (_src$startTime = src.startTime) !== null && _src$startTime !== void 0 ? _src$startTime : entry.startTime;
11727
+ var end = (_src$endTime = src.endTime) !== null && _src$endTime !== void 0 ? _src$endTime : entry.endTime;
11728
+ if (typeof start !== "number" || typeof end !== "number" || start < end) {
11729
+ return [];
11730
+ }
11731
+ var whose = src.startTime === undefined ? "the start is the set's" : "the end is the set's";
11732
+ return [{
11733
+ message: "'startTime' (".concat(start, "s) must be before 'endTime' (").concat(end, "s) \u2014 ").concat(whose, "."),
11734
+ value: src
11735
+ }];
11736
+ }
11737
+ function stringOrUndefined(value) {
11738
+ return typeof value === "string" ? value : undefined;
11739
+ }
11740
+ function misplacedError(src, misplaced, shouldBeRemote) {
11741
+ var these = misplaced.length === 1 ? "this file is" : "".concat(misplaced.length, " files are");
11742
+ return shouldBeRemote ? {
11743
+ message: "Expected a remote video, but ".concat(these, " stored locally: ").concat(misplaced.join(", ")),
11744
+ value: src,
11745
+ fixes: ["video:upload-remote"]
11746
+ } : {
11747
+ message: "Expected a local video (files under /public), but ".concat(these, " remote: ").concat(misplaced.join(", ")),
11748
+ value: src,
11749
+ fixes: ["video:download-remote"]
11750
+ };
11751
+ }
11752
+ function stripQuery(path) {
11753
+ return path.split("?")[0];
11754
+ }
11755
+
11756
+ /**
11757
+ * A video picked from a set (`s.videoset()`). What is true of the file —
11758
+ * its type, size and length — lives in the set; the field carries what one
11759
+ * page chose about it.
11760
+ */
11761
+
11762
+ /**
11763
+ * Define a video: an `.mp4` / `.webm` file, or an HLS stream the Studio makes
11764
+ * from an upload when `stream` is set.
11765
+ */
11766
+
11767
+ function video(options, galleryOptions) {
11768
+ return videoOf(options, galleryOptions);
11769
+ }
11770
+
11771
+ /**
11772
+ * `s.video()` for a project whose media is all remote (`files.remote` in
11773
+ * `val.config.ts`): the same schema, with `.remote()` already applied. See
11774
+ * `initSchema`.
11775
+ */
11776
+
11777
+ function remoteVideo(options, galleryOptions) {
11778
+ return videoOf(options, galleryOptions).remote();
11779
+ }
11780
+ function videoOf(options, galleryOptions) {
11781
+ var _options$GetSchema, _setSchema$remote, _setSchema$remote2;
11782
+ if (options === undefined || !isVideosetModule(options)) {
11783
+ return new VideoSchema(options);
11784
+ }
11785
+ var modulePath = getValPath(options);
11786
+ if (modulePath === undefined) {
11787
+ throw new Error("Invalid argument passed to s.video(). Expected a ValModule constructed through c.define, but got an object without a valid module path.");
11788
+ }
11789
+ var setSchema = (_options$GetSchema = options[GetSchema]) === null || _options$GetSchema === void 0 ? void 0 : _options$GetSchema["executeSerialize"]();
11790
+ if ((setSchema === null || setSchema === void 0 ? void 0 : setSchema.type) !== "record" || setSchema.mediaType !== "videos") {
11791
+ var _setSchema$mediaType, _setSchema$type;
11792
+ throw new Error("s.video(".concat(modulePath, "): the module must be an s.videoset(). Got ").concat((setSchema === null || setSchema === void 0 ? void 0 : setSchema.type) === "record" ? "a record of ".concat((_setSchema$mediaType = setSchema.mediaType) !== null && _setSchema$mediaType !== void 0 ? _setSchema$mediaType : "values") : (_setSchema$type = setSchema === null || setSchema === void 0 ? void 0 : setSchema.type) !== null && _setSchema$type !== void 0 ? _setSchema$type : "no schema", "."));
11793
+ }
11794
+ return new VideoSchema((galleryOptions === null || galleryOptions === void 0 ? void 0 : galleryOptions.stream) !== undefined ? {
11795
+ stream: galleryOptions.stream
11796
+ } : undefined, false, (_setSchema$remote = setSchema.remote) !== null && _setSchema$remote !== void 0 ? _setSchema$remote : false, [], false, false, undefined, null, null, {
11797
+ modulePath: modulePath,
11798
+ entries: getSource(options),
11799
+ remote: (_setSchema$remote2 = setSchema.remote) !== null && _setSchema$remote2 !== void 0 ? _setSchema$remote2 : false
11800
+ });
11801
+ }
11802
+ function isVideosetModule(value) {
11803
+ return GetSchema in value;
10536
11804
  }
10537
11805
 
10538
11806
  /**
@@ -10574,6 +11842,50 @@ var fileset = function fileset(options) {
10574
11842
  });
10575
11843
  };
10576
11844
 
11845
+ /**
11846
+ * Options for s.fontset()
11847
+ */
11848
+
11849
+ /**
11850
+ * Define a collection of fonts.
11851
+ *
11852
+ * A fontset IS a fileset — the same source, the same `s.file(fontsVal)` field
11853
+ * to pick from it, the same checks and fixes — with `accept` defaulting to the
11854
+ * web font formats. What a font gets that any other file does not is decided
11855
+ * by its mime type rather than by the set: the Studio previews any file it
11856
+ * knows is a font, wherever it is shown.
11857
+ *
11858
+ * Remote is off by default: call `.remote()` on the result to allow remote files.
11859
+ *
11860
+ * @example
11861
+ * ```typescript
11862
+ * const schema = s.fontset({ dir: "/public/val/fonts" });
11863
+ * export default c.define("/content/fonts.val.ts", schema, {
11864
+ * "/public/val/fonts/inter_a1b2c.woff2": {
11865
+ * mimeType: "font/woff2",
11866
+ * },
11867
+ * });
11868
+ * ```
11869
+ */
11870
+ var fontset = function fontset(options) {
11871
+ var _options$accept;
11872
+ var accept = (_options$accept = options.accept) !== null && _options$accept !== void 0 ? _options$accept : DEFAULT_FONT_ACCEPT;
11873
+ var notFonts = accept.split(",").map(function (type) {
11874
+ return type.trim();
11875
+ }).filter(function (type) {
11876
+ return !isFontMimeType(type);
11877
+ });
11878
+ if (notFonts.length > 0) {
11879
+ // A fontset that took a PDF would be a fileset under a name that says
11880
+ // otherwise, and the Studio would draw a specimen of nothing.
11881
+ throw new Error("s.fontset(): 'accept' may only name font types (font/woff2, font/woff, font/ttf, font/otf or font/*). Got: ".concat(notFonts.join(", "), ". Use s.fileset() for other files."));
11882
+ }
11883
+ return fileset({
11884
+ dir: options.dir,
11885
+ accept: accept
11886
+ });
11887
+ };
11888
+
10577
11889
  var DateSchema = /*#__PURE__*/function (_Schema) {
10578
11890
  function DateSchema(options) {
10579
11891
  var _this;
@@ -11514,6 +12826,99 @@ var imageset = function imageset(options) {
11514
12826
  });
11515
12827
  };
11516
12828
 
12829
+ /**
12830
+ * Options for s.videoset()
12831
+ */
12832
+
12833
+ /** See `ImagesetAltOption`: `alt` is optional only when it is the default. */
12834
+
12835
+ /**
12836
+ * One video of a set, keyed by its file path (an HLS stream by its master
12837
+ * playlist).
12838
+ *
12839
+ * Two kinds of thing are here. What is TRUE OF THE FILE — what it is, how
12840
+ * big, how long — is read from the bytes and is the set's alone. The rest —
12841
+ * the description, focal point, poster, start and end, and captions — is what
12842
+ * a page chooses about a video, and an entry holds it as the DEFAULT: every
12843
+ * field picked from the set (`s.video(videosVal)`) starts from it and may
12844
+ * override it key by key, so a video described and trimmed once is described
12845
+ * and trimmed everywhere it is used. `fillFromGallery` is the one merge.
12846
+ *
12847
+ * The poster is also the set's thumbnail of the entry: there is no separate
12848
+ * still for the gallery, because two stills of one video would disagree.
12849
+ */
12850
+
12851
+ /**
12852
+ * Define a collection of videos — the video twin of `s.imageset()`.
12853
+ *
12854
+ * A field picks from it with `s.video(videosVal)`. An entry may hold the
12855
+ * description, poster, start and end, focal point and captions as defaults,
12856
+ * and a field overrides any of them by setting its own. Uploading in such a
12857
+ * field adds to the set. Call `.remote()` on the result
12858
+ * to store the set on Val Remote.
12859
+ *
12860
+ * @example
12861
+ * ```typescript
12862
+ * const schema = s.videoset({
12863
+ * dir: "/public/val/videos",
12864
+ * stream: { type: "hls" },
12865
+ * });
12866
+ * export default c.define("/content/videos.val.ts", schema, {
12867
+ * "/public/val/videos/intro_51df2.mp4": {
12868
+ * mimeType: "video/mp4",
12869
+ * width: 1280,
12870
+ * height: 720,
12871
+ * duration: 12.5,
12872
+ * alt: "The team, introducing itself",
12873
+ * startTime: 1.5,
12874
+ * },
12875
+ * });
12876
+ * ```
12877
+ */
12878
+ var videoset = function videoset(options) {
12879
+ var _options$alt, _options$accept;
12880
+ // The same assertion as in `imageset()`, and sound for the same reason:
12881
+ // `VideosetAltOption` makes `alt` required unless `Alt` is the default.
12882
+ var altSchema = (_options$alt = options.alt) !== null && _options$alt !== void 0 ? _options$alt : string().nullable();
12883
+ var itemSchema = new ObjectSchema({
12884
+ mimeType: new StringSchema({}, false),
12885
+ width: new NumberSchema(undefined, false),
12886
+ height: new NumberSchema(undefined, false),
12887
+ duration: new NumberSchema(undefined, false),
12888
+ alt: altSchema
12889
+ }, false);
12890
+ return new RecordSchema(itemSchema, false, [], null, null, {
12891
+ type: "videos",
12892
+ accept: (_options$accept = options.accept) !== null && _options$accept !== void 0 ? _options$accept : DEFAULT_VIDEO_ACCEPT,
12893
+ dir: options.dir,
12894
+ remote: false,
12895
+ altSchema: altSchema,
12896
+ stream: options.stream
12897
+ });
12898
+ };
12899
+
12900
+ /**
12901
+ * The video schema a set's entry is hashed against, for a remote ref.
12902
+ *
12903
+ * The set's item schema is an object, but a remote ref's validation hash is
12904
+ * computed over a MEDIA schema, so one is synthesized from the set — the one
12905
+ * definition the CLI's upload (`videos:upload-remote`) and the Studio's (the
12906
+ * set's gallery) both use, because two copies are two hashes for the same
12907
+ * file. `stream` is left out: it says what is done to an upload on its way
12908
+ * in, not whether the bytes that arrived are valid.
12909
+ */
12910
+ function videosetEntryVideoSchema(set) {
12911
+ return {
12912
+ type: "video",
12913
+ opt: false,
12914
+ options: _objectSpread2(_objectSpread2({}, set.accept ? {
12915
+ accept: set.accept
12916
+ } : {}), set.dir ? {
12917
+ dir: set.dir
12918
+ } : {})
12919
+ };
12920
+ }
12921
+
11517
12922
  // import type { F } from "ts-toolbelt";
11518
12923
  // import { i18n, I18n } from "./schema/future/i18n";
11519
12924
  // import { oneOf } from "./schema/future/oneOf";
@@ -11521,7 +12926,37 @@ var imageset = function imageset(options) {
11521
12926
  // export type InitSchemaLocalized<Locales extends readonly string[]> = {
11522
12927
  // readonly i18n: I18n<Locales>;
11523
12928
  // };
11524
- function initSchema() {
12929
+
12930
+ function initSchema(options) {
12931
+ var media = (options === null || options === void 0 ? void 0 : options.remote) === true ? {
12932
+ image: remoteImage,
12933
+ file: remoteFile,
12934
+ font: remoteFont,
12935
+ video: remoteVideo,
12936
+ richtext: remoteRichtext,
12937
+ imageset: function (options) {
12938
+ return imageset(options).remote();
12939
+ },
12940
+ fileset: function (options) {
12941
+ return fileset(options).remote();
12942
+ },
12943
+ fontset: function (options) {
12944
+ return fontset(options).remote();
12945
+ },
12946
+ videoset: function (options) {
12947
+ return videoset(options).remote();
12948
+ }
12949
+ } : {
12950
+ image: image,
12951
+ file: file,
12952
+ font: font,
12953
+ video: video,
12954
+ richtext: richtext,
12955
+ imageset: imageset,
12956
+ fileset: fileset,
12957
+ fontset: fontset,
12958
+ videoset: videoset
12959
+ };
11525
12960
  // locales: F.Narrow<Locales>
11526
12961
  return {
11527
12962
  string: string,
@@ -11533,14 +12968,17 @@ function initSchema() {
11533
12968
  "enum": enumSchema,
11534
12969
  union: union,
11535
12970
  // oneOf,
11536
- richtext: richtext,
11537
- image: image,
12971
+ richtext: media.richtext,
12972
+ image: media.image,
11538
12973
  literal: literal,
11539
12974
  keyOf: keyOf,
11540
12975
  view: view,
11541
12976
  record: record,
11542
- file: file,
11543
- fileset: fileset,
12977
+ file: media.file,
12978
+ font: media.font,
12979
+ video: media.video,
12980
+ fileset: media.fileset,
12981
+ fontset: media.fontset,
11544
12982
  date: date,
11545
12983
  datetime: datetime,
11546
12984
  color: color,
@@ -11548,7 +12986,8 @@ function initSchema() {
11548
12986
  locale: locale,
11549
12987
  route: route,
11550
12988
  router: router,
11551
- imageset: imageset,
12989
+ imageset: media.imageset,
12990
+ videoset: media.videoset,
11552
12991
  settings: settings
11553
12992
  // i18n: i18n(locales),
11554
12993
  };
@@ -11566,8 +13005,11 @@ function initSchema() {
11566
13005
 
11567
13006
  // TODO: Rename to createValSystem (only to be used by internal things), we can then export * from '@valbuild/core' in the next package then.
11568
13007
  var initVal = function initVal(config) {
13008
+ var _config$files;
11569
13009
  // const locales = options?.locales;
11570
- var s = initSchema();
13010
+ var s = initSchema({
13011
+ remote: (config === null || config === void 0 || (_config$files = config.files) === null || _config$files === void 0 ? void 0 : _config$files.remote) === true
13012
+ });
11571
13013
  // if (locales?.required) {
11572
13014
  // console.error("Locales / i18n currently not implemented");
11573
13015
  // return {
@@ -12668,7 +14110,7 @@ function _extractValModules() {
12668
14110
  * instead would reintroduce exactly the silent skip described above.
12669
14111
  */
12670
14112
  function hasRemoteFileSchema(schema) {
12671
- if (schema.type === "file" || schema.type === "image") {
14113
+ if (schema.type === "file" || schema.type === "image" || schema.type === "video") {
12672
14114
  return !!schema.remote;
12673
14115
  } else if (schema.type === "richtext") {
12674
14116
  var _schema$options;
@@ -12747,7 +14189,7 @@ function hasRemoteFileSchema(schema) {
12747
14189
  * already has a data-loss incident attached to it (see above).
12748
14190
  */
12749
14191
  function hasMediaSchema(schema) {
12750
- if (schema.type === "file" || schema.type === "image") {
14192
+ if (schema.type === "file" || schema.type === "image" || schema.type === "video") {
12751
14193
  return true;
12752
14194
  } else if (schema.type === "richtext") {
12753
14195
  var _schema$options2;
@@ -13113,6 +14555,10 @@ function getValidationBasis(coreVersion, schema, fileExt, metadata, fileHash) {
13113
14555
  // bytes that arrived are valid. Leaving it in would mean that changing a
13114
14556
  // quality setting re-validates every remote file in the project.
13115
14557
  delete options.encode;
14558
+ // `stream` likewise: it is what the Studio made of an upload (an HLS
14559
+ // directory instead of one file), and every file it made is validated on its
14560
+ // own merits.
14561
+ delete options.stream;
13116
14562
  var schemaValidationBasis = {
13117
14563
  type: schema.type,
13118
14564
  opt: schema.opt,
@@ -13132,6 +14578,93 @@ function getFileHash(text) {
13132
14578
  return hashToRemoteFileHash(getSHA256Hash(new Uint8Array(text)));
13133
14579
  }
13134
14580
 
14581
+ var cache$1 = new WeakMap();
14582
+
14583
+ /**
14584
+ * The PUBLISHED entries of every gallery a module's fields pick from — an
14585
+ * `s.image(galleryVal)`, `s.file(filesVal)` or `s.video(videosVal)` — by the
14586
+ * gallery's module path.
14587
+ *
14588
+ * A gallery-backed value names its file and nothing else that is the
14589
+ * gallery's, so a reader has to look the entry up (`fillFromGallery`). In
14590
+ * draft mode the reader has every module's draft source to look in; outside
14591
+ * it, nothing — and the page got a set-backed video with no type, size,
14592
+ * poster or description, and an image with no dimensions. But the field's
14593
+ * schema was built from the gallery module (`s.video(videosVal)` reads it at
14594
+ * definition), so the published entries are already here, on the schema
14595
+ * instance. This is where they are read from.
14596
+ *
14597
+ * Stops at a view: the module it points at is read on its own, with its own
14598
+ * galleries.
14599
+ */
14600
+ function galleriesOf(schema) {
14601
+ if (schema === undefined) {
14602
+ return new Map();
14603
+ }
14604
+ var cached = cache$1.get(schema);
14605
+ if (cached) {
14606
+ return cached;
14607
+ }
14608
+ var galleries = new Map();
14609
+ var seen = new Set();
14610
+ function walk(node) {
14611
+ if (_typeof(node) !== "object" || node === null || seen.has(node)) {
14612
+ return;
14613
+ }
14614
+ seen.add(node);
14615
+ if (node instanceof ValViewSchema) {
14616
+ return;
14617
+ }
14618
+ if (node instanceof ImageSchema) {
14619
+ for (var _i = 0, _Object$entries = Object.entries(node["moduleMetadata"]); _i < _Object$entries.length; _i++) {
14620
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
14621
+ modulePath = _Object$entries$_i[0],
14622
+ entries = _Object$entries$_i[1];
14623
+ galleries.set(modulePath, entries);
14624
+ }
14625
+ return;
14626
+ }
14627
+ if (node instanceof FileSchema) {
14628
+ for (var _i2 = 0, _Object$entries2 = Object.entries(node["moduleMetadata"]); _i2 < _Object$entries2.length; _i2++) {
14629
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
14630
+ _modulePath = _Object$entries2$_i[0],
14631
+ _entries = _Object$entries2$_i[1];
14632
+ galleries.set(_modulePath, _entries);
14633
+ }
14634
+ return;
14635
+ }
14636
+ if (node instanceof VideoSchema) {
14637
+ var gallery = node["gallery"];
14638
+ if (gallery && gallery.entries !== null) {
14639
+ galleries.set(gallery.modulePath, gallery.entries);
14640
+ }
14641
+ return;
14642
+ }
14643
+ if (Array.isArray(node)) {
14644
+ var _iterator = _createForOfIteratorHelper(node),
14645
+ _step;
14646
+ try {
14647
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
14648
+ var item = _step.value;
14649
+ walk(item);
14650
+ }
14651
+ } catch (err) {
14652
+ _iterator.e(err);
14653
+ } finally {
14654
+ _iterator.f();
14655
+ }
14656
+ return;
14657
+ }
14658
+ for (var _i3 = 0, _Object$values = Object.values(node); _i3 < _Object$values.length; _i3++) {
14659
+ var value = _Object$values[_i3];
14660
+ walk(value);
14661
+ }
14662
+ }
14663
+ walk(schema);
14664
+ cache$1.set(schema, galleries);
14665
+ return galleries;
14666
+ }
14667
+
13135
14668
  /**
13136
14669
  * The variant of a discriminated union that a value takes, by its tag.
13137
14670
  *
@@ -13337,7 +14870,7 @@ function deserializeSchema(serialized) {
13337
14870
  return schema;
13338
14871
  }
13339
14872
  function deserializeSchemaImpl(serialized) {
13340
- var _serialized$options, _serialized$options2, _serialized$multiline, _serialized$render, _serialized$accept, _serialized$dir, _serialized$remote, _serialized$jsonValue, _serialized$render2, _serialized$external, _serialized$render3, _serialized$readonly, _serialized$hidden;
14873
+ var _serialized$options, _serialized$options2, _serialized$multiline, _serialized$render, _serialized$accept, _serialized$dir, _serialized$remote, _serialized$jsonValue, _serialized$render2, _serialized$external, _serialized$render3, _serialized$render4, _serialized$remote2, _serialized$readonly, _serialized$hidden;
13341
14874
  switch (serialized.type) {
13342
14875
  case "string":
13343
14876
  return new StringSchema(_objectSpread2(_objectSpread2({}, serialized.options), {}, {
@@ -13396,6 +14929,8 @@ function deserializeSchemaImpl(serialized) {
13396
14929
  accept: (_serialized$accept = serialized.accept) !== null && _serialized$accept !== void 0 ? _serialized$accept : "*/*",
13397
14930
  dir: (_serialized$dir = serialized.dir) !== null && _serialized$dir !== void 0 ? _serialized$dir : "/public/val",
13398
14931
  remote: (_serialized$remote = serialized.remote) !== null && _serialized$remote !== void 0 ? _serialized$remote : false,
14932
+ encode: serialized.encode,
14933
+ stream: serialized.stream,
13399
14934
  altSchema: serialized.alt ? deserializeSchema(serialized.alt) : undefined
13400
14935
  } : undefined, false, false, serialized.description, (_serialized$jsonValue = serialized.jsonValues) !== null && _serialized$jsonValue !== void 0 ? _serialized$jsonValue : false,
13401
14936
  // The preview closure cannot survive serialization; the render can.
@@ -13421,6 +14956,12 @@ function deserializeSchemaImpl(serialized) {
13421
14956
  return new FileSchema(serialized.options, serialized.opt, serialized.remote, [], {}, false, false, serialized.description);
13422
14957
  case "image":
13423
14958
  return new ImageSchema(serialized.options, serialized.opt, serialized.remote, [], {}, false, false, serialized.description);
14959
+ case "video":
14960
+ return new VideoSchema(serialized.options, serialized.opt, serialized.remote, [], false, false, serialized.description, (_serialized$render4 = serialized.render) !== null && _serialized$render4 !== void 0 ? _serialized$render4 : null, null, serialized.referencedModule ? {
14961
+ modulePath: serialized.referencedModule,
14962
+ entries: null,
14963
+ remote: (_serialized$remote2 = serialized.remote) !== null && _serialized$remote2 !== void 0 ? _serialized$remote2 : false
14964
+ } : null);
13424
14965
  case "date":
13425
14966
  return new DateSchema(serialized.options, serialized.opt, [], false, false, serialized.description);
13426
14967
  case "dateTime":
@@ -14116,7 +15657,7 @@ function isPageRouter(routerId) {
14116
15657
 
14117
15658
  var corePackageJson = {
14118
15659
  name: "@valbuild/core",
14119
- version: "0.139.1",
15660
+ version: "0.143.0",
14120
15661
  "private": false,
14121
15662
  description: "Val - supercharged hard-coded content",
14122
15663
  repository: {
@@ -14205,6 +15746,7 @@ var Internal = {
14205
15746
  VERSION: {
14206
15747
  core: corePackageJson.version
14207
15748
  },
15749
+ videosetEntryVideoSchema: videosetEntryVideoSchema,
14208
15750
  // A view's read-path plumbing: `Internal` rather than the public surface,
14209
15751
  // because an app never builds or unwraps a handle — it passes one to
14210
15752
  // `useVal`.
@@ -14215,9 +15757,15 @@ var Internal = {
14215
15757
  viewModulesOf: viewModulesOf,
14216
15758
  mediaUrl: mediaUrl,
14217
15759
  resolveMedia: resolveMedia,
15760
+ resolveVideo: resolveVideo,
14218
15761
  isRemoteMediaPath: isRemoteMediaPath,
14219
15762
  media: {
14220
- fillFromGallery: fillFromGallery
15763
+ fillFromGallery: fillFromGallery,
15764
+ galleriesOf: galleriesOf,
15765
+ galleryEntryOf: galleryEntryOf,
15766
+ GALLERY_DEFAULT_GROUPS: GALLERY_DEFAULT_GROUPS,
15767
+ isHlsVideo: isHlsVideo,
15768
+ HLS_MIME_TYPE: HLS_MIME_TYPE
14221
15769
  },
14222
15770
  getSchema: getSchema,
14223
15771
  getValPath: getValPath,
@@ -14265,6 +15813,13 @@ var Internal = {
14265
15813
  isExternalFileRef: isExternalFileRef,
14266
15814
  createValPathOfItem: createValPathOfItem,
14267
15815
  getSHA256Hash: getSHA256Hash,
15816
+ /**
15817
+ * The same hash as `getSHA256Hash`, fed a chunk at a time: what a caller
15818
+ * hashing hundreds of megabytes uses to give the thread back between chunks.
15819
+ */
15820
+ createSHA256Hash: function createSHA256Hash() {
15821
+ return createHash("sha256");
15822
+ },
14268
15823
  initSchema: initSchema,
14269
15824
  getMimeType: getMimeType,
14270
15825
  mimeTypeToFileExt: mimeTypeToFileExt,
@@ -14272,6 +15827,9 @@ var Internal = {
14272
15827
  mimeTypeMatchesAccept: mimeTypeMatchesAccept,
14273
15828
  EXT_TO_MIME_TYPES: EXT_TO_MIME_TYPES,
14274
15829
  MIME_TYPES_TO_EXT: MIME_TYPES_TO_EXT,
15830
+ FONT_MIME_TYPE_EXTENSIONS: FONT_MIME_TYPE_EXTENSIONS,
15831
+ isFontMimeType: isFontMimeType,
15832
+ sniffFontMimeType: sniffFontMimeType,
14275
15833
  ModuleFilePathSep: ModuleFilePathSep,
14276
15834
  notFileOp: function notFileOp(op) {
14277
15835
  return op.op !== "file";
@@ -14323,4 +15881,4 @@ function tryJsonParse(str) {
14323
15881
  }
14324
15882
  }
14325
15883
 
14326
- export { COLOR_FORMATS as $, ArraySchema as A, BooleanSchema as B, SETTINGS_MODULE_CONVENTION as C, DEFAULT_CONTENT_HOST as D, isRootModuleFilePath as E, FATAL_ERROR_TYPES as F, GenericSelector as G, resolveSettingsModule as H, Internal as I, localeTagError as J, localeOfValue as K, LocaleSchema as L, ModuleFilePathSep as M, NumberSchema as N, ObjectSchema as O, PatchError as P, declaredKeySetOf as Q, RecordSchema as R, Schema as S, THEME_LOGO_DIRECTORY as T, missingDeclaredKeys as U, VAL_EXTENSION as V, discriminatedUnionBranchOf as W, ColorSchema as X, CodeSchema as Y, CODE_LANGUAGES as Z, _typeof as _, _slicedToArray as a, DEFAULT_COLOR_FORMAT as a0, DateTimeSchema as a1, KeyOfSchema as a2, ValViewSchema as a3, isValViewSource as a4, RouteSchema as a5, RichTextSchema as a6, DiscriminatedUnionSchema as a7, EnumSchema as a8, LiteralSchema as a9, deserializeSchema as aa, previewScope as ab, isInlineRender as ac, parseNextJsRoutePattern as ad, parseTanStackRoutePattern as ae, tanStackSegmentsOfRoutePath as af, validateUrlAgainstPattern as ag, getSourcePathFromRoute as ah, isPageRouter as ai, describeSchemeRejection as aj, isCheckableScheme as ak, rejectScheme as al, schemeOf as am, UNSAFE_URL_SCHEMES as an, _createClass as b, _classCallCheck as c, _toConsumableArray as d, DEFAULT_APP_HOST as e, DEFAULT_STATIC_HOST as f, extractValModules as g, computeValModuleShas as h, initVal as i, computeSourcesSha as j, computeSchemaSha as k, hasRemoteFileSchema as l, modules as m, hasMediaSchema as n, DEFAULT_VAL_REMOTE_HOST as o, derefPatch as p, StringSchema as q, ImageSchema as r, splitModuleFilePathAndModulePath as s, FileSchema as t, DateSchema as u, SettingsSchema as v, ASSISTANT_SETTINGS_MAX_LENGTH as w, THEME_RADIUS_STEPS as x, THEME_RADIUS_LENGTHS as y, assistantAvailability as z };
15884
+ export { declaredKeySetOf as $, ArraySchema as A, BooleanSchema as B, DateSchema as C, DEFAULT_CONTENT_HOST as D, SettingsSchema as E, FATAL_ERROR_TYPES as F, GenericSelector as G, ASSISTANT_SETTINGS_MAX_LENGTH as H, Internal as I, THEME_RADIUS_STEPS as J, THEME_RADIUS_LENGTHS as K, assistantAvailability as L, ModuleFilePathSep as M, NumberSchema as N, ObjectSchema as O, PatchError as P, SETTINGS_MODULE_CONVENTION as Q, RecordSchema as R, Schema as S, THEME_LOGO_DIRECTORY as T, isRootModuleFilePath as U, VAL_EXTENSION as V, resolveSettingsModule as W, localeTagError as X, localeOfValue as Y, LocaleSchema as Z, _typeof as _, _slicedToArray as a, missingDeclaredKeys as a0, discriminatedUnionBranchOf as a1, ColorSchema as a2, CodeSchema as a3, CODE_LANGUAGES as a4, COLOR_FORMATS as a5, DEFAULT_COLOR_FORMAT as a6, DateTimeSchema as a7, KeyOfSchema as a8, ValViewSchema as a9, isValViewSource as aa, RouteSchema as ab, RichTextSchema as ac, DiscriminatedUnionSchema as ad, EnumSchema as ae, LiteralSchema as af, deserializeSchema as ag, previewScope as ah, isInlineRender as ai, parseNextJsRoutePattern as aj, parseTanStackRoutePattern as ak, tanStackSegmentsOfRoutePath as al, validateUrlAgainstPattern as am, getSourcePathFromRoute as an, isPageRouter as ao, describeSchemeRejection as ap, isCheckableScheme as aq, rejectScheme as ar, schemeOf as as, UNSAFE_URL_SCHEMES as at, _createClass as b, _classCallCheck as c, _toConsumableArray as d, DEFAULT_APP_HOST as e, DEFAULT_STATIC_HOST as f, extractValModules as g, computeValModuleShas as h, initVal as i, computeSourcesSha as j, computeSchemaSha as k, hasRemoteFileSchema as l, modules as m, hasMediaSchema as n, DEFAULT_VAL_REMOTE_HOST as o, derefPatch as p, StringSchema as q, ImageSchema as r, splitModuleFilePathAndModulePath as s, FileSchema as t, DEFAULT_FONT_ACCEPT as u, FONT_MIME_TYPES as v, VideoSchema as w, DEFAULT_VIDEO_ACCEPT as x, DEFAULT_VIDEO_RENDITIONS as y, DEFAULT_VIDEO_SEGMENT_DURATION as z };