@valbuild/core 0.141.0 → 0.144.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 (29) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/declarations/src/index.d.ts +13 -4
  3. package/dist/declarations/src/initSchema.d.ts +108 -18
  4. package/dist/declarations/src/initVal.d.ts +2 -2
  5. package/dist/declarations/src/mimeType/font.d.ts +36 -0
  6. package/dist/declarations/src/mimeType/index.d.ts +1 -0
  7. package/dist/declarations/src/render.d.ts +1 -1
  8. package/dist/declarations/src/schema/file.d.ts +4 -4
  9. package/dist/declarations/src/schema/filedir.d.ts +83 -0
  10. package/dist/declarations/src/schema/font.d.ts +16 -0
  11. package/dist/declarations/src/schema/fontdir.d.ts +63 -0
  12. package/dist/declarations/src/schema/hasRemoteFileSchema.d.ts +3 -3
  13. package/dist/declarations/src/schema/image.d.ts +4 -4
  14. package/dist/declarations/src/schema/{imageset.d.ts → imagedir.d.ts} +63 -29
  15. package/dist/declarations/src/schema/record.d.ts +17 -6
  16. package/dist/declarations/src/schema/video.d.ts +7 -7
  17. package/dist/declarations/src/schema/{videoset.d.ts → videodir.d.ts} +55 -27
  18. package/dist/declarations/src/source/media.d.ts +26 -3
  19. package/dist/{index-13cf45b1.cjs.prod.js → index-2d194c90.cjs.prod.js} +504 -79
  20. package/dist/{index-fae161b5.cjs.dev.js → index-8b22b2c1.cjs.dev.js} +504 -79
  21. package/dist/{index-a6c53286.esm.js → index-aa16dd90.esm.js} +503 -80
  22. package/dist/valbuild-core.cjs.dev.js +3 -1
  23. package/dist/valbuild-core.cjs.prod.js +3 -1
  24. package/dist/valbuild-core.esm.js +1 -1
  25. package/package.json +1 -1
  26. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  27. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  28. package/patch/dist/valbuild-core-patch.esm.js +2 -2
  29. package/dist/declarations/src/schema/fileset.d.ts +0 -57
package/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # @valbuild/core
2
2
 
