@valbuild/core 0.146.2 → 0.148.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 +41 -0
- package/dist/declarations/src/getSourcePathFromRoute.d.ts +9 -0
- package/dist/declarations/src/index.d.ts +12 -11
- package/dist/declarations/src/initSchema.d.ts +19 -0
- package/dist/declarations/src/remote/fileHash.d.ts +1 -2
- package/dist/declarations/src/router.d.ts +42 -0
- package/dist/declarations/src/schema/enum.d.ts +117 -1
- package/dist/declarations/src/schema/index.d.ts +5 -0
- package/dist/declarations/src/schema/record.d.ts +59 -13
- package/dist/declarations/src/schema/router.d.ts +8 -0
- package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
- package/dist/{index-8519c554.esm.js → index-09bd1d7d.esm.js} +9364 -8733
- package/dist/{index-4bd4b62a.cjs.prod.js → index-ad3488f6.cjs.prod.js} +9365 -8732
- package/dist/{index-89993829.cjs.dev.js → index-b0fa804a.cjs.dev.js} +9365 -8732
- package/dist/valbuild-core.cjs.dev.js +3 -1
- package/dist/valbuild-core.cjs.prod.js +3 -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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @valbuild/core
|
|
2
2
|
|
|
3
|
+
## 0.148.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - Remote files served from a project's own site (addresses like
|
|
8
|
+
`https://www.acme.no/_val/file/…`) are now checked for whether that address
|
|
9
|
+
still serves the project. When a custom domain has been removed or moved to
|
|
10
|
+
another project, the image or file is reported, and **Fix** on the Studio's
|
|
11
|
+
Validate page — or `val validate --fix` — points it at the project's permanent
|
|
12
|
+
`<id>.valstart.dev` address. Nothing is uploaded or downloaded: only the address
|
|
13
|
+
changes.
|
|
14
|
+
|
|
15
|
+
Remote file addresses with a `/_val` prefix after the host are now recognised.
|
|
16
|
+
|
|
17
|
+
## 0.147.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- [#816](https://github.com/valbuild/val/pull/816) [`2f09cd5`](https://github.com/valbuild/val/commit/2f09cd5e2f4fc37f06917899f24f9ad2ee8d3a8b) Thanks [@freekh](https://github.com/freekh)! - Routers can give each route parameter a schema, and a URL can say which language a page is in.
|
|
22
|
+
|
|
23
|
+
- **`s.router(router, params, item)`** takes a schema per route parameter, by the name the route file gives it (`$slug`, `[slug]`, `{-$locale}`). Each key's parameters are validated against them, and a name the route does not have is a schema error.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
s.router(tanstackRouter, { locale: urlLocale, slug: s.string() }, item);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **`s.enum(...).locales({ ... })`** says which of the project's languages each value means, so `/nb/…` can mean `nb-NO`. The tags are checked against `locales.available` in the settings module, like `s.locale()`. On a nullable enum, a second argument names the language of a URL that leaves the segment out, which only a router parameter may use:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const urlLocale = s
|
|
33
|
+
.enum("nb")
|
|
34
|
+
.nullable()
|
|
35
|
+
.locales({ nb: "nb-NO" }, { null: "en-US" });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
A router with a locale parameter makes each page a locale scope, so the Studio and validation know which language every page is in.
|
|
39
|
+
|
|
40
|
+
- **A route's language segment can no longer be forgotten.** In a project that declares `locales.available`, a page route with a `locale` or `lang` segment (`{-$locale}`, `[lang]`) whose module gives that parameter no schema is reported, saying what to add. Any schema answers it, so a `$lang` that is not a language is silenced with its own, e.g. `{ lang: s.enum("typescript", "python") }`. It is an ordinary validation error: the Studio still opens and `val validate` lists it, but a project that already has such a route will see it after upgrading, and it blocks publishing until the parameter has a schema.
|
|
41
|
+
- **The Studio's new-page form offers a language as a list.** A route parameter whose schema is a locale is picked from the project's languages, each named in its own language, with "no prefix" where the URL may leave the segment out. Other enum parameters get a list of their values, and every parameter's description is shown under it. The URL row previews what the choice puts there.
|
|
42
|
+
- **TanStack optional segments (`{-$param}`)** are now understood: route validation, `useValRoute` and the Studio's new-page form accept keys with and without the segment.
|
|
43
|
+
|
|
3
44
|
## 0.146.2
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
|
@@ -22,3 +22,12 @@ export declare function getSourcePathFromRoute(pathname: string, schemas: Record
|
|
|
22
22
|
* which is which.
|
|
23
23
|
*/
|
|
24
24
|
export declare function isPageRouter(routerId: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The route pattern a page router's module serves, or `null` for a router
|
|
27
|
+
* whose keys are not routes of this site.
|
|
28
|
+
*
|
|
29
|
+
* By router id, for the callers that hold a SERIALIZED schema — which carries
|
|
30
|
+
* the id and not the router. A schema instance asks its router directly
|
|
31
|
+
* (`ValRouter.getRoutePattern`), and both end at the same parser.
|
|
32
|
+
*/
|
|
33
|
+
export declare function routePatternOf(routerId: string, moduleFilePath: ModuleFilePath): string[] | null;
|
|
@@ -93,8 +93,8 @@ export { deserializeSchema } from "./schema/deserialize.js";
|
|
|
93
93
|
export { type PreviewItem, type ItemPreviewInput, type RecordPreview, type ArrayPreview, type ReifiedPreview, type PreviewScope, previewScope, } from "./preview.js";
|
|
94
94
|
export { type InlineRender, type FieldRender, type SliderRender, type NumberRender, isInlineRender, } from "./render.js";
|
|
95
95
|
export type { ValRouter, ExternalPageRouter, RouteValidationError, } from "./router.js";
|
|
96
|
-
export { parseNextJsRoutePattern, parseTanStackRoutePattern, tanStackSegmentsOfRoutePath, validateUrlAgainstPattern, } from "./router.js";
|
|
97
|
-
export { getSourcePathFromRoute, isPageRouter } from "./getSourcePathFromRoute.js";
|
|
96
|
+
export { parseNextJsRoutePattern, parseTanStackRoutePattern, tanStackSegmentsOfRoutePath, validateUrlAgainstPattern, matchRoutePattern, } from "./router.js";
|
|
97
|
+
export { getSourcePathFromRoute, isPageRouter, routePatternOf, } from "./getSourcePathFromRoute.js";
|
|
98
98
|
/**
|
|
99
99
|
* Which URL schemes an external page key may carry.
|
|
100
100
|
*
|
|
@@ -113,11 +113,12 @@ export declare const DEFAULT_APP_HOST = "https://admin.val.build";
|
|
|
113
113
|
* Where Val's own immutable build artifacts are served from: the content
|
|
114
114
|
* service's `/v1/static`.
|
|
115
115
|
*
|
|
116
|
-
* Only one kind of thing lives here so far: the WebAssembly
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* TEXT inside the server bundle, which
|
|
120
|
-
*
|
|
116
|
+
* Only one kind of thing lives here so far: the WebAssembly the Studio loads
|
|
117
|
+
* -- the bundler it builds a site with, and the WebP encoder it uses where the
|
|
118
|
+
* canvas cannot write WebP. None of it can travel with the Studio's own bundle:
|
|
119
|
+
* `/api/val/static` is a base64 record of TEXT inside the server bundle, which
|
|
120
|
+
* both corrupts a binary and would carry it (10.9 MB of bundler) into every
|
|
121
|
+
* project that installs `@valbuild/ui`.
|
|
121
122
|
*
|
|
122
123
|
* Everything under this path is addressed by the SHA-256 of its own bytes, so
|
|
123
124
|
* an upload is idempotent, a URL is immutable, and the artifact a build asks
|
|
@@ -129,11 +130,11 @@ export declare const DEFAULT_APP_HOST = "https://admin.val.build";
|
|
|
129
130
|
* availability and one more domain to every customer's egress allowlist.
|
|
130
131
|
*
|
|
131
132
|
* A FIXED LITERAL, and deliberately not derived from a project's configured
|
|
132
|
-
* content host (`VAL_CONTENT_URL`). Deriving it would make serving
|
|
133
|
+
* content host (`VAL_CONTENT_URL`). Deriving it would make serving these binaries
|
|
133
134
|
* an obligation of every content service anybody points Val at, self-hosted
|
|
134
|
-
* ones included. A deployment that must fetch
|
|
135
|
-
* `globalThis.__VAL_ROLLDOWN_WASM_URL__`
|
|
136
|
-
* `packages/ui/build/
|
|
135
|
+
* ones included. A deployment that must fetch them from somewhere else sets
|
|
136
|
+
* one global per binary instead -- `globalThis.__VAL_ROLLDOWN_WASM_URL__` and
|
|
137
|
+
* the others listed in `packages/ui/build/staticWasm.ts`.
|
|
137
138
|
*/
|
|
138
139
|
export declare const DEFAULT_STATIC_HOST = "https://content.val.build/v1/static";
|
|
139
140
|
declare const Internal: {
|
|
@@ -393,6 +393,25 @@ export type InitSchema = {
|
|
|
393
393
|
* });
|
|
394
394
|
* ```
|
|
395
395
|
*
|
|
396
|
+
* @example Route parameters, by the names the route file gives them
|
|
397
|
+
* ```typescript
|
|
398
|
+
* import { s, c, tanstackRouter } from "../val.config";
|
|
399
|
+
* // `/about` is English and `/nb/about` Norwegian: `{-$locale}` is an
|
|
400
|
+
* // optional segment, and `{ null }` says what a URL without it is in.
|
|
401
|
+
* const locale = s
|
|
402
|
+
* .enum("nb")
|
|
403
|
+
* .nullable()
|
|
404
|
+
* .locales({ nb: "nb-NO" }, { null: "en-US" });
|
|
405
|
+
* export default c.define(
|
|
406
|
+
* "/src/routes/{-$locale}.$slug.val.ts",
|
|
407
|
+
* s.router(tanstackRouter, { locale, slug: s.string() }, s.object({ title: s.string() })),
|
|
408
|
+
* {
|
|
409
|
+
* "/about": { title: "About us" },
|
|
410
|
+
* "/nb/about": { title: "Om oss" },
|
|
411
|
+
* },
|
|
412
|
+
* );
|
|
413
|
+
* ```
|
|
414
|
+
*
|
|
396
415
|
* @param router - The router configuration (e.g., nextAppRouter)
|
|
397
416
|
* @param schema - The schema for each route item
|
|
398
417
|
* @returns A RecordSchema configured as a router
|
|
@@ -33,6 +33,37 @@ export type RouteValidationError = {
|
|
|
33
33
|
expectedPath: string | null;
|
|
34
34
|
};
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* The value of each parameter a URL gives a route pattern, or `null` when the
|
|
38
|
+
* URL is not one of that pattern's.
|
|
39
|
+
*
|
|
40
|
+
* A parameter that an OPTIONAL segment left out is `null` rather than absent,
|
|
41
|
+
* so a caller can tell "this route has no such parameter" from "this URL did
|
|
42
|
+
* not use it". A catch-all's segments are joined with `/`, which is how they
|
|
43
|
+
* appear in the key; an optional catch-all with nothing to catch is `null`.
|
|
44
|
+
*
|
|
45
|
+
* The pattern vocabulary is the one both parsers produce: `[name]` is a
|
|
46
|
+
* segment, `[[name]]` an optional one, `[...name]` a catch-all and
|
|
47
|
+
* `[[...name]]` an optional catch-all; anything else is literal.
|
|
48
|
+
*
|
|
49
|
+
* Optional segments are what make this a search rather than a walk: in
|
|
50
|
+
* `/[[locale]]/blog/[slug]` the URL `/blog/hello` only matches once `blog`
|
|
51
|
+
* has been tried as the locale and given back. A present segment is tried
|
|
52
|
+
* first, so `/nb/blog/hello` reads as the locale `nb` — the reading the router
|
|
53
|
+
* itself makes.
|
|
54
|
+
*/
|
|
55
|
+
export declare function matchRoutePattern(urlPath: string, routePattern: string[]): Record<string, string | null> | null;
|
|
56
|
+
/**
|
|
57
|
+
* The parameters a route pattern has, by name, and whether a URL may leave
|
|
58
|
+
* each one out.
|
|
59
|
+
*/
|
|
60
|
+
export declare function routeParamsOfPattern(routePattern: string[]): {
|
|
61
|
+
name: string;
|
|
62
|
+
optional: boolean;
|
|
63
|
+
}[];
|
|
64
|
+
/**
|
|
65
|
+
* Whether a URL is one of a route pattern's, and the pattern to show when not.
|
|
66
|
+
*/
|
|
36
67
|
export declare function validateUrlAgainstPattern(urlPath: string, routePattern: string[]): {
|
|
37
68
|
isValid: boolean;
|
|
38
69
|
expectedPath?: string;
|
|
@@ -61,6 +92,7 @@ export declare function parseNextJsRoutePattern(moduleFilePath: string): string[
|
|
|
61
92
|
* - `.` and `/` both separate segments (`posts.$postId.val.ts` and
|
|
62
93
|
* `posts/$postId.val.ts` are the same route)
|
|
63
94
|
* - `$param` is a dynamic segment, `$` on its own is a splat
|
|
95
|
+
* - `{-$param}` is an optional segment, written `[[param]]`
|
|
64
96
|
* - `index` is the directory's own route and contributes no segment
|
|
65
97
|
* - `route` is a layout for the directory, likewise
|
|
66
98
|
* - `(group)` folders and `_pathless` layout segments are not in the URL
|
|
@@ -75,6 +107,7 @@ export declare function parseNextJsRoutePattern(moduleFilePath: string): string[
|
|
|
75
107
|
* - /routes/posts/$postId.val.ts -> ["posts", "[postId]"]
|
|
76
108
|
* - /src/routes/index.val.ts -> []
|
|
77
109
|
* - /src/routes/files.$.val.ts -> ["files", "[..._splat]"]
|
|
110
|
+
* - /src/routes/{-$locale}.about.val.ts -> ["[[locale]]", "about"]
|
|
78
111
|
* - /src/routes/(app)/_layout.about.val.ts -> ["about"]
|
|
79
112
|
*/
|
|
80
113
|
export declare function parseTanStackRoutePattern(moduleFilePath: string): string[];
|
|
@@ -105,5 +138,14 @@ export interface ValRouter {
|
|
|
105
138
|
* than URLs, which is every other one.
|
|
106
139
|
*/
|
|
107
140
|
getUrlSchemePolicy?(): ExternalUrlSchemePolicy;
|
|
141
|
+
/**
|
|
142
|
+
* The route pattern a module of this router serves, in the `[param]`
|
|
143
|
+
* vocabulary — see {@link matchRoutePattern}.
|
|
144
|
+
*
|
|
145
|
+
* What lets `s.router(router, { locale: …, slug: … }, item)` find a key's
|
|
146
|
+
* parameters by NAME. Absent on a router whose keys are not paths of this
|
|
147
|
+
* site, which therefore has no parameters to give a schema to.
|
|
148
|
+
*/
|
|
149
|
+
getRoutePattern?(moduleFilePath: ModuleFilePath): string[];
|
|
108
150
|
validate(moduleFilePath: ModuleFilePath, urlPaths: string[]): RouteValidationError[];
|
|
109
151
|
}
|
|
@@ -11,6 +11,16 @@ export type SerializedEnumSchema = {
|
|
|
11
11
|
* the editor's dropdown offers them in.
|
|
12
12
|
*/
|
|
13
13
|
values: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Set by `.locales(...)`: the project language each value MEANS, by value.
|
|
16
|
+
* Its presence is what makes this enum a locale — see `EnumSchema.locales`.
|
|
17
|
+
*/
|
|
18
|
+
locales?: Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Set by `.locales(..., { null: tag })`: the language of a URL that leaves
|
|
21
|
+
* this route parameter out. Only meaningful on a router parameter.
|
|
22
|
+
*/
|
|
23
|
+
nullLocale?: string;
|
|
14
24
|
opt: boolean;
|
|
15
25
|
customValidate?: boolean;
|
|
16
26
|
readonly?: boolean;
|
|
@@ -34,6 +44,10 @@ export declare class EnumSchema<Src extends string | null> extends Schema<Src> {
|
|
|
34
44
|
private readonly isHidden;
|
|
35
45
|
private readonly description?;
|
|
36
46
|
private readonly previewInput;
|
|
47
|
+
/** Set by `.locales(...)`: the language each value means. */
|
|
48
|
+
private readonly localeMap;
|
|
49
|
+
/** Set by `.locales(..., { null })`: the language of a missing segment. */
|
|
50
|
+
private readonly nullLocale;
|
|
37
51
|
/**
|
|
38
52
|
* Type-only marker: as a record key, this declares the key set.
|
|
39
53
|
*
|
|
@@ -43,7 +57,11 @@ export declare class EnumSchema<Src extends string | null> extends Schema<Src> {
|
|
|
43
57
|
* cannot be a record key at all. See `LocaleSchema`.
|
|
44
58
|
*/
|
|
45
59
|
readonly __declaresRecordKeys: true;
|
|
46
|
-
constructor(values: readonly string[], opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null
|
|
60
|
+
constructor(values: readonly string[], opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, previewInput?: ItemPreviewInput<Src> | null,
|
|
61
|
+
/** Set by `.locales(...)`: the language each value means. */
|
|
62
|
+
localeMap?: Readonly<Record<string, string>> | null,
|
|
63
|
+
/** Set by `.locales(..., { null })`: the language of a missing segment. */
|
|
64
|
+
nullLocale?: string | null);
|
|
47
65
|
/**
|
|
48
66
|
* Describe this field.
|
|
49
67
|
*
|
|
@@ -85,10 +103,108 @@ export declare class EnumSchema<Src extends string | null> extends Schema<Src> {
|
|
|
85
103
|
*/
|
|
86
104
|
validate(validationFunction: (src: Src) => false | string): EnumSchema<Src>;
|
|
87
105
|
protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
|
|
106
|
+
/**
|
|
107
|
+
* Validate the value a URL gives a route parameter — see
|
|
108
|
+
* `s.router(router, params, item)`.
|
|
109
|
+
*
|
|
110
|
+
* Not `executeValidate`, because one thing is right here that is wrong on a
|
|
111
|
+
* field: `{ null: tag }`. A URL that leaves out an optional segment has a
|
|
112
|
+
* language someone chose; a field that holds `null` has none, and Val does
|
|
113
|
+
* not guess one for it.
|
|
114
|
+
*/
|
|
115
|
+
protected validateRouteParam(path: SourcePath, src: Src): ValidationErrors;
|
|
116
|
+
private validateAs;
|
|
117
|
+
/**
|
|
118
|
+
* The tag `value` is mapped to, or `null` where the mapping does not name it.
|
|
119
|
+
*
|
|
120
|
+
* An OWN property, not `in`: a mapping that came back from JSON is a plain
|
|
121
|
+
* object, which inherits `toString` and its siblings — and an enum value may
|
|
122
|
+
* be spelled like one of them, which would read as mapped when it is not.
|
|
123
|
+
*/
|
|
124
|
+
private localeTagOf;
|
|
125
|
+
/**
|
|
126
|
+
* The cross-module half of the check: is `tag` one of the project's
|
|
127
|
+
* languages? The same deferred error `s.locale()` reports, resolved by
|
|
128
|
+
* `resolveSchemaSourceFixes` against `locales.available` — so a mapped
|
|
129
|
+
* value and a written-out tag are checked by one implementation.
|
|
130
|
+
*/
|
|
131
|
+
private checkLocale;
|
|
132
|
+
/**
|
|
133
|
+
* What is wrong with the mapping itself, whatever the value is.
|
|
134
|
+
*
|
|
135
|
+
* The compiler checks all of this for a schema written in TypeScript except
|
|
136
|
+
* the spelling and the uniqueness of the tags; the rest is here for a schema
|
|
137
|
+
* that came back from JSON.
|
|
138
|
+
*/
|
|
139
|
+
protected localeMappingErrors(): ValidationError[];
|
|
140
|
+
private localeMappingErrorsMemo?;
|
|
141
|
+
private computeLocaleMappingErrors;
|
|
88
142
|
protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
|
|
89
143
|
nullable(): EnumSchema<Src | null>;
|
|
90
144
|
readonly(isReadonly?: boolean): EnumSchema<Src>;
|
|
91
145
|
hidden(isHidden?: boolean): EnumSchema<Src>;
|
|
146
|
+
/**
|
|
147
|
+
* Say which of the project's languages each value MEANS — which makes this
|
|
148
|
+
* enum a locale, the way `s.locale()` is one.
|
|
149
|
+
*
|
|
150
|
+
* For a value that is not a language tag itself: most often a route
|
|
151
|
+
* parameter, where a site writes `/nb/…` and means `nb-NO`. The values are
|
|
152
|
+
* what is stored (and what is in the URL); the tags on the right are what
|
|
153
|
+
* they mean, and those are checked against `locales.available` in the
|
|
154
|
+
* settings module exactly as an `s.locale()` value is.
|
|
155
|
+
*
|
|
156
|
+
* On a nullable enum it takes an optional second argument, `{ null: tag }`:
|
|
157
|
+
* the language of a URL that leaves the segment out, which is how a site
|
|
158
|
+
* serves its default language without a prefix. That is ONLY for a router
|
|
159
|
+
* parameter, where leaving it out of a nullable one is reported on every URL
|
|
160
|
+
* without the segment. On a field `null` means nobody chose a language, and
|
|
161
|
+
* saying which one it "really" is would file content under a language
|
|
162
|
+
* nobody picked, so there it is a schema error.
|
|
163
|
+
*
|
|
164
|
+
* One spelling per language: two values (or a value and `null`) that mean
|
|
165
|
+
* the same tag would give one page two URLs in that language.
|
|
166
|
+
*
|
|
167
|
+
* @example // a route parameter: `/about` is English, `/nb/about` Norwegian
|
|
168
|
+
* import { tanstackRouter } from "../val.config";
|
|
169
|
+
* const urlLocale = s
|
|
170
|
+
* .enum("nb")
|
|
171
|
+
* .nullable()
|
|
172
|
+
* .locales({ nb: "nb-NO" }, { null: "en-US" });
|
|
173
|
+
* const schema = s.router(
|
|
174
|
+
* tanstackRouter,
|
|
175
|
+
* { locale: urlLocale },
|
|
176
|
+
* s.object({ title: s.string() }),
|
|
177
|
+
* );
|
|
178
|
+
* export default c.define("/src/routes/{-$locale}.about.val.ts", schema, {
|
|
179
|
+
* "/about": { title: "About us" },
|
|
180
|
+
* "/nb/about": { title: "Om oss" },
|
|
181
|
+
* });
|
|
182
|
+
*
|
|
183
|
+
* @example // a field that stores a short code
|
|
184
|
+
* const schema = s.object({
|
|
185
|
+
* language: s.enum("en", "nb").locales({ en: "en-US", nb: "nb-NO" }),
|
|
186
|
+
* title: s.string(),
|
|
187
|
+
* });
|
|
188
|
+
* export default c.define("/example.val.ts", schema, {
|
|
189
|
+
* language: "nb",
|
|
190
|
+
* title: "Vinterjakke",
|
|
191
|
+
* });
|
|
192
|
+
*/
|
|
193
|
+
locales(locales: {
|
|
194
|
+
readonly [Value in NonNullable<Src>]: string;
|
|
195
|
+
}, ...whenNull: null extends Src ? [whenNull?: {
|
|
196
|
+
null: string;
|
|
197
|
+
}] : []): EnumSchema<Src>;
|
|
198
|
+
/**
|
|
199
|
+
* The language of a URL that leaves this parameter out, where one was given.
|
|
200
|
+
* For `RecordSchema`, which checks that the route can leave it out at all.
|
|
201
|
+
*/
|
|
202
|
+
protected routeParamNullLocale(): string | null;
|
|
203
|
+
/**
|
|
204
|
+
* Whether `.locales(...)` made this enum a language. If so, an object with
|
|
205
|
+
* it as a field is in that language, the same as one with `s.locale()`.
|
|
206
|
+
*/
|
|
207
|
+
protected isLocaleField(): boolean;
|
|
92
208
|
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
93
209
|
/**
|
|
94
210
|
* How this VALUE is shown where a preview of it is needed — a row in a
|
|
@@ -97,6 +97,11 @@ export declare abstract class Schema<Src extends SelectorSource> {
|
|
|
97
97
|
* and the reason it is validated rather than typed.
|
|
98
98
|
*/
|
|
99
99
|
protected opensLocaleScope(): "field" | "key" | null;
|
|
100
|
+
/**
|
|
101
|
+
* What to call this node in a message about the scope it opens: "object" for
|
|
102
|
+
* one with a locale field, "locale-keyed record" for a record keyed by one.
|
|
103
|
+
*/
|
|
104
|
+
protected localeScopeName(): string;
|
|
100
105
|
/**
|
|
101
106
|
* The child schemas a locale scope reaches, by the path segment that reaches
|
|
102
107
|
* them.
|
|
@@ -59,6 +59,11 @@ export type SerializedRecordSchema = {
|
|
|
59
59
|
* wide default, which the Studio knows on its own.
|
|
60
60
|
*/
|
|
61
61
|
routerSchemes?: string[];
|
|
62
|
+
/**
|
|
63
|
+
* A router's parameter schemas, by the name the route file gives each one.
|
|
64
|
+
* See `s.router(router, params, item)`.
|
|
65
|
+
*/
|
|
66
|
+
params?: Record<string, SerializedSchema>;
|
|
62
67
|
customValidate?: boolean;
|
|
63
68
|
mediaType?: MediaCollectionType;
|
|
64
69
|
accept?: string;
|
|
@@ -116,28 +121,36 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
116
121
|
/**
|
|
117
122
|
* Set by `.external(label)`; the entries live behind that adapter.
|
|
118
123
|
*
|
|
119
|
-
*
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
*
|
|
123
|
-
* silently shifts `previewInput` and `renderInput` at each of those call
|
|
124
|
-
* sites.
|
|
124
|
+
* Appended rather than inserted, and so is everything after it: every
|
|
125
|
+
* `new RecordSchema(...)` in the codebase passes its arguments
|
|
126
|
+
* positionally, so a new parameter anywhere else silently shifts
|
|
127
|
+
* `previewInput` and `renderInput` at each of those call sites.
|
|
125
128
|
*/
|
|
126
129
|
private readonly externalLabel;
|
|
130
|
+
/**
|
|
131
|
+
* Set by `s.router(router, { name: schema }, item)`: a schema for each
|
|
132
|
+
* route parameter, by the name the route file gives it. See
|
|
133
|
+
* `getRouteParamValidations`.
|
|
134
|
+
*/
|
|
135
|
+
private readonly routeParams;
|
|
127
136
|
constructor(item: T, opt?: boolean, customValidateFunctions?: CustomValidateFunction<Src>[], currentRouter?: ValRouter | null, keySchema?: Schema<string> | null, mediaOptions?: MediaOptions | undefined, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined,
|
|
128
137
|
/** When true, entry values are lazily loaded {@link JsonSource} thunks. */
|
|
129
138
|
isJsonValues?: boolean, previewInput?: ItemPreviewInput<Src> | null, renderInput?: FieldRender | null,
|
|
130
139
|
/**
|
|
131
140
|
* Set by `.external(label)`; the entries live behind that adapter.
|
|
132
141
|
*
|
|
133
|
-
*
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
* Appended rather than inserted, and so is everything after it: every
|
|
143
|
+
* `new RecordSchema(...)` in the codebase passes its arguments
|
|
144
|
+
* positionally, so a new parameter anywhere else silently shifts
|
|
145
|
+
* `previewInput` and `renderInput` at each of those call sites.
|
|
146
|
+
*/
|
|
147
|
+
externalLabel?: string | null,
|
|
148
|
+
/**
|
|
149
|
+
* Set by `s.router(router, { name: schema }, item)`: a schema for each
|
|
150
|
+
* route parameter, by the name the route file gives it. See
|
|
151
|
+
* `getRouteParamValidations`.
|
|
139
152
|
*/
|
|
140
|
-
|
|
153
|
+
routeParams?: Readonly<Record<string, Schema<string | null>>> | null);
|
|
141
154
|
/**
|
|
142
155
|
* Describe this field.
|
|
143
156
|
*
|
|
@@ -212,6 +225,7 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
212
225
|
private declaredKeys;
|
|
213
226
|
private declaredKeySet;
|
|
214
227
|
protected opensLocaleScope(): "field" | "key" | null;
|
|
228
|
+
protected localeScopeName(): string;
|
|
215
229
|
protected localeScopeChildren(): {
|
|
216
230
|
key: string;
|
|
217
231
|
schema: Schema<SelectorSource>;
|
|
@@ -364,6 +378,38 @@ RO extends boolean = false> extends Schema<Src> {
|
|
|
364
378
|
*/
|
|
365
379
|
private validateExternal;
|
|
366
380
|
private getRouterValidations;
|
|
381
|
+
/**
|
|
382
|
+
* Validate what each key gives its route parameters, against the schema
|
|
383
|
+
* `s.router(router, { name: schema }, item)` gave that parameter.
|
|
384
|
+
*
|
|
385
|
+
* The parameters are bound by NAME — the name the route file gives them
|
|
386
|
+
* (`$slug`, `[slug]`, `{-$locale}`) — rather than by position, so renaming
|
|
387
|
+
* or adding a segment in the route file cannot silently hand one
|
|
388
|
+
* parameter's schema to another. A name the route does not have is
|
|
389
|
+
* therefore a schema error rather than a parameter that is never checked.
|
|
390
|
+
*
|
|
391
|
+
* Keys that do not match the route at all are left to the router's own
|
|
392
|
+
* validation, which says what the route expects; there is nothing here to
|
|
393
|
+
* give a parameter.
|
|
394
|
+
*/
|
|
395
|
+
private getRouteParamValidations;
|
|
396
|
+
/**
|
|
397
|
+
* A marker for each `locale` or `lang` segment of this page's route that no
|
|
398
|
+
* route parameter schema describes.
|
|
399
|
+
*
|
|
400
|
+
* A router only checks a key's language when its module says which
|
|
401
|
+
* parameter is the language — `s.router(router, { locale: urlLocale, … },
|
|
402
|
+
* item)` — and that is written again in every route module. Forgetting it in
|
|
403
|
+
* one is silent: `/xx/blog/hello` validates and the page is in no language.
|
|
404
|
+
*
|
|
405
|
+
* A marker (`router:check-locale-param`) rather than an error, because
|
|
406
|
+
* whether it is a mistake depends on whether the project declares languages
|
|
407
|
+
* at all, which is in the settings module; `resolveSchemaSourceFixes`
|
|
408
|
+
* answers it. Any schema for the parameter is an answer — `{ lang:
|
|
409
|
+
* s.enum("typescript", "python") }` is how a `$lang` that is not a language
|
|
410
|
+
* says so.
|
|
411
|
+
*/
|
|
412
|
+
private getUndescribedLocaleParamMarkers;
|
|
367
413
|
protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
|
|
368
414
|
protected executeSerialize(): SerializedRecordSchema;
|
|
369
415
|
/**
|
|
@@ -2,5 +2,13 @@ import { Schema, SelectorOfSchema } from "./index.js";
|
|
|
2
2
|
import { ValRouter } from "../router.js";
|
|
3
3
|
import { SelectorSource } from "../selector/index.js";
|
|
4
4
|
import { RecordSchema } from "./record.js";
|
|
5
|
+
/**
|
|
6
|
+
* A schema for each of a route's parameters, by the name the route file gives
|
|
7
|
+
* it: `$slug` / `[slug]` is `slug`, `{-$locale}` is `locale`.
|
|
8
|
+
*/
|
|
9
|
+
export type RouteParamSchemas = {
|
|
10
|
+
readonly [name: string]: Schema<string | null>;
|
|
11
|
+
};
|
|
5
12
|
export declare function router<K extends Schema<string>, T extends Schema<SelectorSource>>(router: ValRouter, key: K, item: T): RecordSchema<T, K, Record<SelectorOfSchema<K>, SelectorOfSchema<T>>>;
|
|
13
|
+
export declare function router<T extends Schema<SelectorSource>>(router: ValRouter, params: RouteParamSchemas, item: T): RecordSchema<T, Schema<string>, Record<string, SelectorOfSchema<T>>>;
|
|
6
14
|
export declare function router<T extends Schema<SelectorSource>>(router: ValRouter, item: T): RecordSchema<T, Schema<string>, Record<string, SelectorOfSchema<T>>>;
|
|
@@ -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", "video:add-metadata", "video:upload-remote", "video:download-remote", "videos:add-metadata", "videos:check-remote", "videos:upload-remote", "keyof:check-keys", "image:check-gallery", "file:check-gallery", "video:check-gallery", "router:check-route", "locale:check-locale", "images:check-unique-folder", "files:check-unique-folder", "images:check-all-files", "files:check-all-files", "videos:check-unique-folder", "videos:check-all-files", "jsonValues:extract-entry", "record:fill-keys", "external:upload", "view:check-module"];
|
|
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", "video:add-metadata", "video:upload-remote", "video:download-remote", "videos:add-metadata", "videos:check-remote", "videos:upload-remote", "keyof:check-keys", "image:check-gallery", "file:check-gallery", "video:check-gallery", "router:check-route", "locale:check-locale", "router:check-locale-param", "images:check-unique-folder", "files:check-unique-folder", "images:check-all-files", "files:check-all-files", "videos:check-unique-folder", "videos:check-all-files", "jsonValues:extract-entry", "record:fill-keys", "external:upload", "view:check-module", "remote:rewrite-host"];
|
|
2
2
|
export type ValidationFix = (typeof ValidationFix)[number];
|