@valbuild/core 0.110.0 → 0.117.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,56 @@
1
+ # @valbuild/core
2
+
3
+ ## 0.117.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#581](https://github.com/valbuild/val/pull/581) [`d94a40f`](https://github.com/valbuild/val/commit/d94a40f8bd11027636d183e293aced820b6f341f) Thanks [@freekh](https://github.com/freekh)! - Images no longer show a validation warning in the editor when nothing is wrong
8
+ with them.
9
+
10
+ Every `s.image()` carrying width, height or a mime type used to be marked in VS
11
+ Code with "Found image metadata, but it could not be validated", whether or not
12
+ the metadata was correct — so the warning sat on every image in the project and
13
+ never went away, not even after applying its own quick fix.
14
+
15
+ That message was never a finding. `@valbuild/core` cannot read files, so it
16
+ cannot answer whether stored dimensions match the image, and it hands the
17
+ question on as an `image:check-metadata` fix instead. `val validate` has always
18
+ resolved that by reading the file and comparing; the language server now does
19
+ the same, and reports only what actually disagrees:
20
+
21
+ ```
22
+ Image width is incorrect! Found: 800. Expected: 944
23
+ ```
24
+
25
+ An image whose metadata is right gets nothing. A missing `width`, `height` or
26
+ `mimeType` is reported, as is a stale one, with the quick fix still offered. A
27
+ file that is not on disk is still reported as a missing file rather than as a
28
+ metadata problem.
29
+
30
+ The four messages involved say what they mean now, in the editor and in
31
+ `val validate` alike:
32
+
33
+ - "Image metadata has not been checked against the file." (was "Found image
34
+ metadata, but it could not be validated. An image must have a width (positive
35
+ number), a height (positive number) and a mime type." — which described a
36
+ check that never ran)
37
+ - "Image metadata is missing: width, height and mimeType." (was "Could not
38
+ validate Image metadata.")
39
+ - "File mimeType has not been checked against the file." (was "Found mimeType,
40
+ but it could not be validated.")
41
+ - "File metadata is missing: mimeType." (was "Missing File mimeType.")
42
+
43
+ Also fixes an `s.file()` whose `mimeType` is missing: it reported "Mime type and
44
+ file extension not matching. Mime type is 'undefined'" with no fix attached, so
45
+ no quick fix was offered and the `file:add-metadata` case was unreachable. It
46
+ now reports the missing mime type and offers to add it.
47
+
48
+ - [#579](https://github.com/valbuild/val/pull/579) [`b2812ae`](https://github.com/valbuild/val/commit/b2812ae4ee03e005ecead3365f49c625e536f94d) Thanks [@freekh](https://github.com/freekh)! - Every release now ships a changelog. Each package's `CHANGELOG.md` records what
49
+ changed under the version that shipped it — with a link to the pull request, the
50
+ commit and the author — and the same entry becomes the body of the GitHub
51
+ Release for the tag. The file is included in the npm tarball, so it is also
52
+ readable from an installed copy.
53
+
54
+ Up to now those changelogs were generated empty, and the GitHub Releases with
55
+ them, so there was no record of what any given version contained. Releases from
56
+ this one on have one; earlier versions stay blank.
@@ -31,7 +31,7 @@ import { ModulePath, SourcePath, getValPath, isVal } from "./val/index.js";
31
31
  import { createValPathOfItem } from "./selector/SelectorProxy.js";
32
32
  import { Operation } from "./patch/index.js";
33
33
  import { initSchema } from "./initSchema.js";
34
- import { getMimeType, mimeTypeToFileExt, filenameToMimeType } from "./mimeType/index.js";
34
+ import { getMimeType, mimeTypeToFileExt, filenameToMimeType, mimeTypeMatchesAccept } from "./mimeType/index.js";
35
35
  import { type ImageMetadata } from "./schema/image.js";
36
36
  import { type FileMetadata } from "./schema/file.js";
37
37
  import { isJson, getJsonImport, resolveJsonValues } from "./source/json.js";
@@ -51,6 +51,7 @@ export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
51
51
  export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
52
52
  export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
53
53
  export { type SerializedColorSchema, type ColorOptions, ColorSchema, } from "./schema/color.js";
54
+ export { type SerializedCodeSchema, type CodeOptions, type CodeLanguage, CodeSchema, CODE_LANGUAGES, } from "./schema/code.js";
54
55
  export { type ColorFormat, type ParsedColor, COLOR_FORMATS, DEFAULT_COLOR_FORMAT, } from "./schema/colorFormat.js";
55
56
  export { type SerializedDateTimeSchema, DateTimeSchema, } from "./schema/datetime.js";
56
57
  export { type SerializedKeyOfSchema, KeyOfSchema } from "./schema/keyOf.js";
@@ -60,7 +61,7 @@ export { type SerializedUnionSchema, UnionSchema, type SerializedStringUnionSche
60
61
  export { type SerializedLiteralSchema, LiteralSchema } from "./schema/literal.js";
61
62
  export { deserializeSchema } from "./schema/deserialize.js";
62
63
  export { type PreviewItem, type ItemPreviewInput, type RecordPreview, type ArrayPreview, type ReifiedPreview, type PreviewScope, previewScope, } from "./preview.js";
63
- export { type CodeLanguage, type StringRender, type InlineRender, type FieldRender, CODE_LANGUAGES, isInlineRender, } from "./render.js";
64
+ export { type InlineRender, type FieldRender, isInlineRender } from "./render.js";
64
65
  export type { ValRouter, RouteValidationError } from "./router.js";
65
66
  export { getSourcePathFromRoute } from "./getSourcePathFromRoute.js";
66
67
  export declare const FATAL_ERROR_TYPES: readonly ["no-schema", "no-source", "invalid-id", "no-module", "invalid-patch"];
@@ -113,6 +114,7 @@ declare const Internal: {
113
114
  getMimeType: typeof getMimeType;
114
115
  mimeTypeToFileExt: typeof mimeTypeToFileExt;
115
116
  filenameToMimeType: typeof filenameToMimeType;
117
+ mimeTypeMatchesAccept: typeof mimeTypeMatchesAccept;
116
118
  EXT_TO_MIME_TYPES: Record<string, string>;
117
119
  MIME_TYPES_TO_EXT: {
118
120
  [k: string]: string;
@@ -13,6 +13,7 @@ import { file } from "./schema/file.js";
13
13
  import { files } from "./schema/files.js";
14
14
  import { date } from "./schema/date.js";
15
15
  import { datetime } from "./schema/datetime.js";
16
+ import { code } from "./schema/code.js";
16
17
  import { color } from "./schema/color.js";
17
18
  import { route } from "./schema/route.js";
18
19
  import { router } from "./schema/router.js";
@@ -206,6 +207,23 @@ export type InitSchema = {
206
207
  *
207
208
  */
208
209
  readonly color: typeof color;
210
+ /**
211
+ * Define a string edited in a code editor.
212
+ *
213
+ * Pass a `language` to syntax highlight it; leave it out for a plain
214
+ * monospaced editor. The value is a plain string, and — unlike `s.string()` —
215
+ * it is never stega encoded, so the code reaches your app as it was written.
216
+ *
217
+ * @example
218
+ * const schema = s.code({ language: "typescript" });
219
+ * export default c.define("/example.val.ts", schema, "const a = 1;");
220
+ *
221
+ * @example
222
+ * const schema = s.code();
223
+ * export default c.define("/example.val.ts", schema, "no highlighting here");
224
+ *
225
+ */
226
+ readonly code: typeof code;
209
227
  /**
210
228
  * Define a string that references a route path in your application.
211
229
  *
@@ -317,6 +335,7 @@ export declare function initSchema(): {
317
335
  date: (options?: Record<string, never>) => import("./index.js").DateSchema<import("./index.js").RawString>;
318
336
  datetime: (options?: Record<string, never>) => import("./index.js").DateTimeSchema<import("./index.js").RawString>;
319
337
  color: (options?: import("./index.js").ColorOptions) => import("./index.js").ColorSchema<import("./index.js").RawString>;
338
+ code: (options?: import("./index.js").CodeOptions) => import("./index.js").CodeSchema<import("./index.js").RawString>;
320
339
  route: <T extends string>(options?: Record<string, never>) => import("./index.js").RouteSchema<T>;
321
340
  router: typeof router;
322
341
  images: <Accept extends `image/${string}`>(options: import("./schema/images.js").ImagesOptions<Accept>) => import("./index.js").RecordSchema<import("./index.js").ObjectSchema<{
@@ -1,3 +1,17 @@
1
1
  export declare function getMimeType(base64Url: string): string | undefined;
2
2
  export declare function mimeTypeToFileExt(mimeType: string): string;
3
3
  export declare function filenameToMimeType(filename: string): string | undefined;
4
+ /**
5
+ * Whether `mimeType` satisfies an `accept` string.
6
+ *
7
+ * `accept` is the HTML file-input syntax: a comma-separated list of exact mime
8
+ * types (`image/png`), type wildcards (`image` followed by `/` and a star) or
9
+ * the catch-all star-slash-star.
10
+ *
11
+ * This lived in three places - `ImageSchema`, `FileSchema` and the media
12
+ * branch of `RecordSchema` - which is how the third copy grew a redundant
13
+ * `image` wildcard case that the general wildcard branch already covered.
14
+ * Anything that has to decide whether an `accept` is satisfied, validation and
15
+ * editor tooling alike, should call this.
16
+ */
17
+ export declare function mimeTypeMatchesAccept(mimeType: string, accept: string): boolean;
@@ -1,10 +1,14 @@
1
1
  import type { SerializedSchema } from "./schema/index.js";
2
2
  /**
3
3
  * A RENDER is how the FIELD ITSELF is laid out in the editor, and it applies
4
- * only when you are looking at the field: `s.string().render({ as: "textarea"
5
- * })`, `.render({ as: "code", language })`, `.render({ as: "inline" })` on an
4
+ * only when you are looking at the field: `.render({ as: "inline" })` on an
6
5
  * array/record item.
7
6
  *
7
+ * It is deliberately the ONE thing a render can say. What a string looks like
8
+ * when it holds more than a line is the schema's own business — `.multiline()`
9
+ * for a text box, `s.code({ language })` for a code editor — not a layout
10
+ * bolted onto `s.string()` from outside.
11
+ *
8
12
  * A PREVIEW (`preview.ts`) is the other thing: how the VALUE is shown wherever
9
13
  * a preview of it is needed — a list row, a reference dropdown, a search hit —
10
14
  * that is, wherever the value is navigable to rather than open. The two never
@@ -25,13 +29,6 @@ import type { SerializedSchema } from "./schema/index.js";
25
29
  * `render` its own execute/store pair again. It is NOT to re-merge the two:
26
30
  * conflating them is what this file was split up to undo.
27
31
  */
28
- /**
29
- * The list is the declaration and {@link CodeLanguage} is derived from it, so
30
- * that a validator elsewhere (`shared`'s zod schema) can enumerate the same
31
- * languages without a second copy that drifts. Same shape as `COLOR_FORMATS`.
32
- */
33
- export declare const CODE_LANGUAGES: readonly ["typescript", "javascript", "javascriptreact", "typescriptreact", "json", "java", "html", "css", "xml", "markdown", "sql", "python", "rust", "php", "go", "cpp", "sass", "vue", "angular"];
34
- export type CodeLanguage = (typeof CODE_LANGUAGES)[number];
35
32
  /**
36
33
  * `{ as: "inline" }` on a field that is the ITEM of an array or record: the
37
34
  * container renders the field itself inside each (sortable) row, instead of a
@@ -49,21 +46,10 @@ export type InlineRender = {
49
46
  as: "inline";
50
47
  };
51
48
  /**
52
- * What `.render(...)` takes on every field except `s.string()`, and what the
53
- * serialized schema carries verbatim.
49
+ * What `.render(...)` takes on every field, and what the serialized schema
50
+ * carries verbatim.
54
51
  */
55
52
  export type FieldRender = InlineRender;
56
- /**
57
- * What `s.string().render(...)` takes, and what the serialized schema carries
58
- * verbatim. A string has editor layouts of its own (textarea, code) in
59
- * addition to the container-facing `inline`.
60
- */
61
- export type StringRender = {
62
- as: "textarea";
63
- } | {
64
- as: "code";
65
- language: CodeLanguage;
66
- } | InlineRender;
67
53
  /**
68
54
  * Is this item schema edited INSIDE its list row, rather than behind a
69
55
  * clickable preview row that navigates to it?
@@ -0,0 +1,85 @@
1
+ import { CustomValidateFunction, Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
+ import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
3
+ import { FieldRender } from "../render.js";
4
+ import { SourcePath } from "../val/index.js";
5
+ import { RawString } from "./string.js";
6
+ import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
7
+ /**
8
+ * The list is the declaration and {@link CodeLanguage} is derived from it, so
9
+ * that a validator elsewhere (`shared`'s zod schema) can enumerate the same
10
+ * languages without a second copy that drifts. Same shape as `COLOR_FORMATS`.
11
+ */
12
+ export declare const CODE_LANGUAGES: readonly ["typescript", "javascript", "javascriptreact", "typescriptreact", "json", "java", "html", "css", "xml", "markdown", "sql", "python", "rust", "php", "go", "cpp", "sass", "vue", "angular"];
13
+ export type CodeLanguage = (typeof CODE_LANGUAGES)[number];
14
+ export type CodeOptions = {
15
+ /**
16
+ * The language to syntax highlight the editor with.
17
+ *
18
+ * Omit it for a plain monospaced editor with no highlighting.
19
+ *
20
+ * @example
21
+ * "typescript"
22
+ * "json"
23
+ * "markdown"
24
+ */
25
+ language?: CodeLanguage;
26
+ };
27
+ export type SerializedCodeSchema = {
28
+ type: "code";
29
+ /** Static layout config, carried whole in the serialized schema — see `render.ts`. */
30
+ render?: FieldRender;
31
+ /** Set when this schema declares a `preview`. The closure itself cannot serialize. */
32
+ preview?: true;
33
+ options?: CodeOptions;
34
+ opt: boolean;
35
+ customValidate?: boolean;
36
+ readonly?: boolean;
37
+ hidden?: boolean;
38
+ description?: string;
39
+ };
40
+ /**
41
+ * A string edited in a code editor.
42
+ *
43
+ * Its own schema type rather than a layout on `s.string()`: the language is
44
+ * part of what the content IS, not of how one field happens to be drawn, and
45
+ * being a type is what lets the value stay out of stega encoding — invisible
46
+ * characters woven into source code are not something a consumer can render.
47
+ */
48
+ export declare class CodeSchema<Src extends string | null> extends Schema<Src> {
49
+ private readonly options?;
50
+ private readonly opt;
51
+ private readonly customValidateFunctions;
52
+ private readonly isReadonly;
53
+ private readonly isHidden;
54
+ private readonly description?;
55
+ private readonly renderInput;
56
+ private readonly previewInput;
57
+ constructor(options?: CodeOptions | undefined, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null, previewInput?: ItemPreviewInput<Src> | null);
58
+ describe(description: string | null): CodeSchema<Src>;
59
+ validate(validationFunction: (src: Src) => false | string): CodeSchema<Src>;
60
+ protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
61
+ protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
62
+ protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
63
+ nullable(): CodeSchema<Src | null>;
64
+ readonly(): CodeSchema<Src>;
65
+ hidden(): CodeSchema<Src>;
66
+ /**
67
+ * How this field is laid out in the editor when it is the item of an array
68
+ * or record: `{ as: "inline" }` renders the field itself inside each row,
69
+ * instead of a preview row that navigates to it.
70
+ *
71
+ * Static configuration, not a callback — see `render.ts`.
72
+ */
73
+ render(input: FieldRender): CodeSchema<Src>;
74
+ /**
75
+ * How this VALUE is shown where a preview of it is needed — a row in a
76
+ * sortable list, a reference dropdown, a search hit. Never how the field
77
+ * itself is edited (that is `render`). See `preview.ts`.
78
+ */
79
+ preview(select: ItemPreviewInput<Src>): CodeSchema<Src>;
80
+ protected executePreviewItem(src: NonNullable<Src>): PreviewItem | null;
81
+ protected declaresItemPreview(): boolean;
82
+ protected executeSerialize(): SerializedSchema;
83
+ protected executePreview(): ReifiedPreview;
84
+ }
85
+ export declare const code: (options?: CodeOptions) => CodeSchema<RawString>;
@@ -12,6 +12,7 @@ import { SerializedRecordSchema } from "./record.js";
12
12
  import { SerializedRichTextSchema } from "./richtext.js";
13
13
  import { RawString, SerializedStringSchema } from "./string.js";
14
14
  import { SerializedUnionSchema } from "./union.js";
15
+ import { SerializedCodeSchema } from "./code.js";
15
16
  import { SerializedColorSchema } from "./color.js";
16
17
  import { SerializedDateSchema } from "./date.js";
17
18
  import { SerializedDateTimeSchema } from "./datetime.js";
@@ -20,7 +21,7 @@ import { ValidationError, ValidationErrors } from "./validation/ValidationError.
20
21
  import { FileSource } from "../source/media.js";
21
22
  import { GenericRichTextSourceNode, RichTextSource } from "../source/richtext.js";
22
23
  import { ReifiedPreview, PreviewScope, PreviewItem } from "../preview.js";
23
- export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedRouteSchema | SerializedImageSchema;
24
+ export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedImageSchema;
24
25
  type Primitives = number | string | boolean | null | FileSource;
25
26
  export type AssertError = {
26
27
  message: string;
@@ -1,6 +1,6 @@
1
1
  import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
2
  import { ItemPreviewInput, PreviewItem, ReifiedPreview } from "../preview.js";
3
- import { StringRender } from "../render.js";
3
+ import { FieldRender } from "../render.js";
4
4
  import { SourcePath } from "../val/index.js";
5
5
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
6
6
  type StringOptions = {
@@ -20,7 +20,17 @@ export type SerializedStringSchema = {
20
20
  * off the schema it already has. See `render.ts` for what that assumption
21
21
  * buys, and what to do if a render ever needs to stop being static.
22
22
  */
23
- render?: StringRender;
23
+ render?: FieldRender;
24
+ /**
25
+ * Set by `.multiline()`: the field is a growing text box rather than a
26
+ * single-line input.
27
+ *
28
+ * A property of the schema rather than a `render` variant, because it says
29
+ * what the string IS — text that may hold line breaks — and not merely how one
30
+ * field is drawn. It is read the same way a render is, straight off the
31
+ * serialized schema where the field is drawn.
32
+ */
33
+ multiline?: boolean;
24
34
  /** Set when this schema declares a `preview`. The closure itself cannot serialize. */
25
35
  preview?: true;
26
36
  options?: {
@@ -54,7 +64,8 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
54
64
  private readonly isHidden;
55
65
  private readonly description?;
56
66
  private readonly previewInput;
57
- constructor(options?: StringOptions | undefined, opt?: boolean, isRaw?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], renderInput?: StringRender | null, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null);
67
+ private readonly isMultiline;
68
+ constructor(options?: StringOptions | undefined, opt?: boolean, isRaw?: boolean, customValidateFunctions?: ((src: Src) => false | string)[], renderInput?: FieldRender | null, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null, isMultiline?: boolean);
58
69
  describe(description: string | null): StringSchema<Src>;
59
70
  /**
60
71
  * @deprecated Use `minLength` instead
@@ -77,13 +88,27 @@ export declare class StringSchema<Src extends string | null> extends Schema<Src>
77
88
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
78
89
  protected executeSerialize(): SerializedSchema;
79
90
  /**
80
- * How this field is laid out in the editor: a textarea, or a code editor for
81
- * the given language.
91
+ * This string holds text that may run to several lines: the editor gives it a
92
+ * growing text box instead of a single-line input.
93
+ *
94
+ * Nothing else changes — the value is still a plain string, and no validation
95
+ * is added or removed. For code, use `s.code({ language })` instead, which is
96
+ * its own schema type.
97
+ *
98
+ * @example
99
+ * const schema = s.string().multiline();
100
+ * export default c.define("/example.val.ts", schema, "Line one\nLine two");
101
+ */
102
+ multiline(): StringSchema<Src>;
103
+ /**
104
+ * How this field is laid out in the editor when it is the item of an array or
105
+ * record: `{ as: "inline" }` renders the field itself inside each row,
106
+ * instead of a preview row that navigates to it.
82
107
  *
83
108
  * Static configuration, not a callback — see `render.ts`. What a CONTAINER
84
109
  * shows for its items is a `preview`, which is a different thing entirely.
85
110
  */
86
- render(input: StringRender): StringSchema<Src>;
111
+ render(input: FieldRender): StringSchema<Src>;
87
112
  /**
88
113
  * How this VALUE is shown where a preview of it is needed — a row in a
89
114
  * sortable list, a reference dropdown, a search hit. Never how the field