@valbuild/core 0.126.0 → 0.128.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 +296 -0
- package/dist/declarations/src/index.d.ts +5 -1
- package/dist/declarations/src/initSchema.d.ts +31 -15
- package/dist/declarations/src/locale.d.ts +55 -0
- package/dist/declarations/src/schema/array.d.ts +4 -0
- package/dist/declarations/src/schema/declaredKeys.d.ts +37 -0
- package/dist/declarations/src/schema/discriminatedUnion.d.ts +4 -0
- package/dist/declarations/src/schema/discriminatedUnionBranch.d.ts +20 -0
- package/dist/declarations/src/schema/enum.d.ts +9 -0
- package/dist/declarations/src/schema/file.d.ts +3 -3
- package/dist/declarations/src/schema/{files.d.ts → fileset.d.ts} +10 -10
- package/dist/declarations/src/schema/hasRemoteFileSchema.d.ts +3 -3
- package/dist/declarations/src/schema/image.d.ts +9 -5
- package/dist/declarations/src/schema/{images.d.ts → imageset.d.ts} +54 -21
- package/dist/declarations/src/schema/index.d.ts +116 -3
- package/dist/declarations/src/schema/literal.d.ts +2 -0
- package/dist/declarations/src/schema/locale.d.ts +132 -0
- package/dist/declarations/src/schema/object.d.ts +7 -0
- package/dist/declarations/src/schema/record.d.ts +57 -7
- package/dist/declarations/src/schema/settings.d.ts +54 -1
- package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
- package/dist/declarations/src/source/settings.d.ts +51 -0
- package/dist/{index-6db78f3c.cjs.prod.js → index-053ccde2.cjs.prod.js} +1114 -91
- package/dist/{index-b41b638f.cjs.dev.js → index-c3ae637d.cjs.dev.js} +1114 -91
- package/dist/{index-cd25c2dc.esm.js → index-db41de3c.esm.js} +1109 -92
- package/dist/valbuild-core.cjs.dev.js +7 -1
- package/dist/valbuild-core.cjs.prod.js +7 -1
- package/dist/valbuild-core.esm.js +1 -1
- package/package.json +1 -1
- package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
- package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
- package/patch/dist/valbuild-core-patch.esm.js +2 -2
|
@@ -11,7 +11,7 @@ import type { ImageEncodeOption } from "./image.js";
|
|
|
11
11
|
type MediaOptions = {
|
|
12
12
|
type: "files" | "images";
|
|
13
13
|
accept: string;
|
|
14
|
-
|
|
14
|
+
dir: string;
|
|
15
15
|
remote: boolean;
|
|
16
16
|
altSchema?: Schema<SelectorSource>;
|
|
17
17
|
/** Images only: how uploads are re-encoded in the browser. See `image.ts`. */
|
|
@@ -34,7 +34,7 @@ export type SerializedRecordSchema = {
|
|
|
34
34
|
customValidate?: boolean;
|
|
35
35
|
mediaType?: "files" | "images";
|
|
36
36
|
accept?: string;
|
|
37
|
-
|
|
37
|
+
dir?: string;
|
|
38
38
|
remote?: boolean;
|
|
39
39
|
encode?: ImageEncodeOption;
|
|
40
40
|
alt?: SerializedSchema;
|
|
@@ -152,6 +152,37 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
152
152
|
*/
|
|
153
153
|
validate(validationFunction: (src: Src) => false | string): RecordSchema<T, K, Src>;
|
|
154
154
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
155
|
+
/**
|
|
156
|
+
* The check that a record with a DECLARED key set holds every one of them.
|
|
157
|
+
*
|
|
158
|
+
* `s.record(s.locale(), item)` has one entry per language and
|
|
159
|
+
* `s.record(s.union(s.literal("a"), s.literal("b")), item)` has both — the
|
|
160
|
+
* keys are part of the schema either way, so a missing one is a hole in the
|
|
161
|
+
* content rather than content nobody has written yet. TypeScript already
|
|
162
|
+
* demanded both keys of the second; this is the validator catching up, which
|
|
163
|
+
* is why the two now behave the same.
|
|
164
|
+
*
|
|
165
|
+
* Deferred to `resolveSchemaSourceFixes` even for the literal case, where the
|
|
166
|
+
* keys are known right here: a locale record's keys are in another file, and
|
|
167
|
+
* one code path for both is one message, one fix and one thing to be wrong.
|
|
168
|
+
*/
|
|
169
|
+
private getDeclaredKeysValidation;
|
|
170
|
+
/**
|
|
171
|
+
* The key set this record's schema declares, computed once.
|
|
172
|
+
*
|
|
173
|
+
* `declaredKeySetOf` reads a SERIALIZED schema, and serializing the key
|
|
174
|
+
* schema walks it — twice per validate, and once per record instance, for an
|
|
175
|
+
* answer that is a property of the schema and cannot move between them. A
|
|
176
|
+
* schema is built once and validated against many times, so the walk happens
|
|
177
|
+
* on the first question and not on any after it.
|
|
178
|
+
*/
|
|
179
|
+
private declaredKeys;
|
|
180
|
+
private declaredKeySet;
|
|
181
|
+
protected opensLocaleScope(): "field" | "key" | null;
|
|
182
|
+
protected localeScopeChildren(): {
|
|
183
|
+
key: string;
|
|
184
|
+
schema: Schema<SelectorSource>;
|
|
185
|
+
}[];
|
|
155
186
|
private isRemoteUrl;
|
|
156
187
|
/** Marks the validation errors reported at `path` as key errors (in place). */
|
|
157
188
|
private markKeyErrorsAtPath;
|
|
@@ -198,7 +229,7 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
198
229
|
* Allow the files in this gallery to be stored on Val's remote content host
|
|
199
230
|
* instead of in your repository.
|
|
200
231
|
*
|
|
201
|
-
* For `s.
|
|
232
|
+
* For `s.imageset()` and `s.fileset()`, which are records of media. Remote is off
|
|
202
233
|
* until this is called.
|
|
203
234
|
*
|
|
204
235
|
* Each entry is then keyed by its URL on the content host rather than by a
|
|
@@ -207,7 +238,7 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
207
238
|
* upload it and rewrite the key.
|
|
208
239
|
*
|
|
209
240
|
* @example
|
|
210
|
-
* const schema = s.
|
|
241
|
+
* const schema = s.imageset({ dir: "/public/val/images" }).remote();
|
|
211
242
|
* export default c.define("/content/images.val.ts", schema, {
|
|
212
243
|
* "https://remote.val.build/file/p/my-project/b/01/v/1.0.0/h/8f2a1c/f/3b9d70/p/public/val/images/hero.webp":
|
|
213
244
|
* {
|
|
@@ -226,7 +257,7 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
226
257
|
* which lets the runtime, the Studio and validation work one entry at a time
|
|
227
258
|
* so a record/router can scale to many thousands of entries.
|
|
228
259
|
*
|
|
229
|
-
* Not supported on image/file galleries (`s.
|
|
260
|
+
* Not supported on image/file galleries (`s.imageset()` / `s.fileset()`).
|
|
230
261
|
*
|
|
231
262
|
* Only supported on a module's ROOT record/router — a `.jsonValues()` record
|
|
232
263
|
* nested inside an object/array/record is rejected at startup with a module
|
|
@@ -253,7 +284,7 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
253
284
|
* bucket — instead of in the module.
|
|
254
285
|
*
|
|
255
286
|
* Available on every record-derived schema, which is `s.record()`,
|
|
256
|
-
* `s.router()`, `s.
|
|
287
|
+
* `s.router()`, `s.imageset()` and `s.fileset()` alike: a router is a
|
|
257
288
|
* `RecordSchema` with a `ValRouter`, a gallery one with media options, so all
|
|
258
289
|
* four get external storage from this one method.
|
|
259
290
|
*
|
|
@@ -344,6 +375,25 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
344
375
|
*/
|
|
345
376
|
render(input: FieldRender): RecordSchema<T, K, Src>;
|
|
346
377
|
}
|
|
347
|
-
|
|
378
|
+
/**
|
|
379
|
+
* The source of a record, given what its key schema says about its keys.
|
|
380
|
+
*
|
|
381
|
+
* A key schema that ENUMERATES its keys (a union of literals, or `s.locale()`)
|
|
382
|
+
* makes every one of them an entry of the record, and an entry nobody has
|
|
383
|
+
* written yet is `null` rather than absent — so the value type widens by
|
|
384
|
+
* `null`. That is the whole of option F at the type level: a half-translated
|
|
385
|
+
* record is a record with nulls in it, which is data you can count, filter and
|
|
386
|
+
* see in a diff, rather than keys that are simply not there.
|
|
387
|
+
*
|
|
388
|
+
* For a union of literals TypeScript already required every key, since
|
|
389
|
+
* `SelectorOfSchema<K>` is the literal union itself. `s.locale()` is a flavoured
|
|
390
|
+
* string, so it requires nothing here and the requirement is the validator's —
|
|
391
|
+
* deliberately: which languages exist is in the settings module, where the
|
|
392
|
+
* project can change it without a deploy.
|
|
393
|
+
*/
|
|
394
|
+
export type RecordSrcOf<K extends Schema<string>, S extends Schema<SelectorSource>> = K extends {
|
|
395
|
+
__declaresRecordKeys: true;
|
|
396
|
+
} ? Record<SelectorOfSchema<K>, SelectorOfSchema<S> | null> : Record<SelectorOfSchema<K>, SelectorOfSchema<S>>;
|
|
397
|
+
export declare function record<K extends Schema<string>, S extends Schema<SelectorSource>>(key: K, schema: S): RecordSchema<S, K, RecordSrcOf<K, S>>;
|
|
348
398
|
export declare function record<S extends Schema<SelectorSource>>(schema: S): RecordSchema<S, Schema<string>, Record<string, SelectorOfSchema<S>>>;
|
|
349
399
|
export {};
|
|
@@ -38,6 +38,19 @@ export type SerializedSettingsSchema = {
|
|
|
38
38
|
readonly?: boolean;
|
|
39
39
|
hidden?: boolean;
|
|
40
40
|
description?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Which of Val's sections this is, where it is one.
|
|
43
|
+
*
|
|
44
|
+
* The name rather than the rule itself, because the rule is a closure and a
|
|
45
|
+
* closure does not survive JSON. Val owns both ends — every section's rules
|
|
46
|
+
* are in {@link sectionValidators} in this file — so the name is enough for
|
|
47
|
+
* {@link deserializeSchema} to put the same rules back, and a Studio that
|
|
48
|
+
* does not recognise the name simply runs none (see `sectionValidate`).
|
|
49
|
+
*
|
|
50
|
+
* Absent on the settings module itself, which is a bag of sections and has no
|
|
51
|
+
* rules of its own.
|
|
52
|
+
*/
|
|
53
|
+
section?: string;
|
|
41
54
|
};
|
|
42
55
|
/**
|
|
43
56
|
* An object whose keys are all optional: absent means unset.
|
|
@@ -58,7 +71,47 @@ export declare class SettingsSchema<Src extends {
|
|
|
58
71
|
private readonly isReadonly;
|
|
59
72
|
private readonly isHidden;
|
|
60
73
|
private readonly description?;
|
|
61
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Which section this is, where Val has rules about it beyond its keys.
|
|
76
|
+
*
|
|
77
|
+
* Not the `validate` the class deliberately does not offer: that one is the
|
|
78
|
+
* schema author's, and `s.settings()` takes no arguments to declare it
|
|
79
|
+
* with. This names a rule Val states about a shape Val owns — no language
|
|
80
|
+
* declared twice in `locales.available` is true of every project, and
|
|
81
|
+
* there is nowhere else it can be said. A single value's rules stay on its
|
|
82
|
+
* own schema; this is for the ones that need to see more than one value,
|
|
83
|
+
* or to report on a row of a list.
|
|
84
|
+
*
|
|
85
|
+
* A NAME rather than the function, so it survives serialization: the schema
|
|
86
|
+
* reaches the Studio's validation worker as JSON, and a closure would be
|
|
87
|
+
* dropped on the way — which is how these rules once went missing there.
|
|
88
|
+
*/
|
|
89
|
+
private readonly section?;
|
|
90
|
+
constructor(items: Record<string, Schema<SelectorSource>>, opt?: boolean, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined,
|
|
91
|
+
/**
|
|
92
|
+
* Which section this is, where Val has rules about it beyond its keys.
|
|
93
|
+
*
|
|
94
|
+
* Not the `validate` the class deliberately does not offer: that one is the
|
|
95
|
+
* schema author's, and `s.settings()` takes no arguments to declare it
|
|
96
|
+
* with. This names a rule Val states about a shape Val owns — no language
|
|
97
|
+
* declared twice in `locales.available` is true of every project, and
|
|
98
|
+
* there is nowhere else it can be said. A single value's rules stay on its
|
|
99
|
+
* own schema; this is for the ones that need to see more than one value,
|
|
100
|
+
* or to report on a row of a list.
|
|
101
|
+
*
|
|
102
|
+
* A NAME rather than the function, so it survives serialization: the schema
|
|
103
|
+
* reaches the Studio's validation worker as JSON, and a closure would be
|
|
104
|
+
* dropped on the way — which is how these rules once went missing there.
|
|
105
|
+
*/
|
|
106
|
+
section?: string | undefined);
|
|
107
|
+
/**
|
|
108
|
+
* This section's rules, or `undefined` where there are none to run.
|
|
109
|
+
*
|
|
110
|
+
* A name this Val does not know means a schema written by a newer one. Its
|
|
111
|
+
* per-field validation still runs; only the section's own rule is missed,
|
|
112
|
+
* which is the mild half of the failure and better than refusing the schema.
|
|
113
|
+
*/
|
|
114
|
+
private sectionValidate;
|
|
62
115
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
63
116
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
64
117
|
nullable(): SettingsSchema<Src | null>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const ValidationFix: readonly ["image:add-metadata", "image:check-metadata", "image:upload-remote", "image:download-remote", "image:check-remote", "images:check-remote", "images:upload-remote", "file:add-metadata", "file:check-metadata", "file:upload-remote", "file:download-remote", "file:check-remote", "files:check-remote", "files:upload-remote", "keyof:check-keys", "router:check-route", "images:check-unique-folder", "files:check-unique-folder", "images:check-all-files", "files:check-all-files", "jsonValues:extract-entry", "external:upload"];
|
|
1
|
+
export declare const ValidationFix: readonly ["image:add-metadata", "image:check-metadata", "image:upload-remote", "image:download-remote", "image:check-remote", "images:check-remote", "images:upload-remote", "file:add-metadata", "file:check-metadata", "file:upload-remote", "file:download-remote", "file:check-remote", "files:check-remote", "files:upload-remote", "keyof:check-keys", "router:check-route", "locale:check-locale", "images:check-unique-folder", "files:check-unique-folder", "images:check-all-files", "files:check-all-files", "jsonValues:extract-entry", "record:fill-keys", "external:upload"];
|
|
2
2
|
export type ValidationFix = (typeof ValidationFix)[number];
|
|
@@ -22,6 +22,41 @@
|
|
|
22
22
|
*/
|
|
23
23
|
export type SettingsSource = {
|
|
24
24
|
assistant?: AssistantSettingsSource;
|
|
25
|
+
locales?: LocalesSettingsSource;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The languages this project publishes.
|
|
29
|
+
*
|
|
30
|
+
* Content rather than configuration, and deliberately: which languages a site
|
|
31
|
+
* has is a decision the people who write it make, and under a build-time
|
|
32
|
+
* constant it took a developer and a deploy. It is the same move `assistant`
|
|
33
|
+
* makes with `enabled`.
|
|
34
|
+
*
|
|
35
|
+
* Val ships no list of its own. A project with no `locales` section has not
|
|
36
|
+
* said it is translated, and nothing about locales appears anywhere — no picker
|
|
37
|
+
* in the Studio, no checks, nothing.
|
|
38
|
+
*
|
|
39
|
+
* **This list is a decision with a blast radius.** Every locale in content is
|
|
40
|
+
* checked against it, so removing one invalidates the content that uses it, and
|
|
41
|
+
* adding one leaves every locale-keyed record short of a language until it is
|
|
42
|
+
* filled in. That is the intended behaviour — a language that is declared and
|
|
43
|
+
* missing everywhere is worth being told about — but it is why the Studio warns
|
|
44
|
+
* before saving a removal rather than treating this as an ordinary field.
|
|
45
|
+
*
|
|
46
|
+
* There is no default language, deliberately. Every locale-specific field asks
|
|
47
|
+
* which language it is in, and a default is exactly the answer that lets that
|
|
48
|
+
* question go unanswered — content ends up filed under a language nobody chose,
|
|
49
|
+
* which is the state translation is meant to make visible.
|
|
50
|
+
*/
|
|
51
|
+
export type LocalesSettingsSource = {
|
|
52
|
+
/**
|
|
53
|
+
* The languages, as canonical BCP 47 tags: `en-US`, `nb-NO`.
|
|
54
|
+
*
|
|
55
|
+
* Order is the project's own, and it is kept: it decides the order of the
|
|
56
|
+
* Studio's picker and of the rows in a locale-keyed record, so a project can
|
|
57
|
+
* put the language it works in first.
|
|
58
|
+
*/
|
|
59
|
+
available?: string[] | null;
|
|
25
60
|
};
|
|
26
61
|
/**
|
|
27
62
|
* The assistant: whether editors have one, and what it is told about the
|
|
@@ -55,6 +90,22 @@ export type AssistantSettingsSource = {
|
|
|
55
90
|
* `glossary`) rather than a field that looks like it should absorb them.
|
|
56
91
|
*/
|
|
57
92
|
tone?: string | null;
|
|
93
|
+
/**
|
|
94
|
+
* How to translate into each language, keyed by language.
|
|
95
|
+
*
|
|
96
|
+
* Per language rather than one field, because translation rules are per
|
|
97
|
+
* language: bokmål or nynorsk, `du` or `De`, which product names stay in
|
|
98
|
+
* English. Only the target language's note is sent, so a French rule does not
|
|
99
|
+
* ride along in a Norwegian request.
|
|
100
|
+
*
|
|
101
|
+
* Keyed by language, so it holds every one of them once it exists at all —
|
|
102
|
+
* `null` where a language needs no special instruction, which is most of them.
|
|
103
|
+
* That is the same rule every locale-keyed record follows (see
|
|
104
|
+
* `declaredKeys.ts`): a gap you can count and see in a diff, rather than a key
|
|
105
|
+
* that is simply not there. A project with no notes at all leaves the whole
|
|
106
|
+
* field unset and nothing is required.
|
|
107
|
+
*/
|
|
108
|
+
translation?: Record<string, string | null> | null;
|
|
58
109
|
};
|
|
59
110
|
/**
|
|
60
111
|
* The cap on each of the assistant's prose fields, in characters.
|