3
+ ## 0.144.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#826](https://github.com/valbuild/val/pull/826) [`860ab2b`](https://github.com/valbuild/val/commit/860ab2b87333daf95a48215b0e4a1b44f441c878) Thanks [@freekh](https://github.com/freekh)! - New names for the media collections: `s.imagedir()`, `s.filedir()`, `s.fontdir()` and `s.videodir()`. The directory the uploads land in is now the first argument instead of the `dir` option:
8
+
9
+ ```ts
10
+ // before
11
+ s.imageset({ dir: "/public/val/images", accept: "image/webp" });
12
+ s.fileset({ dir: "/public/val/docs", accept: "application/pdf" });
13
+ s.fontset({ dir: "/public/val/fonts" });
14
+ s.videoset({ dir: "/public/val/videos", stream: { type: "hls" } });
15
+
16
+ // after
17
+ s.imagedir("/public/val/images", { accept: "image/webp" });
18
+ s.filedir("/public/val/docs", { accept: "application/pdf" });
19
+ s.fontdir("/public/val/fonts");
20
+ s.videodir("/public/val/videos", { stream: { type: "hls" } });
21
+ ```
22
+
23
+ Every other option is unchanged, and so is the content: a module only needs its schema call changed. The options object can now be left out entirely where nothing else is set, as in `s.fontdir("/public/val/fonts")`.
24
+
25
+ `s.imageset()`, `s.fileset()`, `s.fontset()` and `s.videoset()` still work and build exactly the same schema, but are deprecated and will be removed in a later release. The exported `VideosetEntryMetadata`, `VideosetOptions` and `SerializedVideosetSchema` types are deprecated in favour of `VideodirEntryMetadata`, `VideodirOptions` and `SerializedVideodirSchema`, and the image, file and font equivalents (`ImagedirOptions`, `ImagedirEntryMetadata`, `FiledirOptions`, `FiledirEntryMetadata`, `FontdirOptions`, `MediaDir`, …) are now exported too.
26
+
27
+ ### Patch Changes
28
+
29
+ - [#788](https://github.com/valbuild/val/pull/788) [`a84271c`](https://github.com/valbuild/val/commit/a84271c534822071c1e428cf9990602459682d25) Thanks [@freekh](https://github.com/freekh)! - Fixes to `npx val validate` for `s.imagedir()` and `s.filedir()`, and the `s.image(galleryVal)` / `s.file(filesVal)` fields that use them:
30
+
31
+ - **`--fix` now updates every field that uses an uploaded image or file.** When `--fix` moves an entry of an `s.imagedir(dir).remote()` or `s.filedir(dir, options).remote()` to Val Remote, the entry's key changes from the local path to the remote URL. Fields that held the old path were left pointing at it, and reported "The gallery does not have an image at …" with nothing to fix it. Now `s.image(galleryVal)` and `s.file(filesVal)` fields, and images in rich text (`s.richtext({ img: s.image(galleryVal) })`), are updated in the same run. The VS Code extension's "upload to Val Remote" updates them in the same edit, including fields inside `.jsonValues()` entries, which live in their own `*.val.json` files.
32
+ - **A field that uses an image or file from a `.remote()` gallery is no longer reported as "Expected local image, but found remote".** The gallery decides whether its files are on Val Remote; the field only has to name a file the gallery has. Before, `--fix` would download the file and point the field at a path the gallery did not have.
33
+ - **Images uploaded through a field into a `.remote()` gallery.** When you upload an image through an `s.image(galleryVal)` field in Val Studio, the field stores the remote URL and the gallery stores the image under its local path. Validation now accepts this, and `useVal` / `fetchVal` now fill in the image's `width`, `height` and `mimeType` from the gallery for it, as they do for every other gallery image.
34
+ - **Errors in a gallery's keys are reported.** A remote URL in a gallery that is not `.remote()`, a remote URL that is not valid, or one outside the gallery's `dir` was silently ignored by `npx val validate`. It is now reported as an error. `--fix` does not change it: you decide whether the key or the gallery's options are wrong.
35
+ - **A file is no longer reported as valid when it still has an error.** When `--fix` fixed one error in a file and another error was left, the file could be printed as "✔ valid".
36
+
37
+ ## 0.143.0
38
+
39
+ ### Minor Changes
40
+
41
+ - [#813](https://github.com/valbuild/val/pull/813) [`f7c25bd`](https://github.com/valbuild/val/commit/f7c25bd4b44c288db8e5ab914e6d8a72c0216ba5) Thanks [@freekh](https://github.com/freekh)! - New: `s.font(fontsVal)`, a field that picks a font from an `s.fontset()`.
42
+
43
+ ```ts
44
+ s.object({
45
+ headingFont: s.font(fontsVal),
46
+ title: s.string(),
47
+ });
48
+ ```
49
+
50
+ It stores `{ path }` like `s.file(fontsVal)` does, and refuses a module that is not a font set. In the Studio, a font field is shown among its object's other fields as a small row — the letter "A" set in the chosen font, and the font's file name. Click it to open the field, which shows every font in the set to pick from, an upload button, and a full specimen of the chosen font. This applies to `s.file()` fields that point at a font set too.
51
+
52
+ - [#813](https://github.com/valbuild/val/pull/813) [`729c4ed`](https://github.com/valbuild/val/commit/729c4edfa28f5a2942c435ccf225e65858b080cc) Thanks [@freekh](https://github.com/freekh)! - New: `s.fontset()`, a collection of fonts.
53
+
54
+ ```ts
55
+ const fontsVal = c.define(
56
+ "/content/fonts.val.ts",
57
+ s.fontset({ dir: "/public/val/fonts" }),
58
+ {},
59
+ );
60
+ // a field that picks one of them
61
+ s.object({ headingFont: s.file(fontsVal) });
62
+ ```
63
+
64
+ It is an `s.fileset()` whose `accept` defaults to the web font formats (`font/woff2`, `font/woff`, `font/ttf`, `font/otf`); pass `accept: "font/woff2"` to allow only WOFF2. Fonts are not converted on upload.
65
+
66
+ The Studio now previews fonts set in themselves — in the gallery, in the picker of a field that points at a font set, and in the field — wherever a file is a font, including fonts in an ordinary `s.fileset()`.
67
+
68
+ Font uploads are typed from their bytes, so a font is stored as `font/woff2`, `font/ttf` and so on even when the browser reports no type for it. Files ending in `.ttf`, `.otf` and `.woff` now map to the `font/` types rather than the older `application/x-font-*` names, which are still accepted where they are already stored.
69
+
3
70
  ## 0.141.0
4
71
 
5
72
  ### Minor Changes
@@ -7,8 +7,12 @@ export { hasRemoteFileSchema, hasMediaSchema, } from "./schema/hasRemoteFileSche
7
7
  export type { ImageMetadata, ImageEncodeOption, ImageEncodeOptions, } from "./schema/image.js";
8
8
  export type { FileMetadata } from "./schema/file.js";
9
9
  export type { GalleryVideoOptions, VideoMetadata, VideoOptions, VideoStreamOption, VideoStreamOptions, } from "./schema/video.js";
10
- import { videosetEntryVideoSchema } from "./schema/videoset.js";
11
- export type { VideosetEntryMetadata, VideosetOptions, SerializedVideosetSchema, } from "./schema/videoset.js";
10
+ import { videosetEntryVideoSchema } from "./schema/videodir.js";
11
+ export type { VideodirEntryMetadata, VideodirOptions, SerializedVideodirSchema, VideosetEntryMetadata, VideosetOptions, SerializedVideosetSchema, } from "./schema/videodir.js";
12
+ export type { ImagedirEntryMetadata, ImagedirOptions, SerializedImagedirSchema, } from "./schema/imagedir.js";
13
+ export type { FiledirEntryMetadata, FiledirOptions, SerializedFiledirSchema, } from "./schema/filedir.js";
14
+ export type { FontdirOptions } from "./schema/fontdir.js";
15
+ export type { MediaDir } from "./schema/record.js";
12
16
  export type { ValModule, SerializedModule, InferValModuleType } from "./module.js";
13
17
  export type { SourceObject, SourcePrimitive, Source } from "./source/index.js";
14
18
  export type { FileSource } from "./source/media.js";
@@ -35,7 +39,7 @@ import { ModulePath, SourcePath, getValPath, isVal } from "./val/index.js";
35
39
  import { createValPathOfItem } from "./selector/SelectorProxy.js";
36
40
  import { Operation } from "./patch/index.js";
37
41
  import { initSchema } from "./initSchema.js";
38
- import { getMimeType, mimeTypeToFileExt, filenameToMimeType, mimeTypeMatchesAccept } from "./mimeType/index.js";
42
+ import { getMimeType, mimeTypeToFileExt, filenameToMimeType, mimeTypeMatchesAccept, isFontMimeType, sniffFontMimeType } from "./mimeType/index.js";
39
43
  import { type ImageMetadata } from "./schema/image.js";
40
44
  import { type FileMetadata } from "./schema/file.js";
41
45
  import { isJson, getJsonImport, resolveJsonValues } from "./source/json.js";
@@ -47,7 +51,7 @@ import { getValidationBasis, getValidationHash } from "./remote/validationBasis.
47
51
  import { getFileHash, hashToRemoteFileHash } from "./remote/fileHash.js";
48
52
  import { splitRemoteRef } from "./remote/splitRemoteRef.js";
49
53
  import { galleriesOf } from "./source/galleries.js";
50
- import { fillFromGallery, galleryEntryOf, isHlsVideo, isRemoteMediaPath, mediaUrl, resolveMedia, resolveVideo } from "./source/media.js";
54
+ import { fillFromGallery, galleryEntryOf, galleryKeyOf, isHlsVideo, isRemoteMediaPath, mediaUrl, resolveMedia, resolveVideo } from "./source/media.js";
51
55
  import { colorToHex, convertColor, detectColorFormat, formatColor, parseColor } from "./schema/colorFormat.js";
52
56
  export { type SerializedArraySchema, ArraySchema } from "./schema/array.js";
53
57
  export { type SerializedObjectSchema, ObjectSchema } from "./schema/object.js";
@@ -57,6 +61,7 @@ export { type SerializedNumberSchema, NumberSchema } from "./schema/number.js";
57
61
  export { type SerializedBooleanSchema, BooleanSchema } from "./schema/boolean.js";
58
62
  export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
59
63
  export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
64
+ export { DEFAULT_FONT_ACCEPT, FONT_MIME_TYPES } from "./mimeType/font.js";
60
65
  export { type SerializedVideoSchema, VideoSchema, DEFAULT_VIDEO_ACCEPT, DEFAULT_VIDEO_RENDITIONS, DEFAULT_VIDEO_SEGMENT_DURATION, } from "./schema/video.js";
61
66
  export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
62
67
  export { type SerializedSettingsSchema, SettingsSchema, } from "./schema/settings.js";
@@ -147,6 +152,7 @@ declare const Internal: {
147
152
  fillFromGallery: typeof fillFromGallery;
148
153
  galleriesOf: typeof galleriesOf;
149
154
  galleryEntryOf: typeof galleryEntryOf;
155
+ galleryKeyOf: typeof galleryKeyOf;
150
156
  GALLERY_DEFAULT_GROUPS: readonly (readonly string[])[];
151
157
  isHlsVideo: typeof isHlsVideo;
152
158
  HLS_MIME_TYPE: string;
@@ -208,6 +214,9 @@ declare const Internal: {
208
214
  MIME_TYPES_TO_EXT: {
209
215
  [k: string]: string;
210
216
  };
217
+ FONT_MIME_TYPE_EXTENSIONS: Record<string, string>;
218
+ isFontMimeType: typeof isFontMimeType;
219
+ sniffFontMimeType: typeof sniffFontMimeType;
211
220
  ModuleFilePathSep: string;
212
221
  notFileOp: (op: Operation) => op is {
213
222
  op: "add";
@@ -13,8 +13,10 @@ import { keyOf } from "./schema/keyOf.js";
13
13
  import { view } from "./schema/view.js";
14
14
  import { record } from "./schema/record.js";
15
15
  import { file } from "./schema/file.js";
16
+ import { font } from "./schema/font.js";
16
17
  import { video } from "./schema/video.js";
17
- import { fileset } from "./schema/fileset.js";
18
+ import { filedir, fileset } from "./schema/filedir.js";
19
+ import { fontdir, fontset } from "./schema/fontdir.js";
18
20
  import { date } from "./schema/date.js";
19
21
  import { datetime } from "./schema/datetime.js";
20
22
  import { code } from "./schema/code.js";
@@ -22,8 +24,8 @@ import { color } from "./schema/color.js";
22
24
  import { locale } from "./schema/locale.js";
23
25
  import { route } from "./schema/route.js";
24
26
  import { router } from "./schema/router.js";
25
- import { imageset } from "./schema/imageset.js";
26
- import { videoset } from "./schema/videoset.js";
27
+ import { imagedir, imageset } from "./schema/imagedir.js";
28
+ import { videodir, videoset } from "./schema/videodir.js";
27
29
  import { settings } from "./schema/settings.js";
28
30
  export type InitSchema = {
29
31
  /**
@@ -151,7 +153,7 @@ export type InitSchema = {
151
153
  *
152
154
  * @example
153
155
  * // Backed by a gallery: width, height and mimeType live there.
154
- * import galleryVal from "./gallery.val"; // an s.imageset() module
156
+ * import galleryVal from "./gallery.val"; // an s.imagedir() module
155
157
  * const schema = s.image(galleryVal);
156
158
  * export default c.define("/example.val.ts", schema, {
157
159
  * path: "/public/val/example.png",
@@ -219,6 +221,19 @@ export type InitSchema = {
219
221
  *
220
222
  */
221
223
  readonly file: typeof file;
224
+ /**
225
+ * A font picked from an `s.fontdir()`: `s.file(fontsVal)` for fonts, which
226
+ * refuses a module that is not a font set. The Studio shows it in its parent
227
+ * as the font's "A" and file name, and opens it on the set to pick from.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * import fontsVal from "./fonts.val"; // an s.fontdir() module
232
+ * const schema = s.object({ headingFont: s.font(fontsVal) });
233
+ * // source: { headingFont: { path: "/public/val/fonts/inter_a1b2c.woff2" } }
234
+ * ```
235
+ */
236
+ readonly font: typeof font;
222
237
  /**
223
238
  * Define a video.
224
239
  *
@@ -246,7 +261,7 @@ export type InitSchema = {
246
261
  *
247
262
  * @example
248
263
  * // Picked from a set: mimeType, width, height and duration live there.
249
- * import videosVal from "./videos.val"; // an s.videoset() module
264
+ * import videosVal from "./videos.val"; // an s.videodir() module
250
265
  * const schema = s.video(videosVal);
251
266
  * export default c.define("/example.val.ts", schema, {
252
267
  * path: "/public/val/videos/intro_51df2.mp4",
@@ -362,13 +377,16 @@ export type InitSchema = {
362
377
  */
363
378
  readonly router: typeof router;
364
379
  /**
365
- * Define a collection of images.
380
+ * Define a directory of images: a whole module of images, keyed by file
381
+ * path, that fields pick from with `s.image(galleryVal)`.
382
+ *
383
+ * The first argument is the directory uploads land in, and must start with
384
+ * `/public`.
366
385
  *
367
386
  * @example
368
387
  * ```typescript
369
- * const schema = s.imageset({
388
+ * const schema = s.imagedir("/public/val/images", {
370
389
  * accept: "image/webp",
371
- * dir: "/public/val/images",
372
390
  * alt: s.string().minLength(4),
373
391
  * });
374
392
  * export default c.define("/content/images.val.ts", schema, {
@@ -381,15 +399,18 @@ export type InitSchema = {
381
399
  * });
382
400
  * ```
383
401
  */
384
- readonly imageset: typeof imageset;
402
+ readonly imagedir: typeof imagedir;
385
403
  /**
386
- * Define a collection of files.
404
+ * Define a directory of files: a whole module of files, keyed by file path,
405
+ * that fields pick from with `s.file(filesVal)`.
406
+ *
407
+ * The first argument is the directory uploads land in, and must start with
408
+ * `/public`.
387
409
  *
388
410
  * @example
389
411
  * ```typescript
390
- * const schema = s.fileset({
412
+ * const schema = s.filedir("/public/val/documents", {
391
413
  * accept: "application/pdf",
392
- * dir: "/public/val/documents",
393
414
  * });
394
415
  * export default c.define("/content/documents.val.ts", schema, {
395
416
  * "/public/val/documents/report.pdf": {
@@ -398,15 +419,30 @@ export type InitSchema = {
398
419
  * });
399
420
  * ```
400
421
  */
401
- readonly fileset: typeof fileset;
422
+ readonly filedir: typeof filedir;
402
423
  /**
403
- * Define a collection of videos. A field picks from it with
424
+ * Define a directory of fonts: an `s.filedir()` that accepts the web font
425
+ * formats, and whose files the Studio previews as type. A field picks from
426
+ * it with `s.font(fontsVal)`.
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * const schema = s.fontdir("/public/val/fonts");
431
+ * export default c.define("/content/fonts.val.ts", schema, {
432
+ * "/public/val/fonts/inter_a1b2c.woff2": {
433
+ * mimeType: "font/woff2",
434
+ * },
435
+ * });
436
+ * ```
437
+ */
438
+ readonly fontdir: typeof fontdir;
439
+ /**
440
+ * Define a directory of videos. A field picks from it with
404
441
  * `s.video(videosVal)`.
405
442
  *
406
443
  * @example
407
444
  * ```typescript
408
- * const schema = s.videoset({
409
- * dir: "/public/val/videos",
445
+ * const schema = s.videodir("/public/val/videos", {
410
446
  * stream: { type: "hls" },
411
447
  * });
412
448
  * export default c.define("/content/videos.val.ts", schema, {
@@ -420,6 +456,60 @@ export type InitSchema = {
420
456
  * });
421
457
  * ```
422
458
  */
459
+ readonly videodir: typeof videodir;
460
+ /**
461
+ * Define a collection of images.
462
+ *
463
+ * @deprecated Use `s.imagedir(dir, options)`: the directory moved out of the
464
+ * options and into the first argument.
465
+ *
466
+ * @example
467
+ * ```typescript
468
+ * // was: s.imageset({ dir: "/public/val/images", accept: "image/webp" })
469
+ * const schema = s.imagedir("/public/val/images", { accept: "image/webp" });
470
+ * ```
471
+ */
472
+ readonly imageset: typeof imageset;
473
+ /**
474
+ * Define a collection of files.
475
+ *
476
+ * @deprecated Use `s.filedir(dir, options)`: the directory moved out of the
477
+ * options and into the first argument.
478
+ *
479
+ * @example
480
+ * ```typescript
481
+ * // was: s.fileset({ dir: "/public/val/documents", accept: "application/pdf" })
482
+ * const schema = s.filedir("/public/val/documents", {
483
+ * accept: "application/pdf",
484
+ * });
485
+ * ```
486
+ */
487
+ readonly fileset: typeof fileset;
488
+ /**
489
+ * Define a collection of fonts.
490
+ *
491
+ * @deprecated Use `s.fontdir(dir, options)`: the directory moved out of the
492
+ * options and into the first argument.
493
+ *
494
+ * @example
495
+ * ```typescript
496
+ * // was: s.fontset({ dir: "/public/val/fonts" })
497
+ * const schema = s.fontdir("/public/val/fonts");
498
+ * ```
499
+ */
500
+ readonly fontset: typeof fontset;
501
+ /**
502
+ * Define a collection of videos.
503
+ *
504
+ * @deprecated Use `s.videodir(dir, options)`: the directory moved out of the
505
+ * options and into the first argument.
506
+ *
507
+ * @example
508
+ * ```typescript
509
+ * // was: s.videoset({ dir: "/public/val/videos", stream: { type: "hls" } })
510
+ * const schema = s.videodir("/public/val/videos", { stream: { type: "hls" } });
511
+ * ```
512
+ */
423
513
  readonly videoset: typeof videoset;
424
514
  /**
425
515
  * Define the project's settings.
@@ -451,8 +541,8 @@ export type InitSchemaOptions = {
451
541
  /**
452
542
  * Every media schema is remote: `files.remote` in `val.config.ts`.
453
543
  *
454
- * `s.image()`, `s.file()`, `s.video()`, `s.imageset()`, `s.fileset()` and
455
- * `s.videoset()` come back with `.remote()` already applied, and so does the
544
+ * `s.image()`, `s.file()`, `s.video()`, `s.imagedir()`, `s.filedir()`,
545
+ * `s.fontdir()` and `s.videodir()` (and their deprecated `*set` names) come back with `.remote()` already applied, and so does the
456
546
  * image `s.richtext({ img: true })` builds for itself. Nothing downstream
457
547
  * needs to know: the Studio, the MCP tools, validation and publishing all
458
548
  * read `remote` off the schema, exactly as they do for a `.remote()` written
@@ -80,8 +80,8 @@ export type ValConfig = {
80
80
  * Store every image, file and video on Val's remote content host instead
81
81
  * of in the repository.
82
82
  *
83
- * `s.image()`, `s.file()`, `s.video()`, `s.imageset()`, `s.fileset()`,
84
- * `s.videoset()` and `s.richtext({ img: true })` all behave as though
83
+ * `s.image()`, `s.file()`, `s.video()`, `s.imagedir()`, `s.filedir()`,
84
+ * `s.fontdir()`, `s.videodir()` and `s.richtext({ img: true })` all behave as though
85
85
  * `.remote()` had been written on them. Required for a project in the Val
86
86
  * app, which has no repository for the bytes to go into: there the Val
87
87
  * server refuses to start without it.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Fonts, as far as a mime type can tell.
3
+ *
4
+ * The four web formats have IANA types under `font/` (RFC 8081), and those are
5
+ * what Val stores. Browsers and operating systems disagree about them: a file
6
+ * picker reports `.woff2` as `font/woff2`, `application/font-woff2` or nothing
7
+ * at all depending on the platform, and nothing at all reads as
8
+ * `application/octet-stream` once the file is a data URL. That is why an upload
9
+ * asks the BYTES ({@link sniffFontMimeType}) rather than trusting the picker.
10
+ */
11
+ /** The formats `s.fontdir()` accepts unless told otherwise, best first. */
12
+ export declare const FONT_MIME_TYPES: readonly ["font/woff2", "font/woff", "font/ttf", "font/otf"];
13
+ /** `s.fontdir()`'s `accept` when none is given. */
14
+ export declare const DEFAULT_FONT_ACCEPT: string;
15
+ /**
16
+ * The extension of each font type, for a file input: a picker matches an
17
+ * `accept` of mime types through the operating system's own table, and where
18
+ * that table does not know `font/woff2` the file is greyed out.
19
+ */
20
+ export declare const FONT_MIME_TYPE_EXTENSIONS: Record<string, string>;
21
+ /** `mimeType`, with a legacy font name read as its `font/` type. */
22
+ export declare function canonicalFontMimeType(mimeType: string): string;
23
+ /**
24
+ * Whether a file of `mimeType` is a font a browser can draw: the `font/` types,
25
+ * and the pre-RFC 8081 names still found in older data.
26
+ */
27
+ export declare function isFontMimeType(mimeType: string | null | undefined): boolean;
28
+ /**
29
+ * The font type of `bytes`, read from the signature every font format starts
30
+ * with, or `null` when they are not a font.
31
+ *
32
+ * Only the first four bytes are needed: the WOFF signatures (`wOFF`, `wOF2`),
33
+ * the SFNT versions of TrueType (`0x00010000`, or `true` from old Apple fonts)
34
+ * and of CFF-flavoured OpenType (`OTTO`), and a collection's tag (`ttcf`).
35
+ */
36
+ export declare function sniffFontMimeType(bytes: Uint8Array): string | null;
@@ -1,2 +1,3 @@
1
1
  export * from "./convertMimeType.js";
2
2
  export * from "./all.js";
3
+ export * from "./font.js";
@@ -75,7 +75,7 @@ import type { SerializedSchema } from "./schema/index.js";
75
75
  * `s.array(s.object({ tags: s.array(s.string()) })).render({ as: "inline" })`
76
76
  * the objects are inline and `tags` keeps its own default.
77
77
  *
78
- * Not available on `s.router(...)`, `s.imageset(...)` or `s.fileset(...)`:
78
+ * Not available on `s.router(...)`, `s.imagedir(...)` or `s.filedir(...)`:
79
79
  * pages and media have UIs of their own, and `RecordSchema.render` throws there.
80
80
  *
81
81
  * On `s.keyOf(...)` the same value means something else: the selected entry's
@@ -5,7 +5,7 @@ import { ModulePath, SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
6
6
  import { ValModule } from "../index.js";
7
7
  import { ItemPreviewInput, PreviewItem } from "../preview.js";
8
- import { FilesetEntryMetadata } from "./fileset.js";
8
+ import { FiledirEntryMetadata } from "./filedir.js";
9
9
  export type FileOptions = {
10
10
  accept?: string;
11
11
  };
@@ -32,7 +32,7 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
32
32
  private readonly isHidden;
33
33
  private readonly description?;
34
34
  private readonly previewInput;
35
- constructor(options?: FileOptions | undefined, opt?: boolean, isRemote?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], moduleMetadata?: Record<ModulePath, Record<string, FilesetEntryMetadata>>, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null);
35
+ constructor(options?: FileOptions | undefined, opt?: boolean, isRemote?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], moduleMetadata?: Record<ModulePath, Record<string, FiledirEntryMetadata>>, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null);
36
36
  /**
37
37
  * Describe this field.
38
38
  *
@@ -135,7 +135,7 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
135
135
  * A file picked from a gallery. Its mime type lives in the gallery, so the
136
136
  * field carries only the path.
137
137
  */
138
- export declare function file(galleryModule: ValModule<Record<string, FilesetEntryMetadata>>): FileSchema<GalleryFileSource>;
138
+ export declare function file(galleryModule: ValModule<Record<string, FiledirEntryMetadata>>): FileSchema<GalleryFileSource>;
139
139
  /** A file of its own, carrying its own mime type. */
140
140
  export declare function file(options?: FileOptions): FileSchema<FileSource>;
141
141
  /**
@@ -143,5 +143,5 @@ export declare function file(options?: FileOptions): FileSchema<FileSource>;
143
143
  * `val.config.ts`): the same schema, with `.remote()` already applied. See
144
144
  * `initSchema`.
145
145
  */
146
- export declare function remoteFile(galleryModule: ValModule<Record<string, FilesetEntryMetadata>>): FileSchema<GalleryFileSource>;
146
+ export declare function remoteFile(galleryModule: ValModule<Record<string, FiledirEntryMetadata>>): FileSchema<GalleryFileSource>;
147
147
  export declare function remoteFile(options?: FileOptions): FileSchema<FileSource>;
@@ -0,0 +1,83 @@
1
+ import { Schema } from "./index.js";
2
+ import type { MediaDir, SerializedRecordSchema } from "./record.js";
3
+ import { RecordSchema } from "./record.js";
4
+ import { ObjectSchema } from "./object.js";
5
+ import { StringSchema } from "./string.js";
6
+ /**
7
+ * Options for `s.filedir(dir, options)`: everything but the directory, which
8
+ * is the first argument.
9
+ */
10
+ export type FiledirOptions = {
11
+ /**
12
+ * The accepted mime type pattern (e.g., "application/pdf", "text/*", "*\/*")
13
+ */
14
+ accept: string;
15
+ };
16
+ /**
17
+ * Options for the deprecated `s.fileset(options)`, which took the directory
18
+ * as `dir`.
19
+ *
20
+ * @deprecated Use `s.filedir(dir, options)` and `FiledirOptions`.
21
+ */
22
+ export type FilesetOptions = FiledirOptions & {
23
+ /**
24
+ * The directory where files should be stored.
25
+ * Must start with "/public" (e.g., "/public/val/files")
26
+ */
27
+ dir: MediaDir;
28
+ };
29
+ /**
30
+ * Metadata for a file entry in the files record
31
+ */
32
+ export type FiledirEntryMetadata = {
33
+ mimeType: string;
34
+ };
35
+ /** @deprecated Use `FiledirEntryMetadata`. */
36
+ export type FilesetEntryMetadata = FiledirEntryMetadata;
37
+ export type SerializedFiledirSchema = SerializedRecordSchema;
38
+ /** @deprecated Use `SerializedFiledirSchema`. */
39
+ export type SerializedFilesetSchema = SerializedFiledirSchema;
40
+ type FiledirItemProps = {
41
+ mimeType: StringSchema<string>;
42
+ };
43
+ type FiledirItemSrc = {
44
+ mimeType: string;
45
+ };
46
+ type FiledirSchema = RecordSchema<ObjectSchema<FiledirItemProps, FiledirItemSrc>, Schema<string>, Record<string, FiledirEntryMetadata>>;
47
+ /**
48
+ * Define a directory of files: a whole module that holds many files, keyed by
49
+ * their file path, that fields pick from with `s.file(filesVal)`.
50
+ *
51
+ * The directory is the first argument and is required — it decides where
52
+ * uploads land. Remote is off by default: call `.remote()` on the result to
53
+ * allow remote files.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const schema = s.filedir("/public/val/documents", {
58
+ * accept: "application/pdf",
59
+ * });
60
+ * export default c.define("/content/documents.val.ts", schema, {
61
+ * "/public/val/documents/report.pdf": {
62
+ * mimeType: "application/pdf",
63
+ * },
64
+ * });
65
+ * ```
66
+ */
67
+ export declare const filedir: (dir: MediaDir, options: FiledirOptions) => FiledirSchema;
68
+ /**
69
+ * Define a collection of files.
70
+ *
71
+ * @deprecated Use `s.filedir(dir, options)`: the directory moved out of the
72
+ * options and into the first argument, and nothing else changed.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // was: s.fileset({ dir: "/public/val/documents", accept: "application/pdf" })
77
+ * const schema = s.filedir("/public/val/documents", {
78
+ * accept: "application/pdf",
79
+ * });
80
+ * ```
81
+ */
82
+ export declare const fileset: (options: FilesetOptions) => FiledirSchema;
83
+ export {};
@@ -0,0 +1,16 @@
1
+ import { ValModule } from "../index.js";
2
+ import { FileSchema } from "./file.js";
3
+ import { FiledirEntryMetadata } from "./filedir.js";
4
+ import { GalleryFileSource } from "../source/media.js";
5
+ /**
6
+ * A font picked from an `s.fontdir()`.
7
+ *
8
+ * The same field `s.file(fontsVal)` is — it stores `{ path }` and serializes
9
+ * identically — with one thing more: it refuses a set that is not a font set,
10
+ * at definition, so a field called a font cannot end up offering PDFs. What the
11
+ * Studio does with it (a specimen in its parent, the set to pick from when
12
+ * opened) it does for any file field backed by a font set.
13
+ */
14
+ export declare function font(fontsetModule: ValModule<Record<string, FiledirEntryMetadata>>): FileSchema<GalleryFileSource>;
15
+ /** `s.font()` with `.remote()` applied: see `files.remote` in `initSchema`. */
16
+ export declare function remoteFont(fontsetModule: ValModule<Record<string, FiledirEntryMetadata>>): FileSchema<GalleryFileSource>;
@@ -0,0 +1,63 @@
1
+ import { filedir } from "./filedir.js";
2
+ import type { MediaDir } from "./record.js";
3
+ /**
4
+ * Options for `s.fontdir(dir, options)`: everything but the directory, which
5
+ * is the first argument.
6
+ */
7
+ export type FontdirOptions = {
8
+ /**
9
+ * The accepted font types. Defaults to every web font format:
10
+ * `"font/woff2,font/woff,font/ttf,font/otf"`. Narrow it to `"font/woff2"` to
11
+ * keep the site's fonts to the one format every current browser reads and
12
+ * that is smallest on the wire.
13
+ */
14
+ accept?: string;
15
+ };
16
+ /**
17
+ * Options for the deprecated `s.fontset(options)`, which took the directory
18
+ * as `dir`.
19
+ *
20
+ * @deprecated Use `s.fontdir(dir, options)` and `FontdirOptions`.
21
+ */
22
+ export type FontsetOptions = FontdirOptions & {
23
+ /**
24
+ * The directory where fonts should be stored.
25
+ * Must start with "/public" (e.g., "/public/val/fonts")
26
+ */
27
+ dir: MediaDir;
28
+ };
29
+ /**
30
+ * Define a directory of fonts.
31
+ *
32
+ * A fontdir IS a filedir — the same source, the same `s.font(fontsVal)` field
33
+ * to pick from it, the same checks and fixes — with `accept` defaulting to the
34
+ * web font formats. What a font gets that any other file does not is decided
35
+ * by its mime type rather than by the directory: the Studio previews any file
36
+ * it knows is a font, wherever it is shown.
37
+ *
38
+ * Remote is off by default: call `.remote()` on the result to allow remote files.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const schema = s.fontdir("/public/val/fonts");
43
+ * export default c.define("/content/fonts.val.ts", schema, {
44
+ * "/public/val/fonts/inter_a1b2c.woff2": {
45
+ * mimeType: "font/woff2",
46
+ * },
47
+ * });
48
+ * ```
49
+ */
50
+ export declare const fontdir: (dir: MediaDir, options?: FontdirOptions) => ReturnType<typeof filedir>;
51
+ /**
52
+ * Define a collection of fonts.
53
+ *
54
+ * @deprecated Use `s.fontdir(dir, options)`: the directory moved out of the
55
+ * options and into the first argument, and nothing else changed.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // was: s.fontset({ dir: "/public/val/fonts" })
60
+ * const schema = s.fontdir("/public/val/fonts");
61
+ * ```
62
+ */
63
+ export declare const fontset: (options: FontsetOptions) => ReturnType<typeof filedir>;
@@ -5,7 +5,7 @@ import type { SerializedSchema } from "./index.js";
5
5
  * The one answer to that question. There used to be two — `hasRemoteFileSchema`
6
6
  * in the server, gating whether `/save` demands remote credentials, and
7
7
  * `findRequiredRemoteFiles` in the Studio, gating the `/remote/settings` fetch —
8
- * and they disagreed about `s.imageset({ ... }).remote()`: the Studio's version
8
+ * and they disagreed about `s.imagedir(dir, { ... }).remote()`: the Studio's version
9
9
  * counted a remote media record, the server's did not, because a media
10
10
  * collection serializes as a `record` of metadata rather than as an image
11
11
  * schema.
@@ -43,8 +43,8 @@ export declare function hasRemoteFileSchema(schema: SerializedSchema): boolean;
43
43
  *
44
44
  * It is a walk over the SERIALIZED schema for the reasons given above, and for
45
45
  * one more that is specific to this question: the two cases most likely to be
46
- * missed are invisible to a value type. A media collection (`s.imageset()` /
47
- * `s.fileset()`) serializes as a record of metadata with no image schema inside
46
+ * missed are invisible to a value type. A media collection (`s.imagedir()` /
47
+ * `s.filedir()`) serializes as a record of metadata with no image schema inside
48
48
  * it, and a richtext inline image lives in a constructor argument. A check
49
49
  * written against the item TYPE would silently pass both — and the gallery case
50
50
  * already has a data-loss incident attached to it (see above).