@valbuild/core 0.16.0 → 0.18.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/ROADMAP.md +106 -0
- package/dist/declarations/src/ValApi.d.ts +26 -0
- package/dist/declarations/src/expr/eval.d.ts +1 -1
- package/dist/declarations/src/future/fetchVal.d.ts +5 -0
- package/dist/declarations/src/index.d.ts +36 -9
- package/dist/declarations/src/initSchema.d.ts +8 -10
- package/dist/declarations/src/initVal.d.ts +3 -21
- package/dist/declarations/src/module.d.ts +2 -14
- package/dist/declarations/src/schema/image.d.ts +1 -1
- package/dist/declarations/src/schema/index.d.ts +3 -5
- package/dist/declarations/src/schema/keyOf.d.ts +23 -0
- package/dist/declarations/src/schema/record.d.ts +19 -0
- package/dist/declarations/src/schema/union.d.ts +1 -0
- package/dist/declarations/src/selector/SelectorProxy.d.ts +7 -0
- package/dist/declarations/src/selector/array.d.ts +2 -13
- package/dist/declarations/src/selector/file.d.ts +1 -4
- package/dist/declarations/src/selector/future/array.d.ts +17 -0
- package/dist/declarations/src/selector/future/boolean.d.ts +2 -0
- package/dist/declarations/src/selector/future/file.d.ts +9 -0
- package/dist/declarations/src/selector/{i18n.d.ts → future/i18n.d.ts} +2 -2
- package/dist/declarations/src/selector/future/index.d.ts +81 -0
- package/dist/declarations/src/selector/future/number.d.ts +2 -0
- package/dist/declarations/src/selector/future/object.d.ts +10 -0
- package/dist/declarations/src/selector/future/primitive.d.ts +9 -0
- package/dist/declarations/src/selector/{remote.d.ts → future/remote.d.ts} +1 -1
- package/dist/declarations/src/selector/future/string.d.ts +2 -0
- package/dist/declarations/src/selector/index.d.ts +5 -32
- package/dist/declarations/src/selector/object.d.ts +2 -9
- package/dist/declarations/src/selector/primitive.d.ts +3 -9
- package/dist/declarations/src/source/{i18n.d.ts → future/i18n.d.ts} +2 -2
- package/dist/declarations/src/source/{remote.d.ts → future/remote.d.ts} +3 -3
- package/dist/declarations/src/source/index.d.ts +4 -3
- package/dist/declarations/src/val/index.d.ts +2 -2
- package/dist/{index-2fff5ca8.cjs.dev.js → index-486c7fbf.cjs.dev.js} +242 -54
- package/dist/{index-af761363.esm.js → index-4abf3a1f.esm.js} +189 -1
- package/dist/{index-cac9ecbd.cjs.prod.js → index-601a7d73.cjs.prod.js} +242 -54
- package/dist/{SelectorProxy-2af1b2b8.cjs.prod.js → index-8706c87e.cjs.prod.js} +25 -199
- package/dist/{SelectorProxy-873782a5.cjs.dev.js → index-a6e642dd.cjs.dev.js} +25 -199
- package/dist/{SelectorProxy-63c2d0e2.esm.js → index-a9235737.esm.js} +23 -195
- package/dist/{ops-1b6e0e35.cjs.prod.js → ops-0d09f8ee.cjs.prod.js} +258 -126
- package/dist/{ops-74661336.esm.js → ops-a2a295f8.esm.js} +195 -65
- package/dist/{ops-ea4827fc.cjs.dev.js → ops-f3015423.cjs.dev.js} +258 -126
- package/dist/valbuild-core.cjs.dev.js +784 -197
- package/dist/valbuild-core.cjs.prod.js +784 -197
- package/dist/valbuild-core.esm.js +719 -133
- package/expr/dist/valbuild-core-expr.cjs.dev.js +8 -8
- package/expr/dist/valbuild-core-expr.cjs.prod.js +8 -8
- package/expr/dist/valbuild-core-expr.esm.js +2 -2
- package/package.json +2 -3
- package/patch/dist/valbuild-core-patch.cjs.dev.js +24 -24
- package/patch/dist/valbuild-core-patch.cjs.prod.js +24 -24
- package/patch/dist/valbuild-core-patch.esm.js +3 -3
- package/src/ValApi.ts +85 -0
- package/src/expr/eval.test.ts +2 -2
- package/src/expr/eval.ts +2 -2
- package/src/{fetchVal.test.ts → future/fetchVal.test.ts} +57 -57
- package/src/{fetchVal.ts → future/fetchVal.ts} +17 -22
- package/src/index.ts +46 -14
- package/src/initSchema.ts +17 -12
- package/src/initVal.ts +42 -52
- package/src/module.test.ts +40 -40
- package/src/module.ts +53 -43
- package/src/patch/deref.test.ts +1 -1
- package/src/patch/deref.ts +1 -1
- package/src/patch/json.test.ts +0 -1
- package/src/schema/{i18n.ts → future/i18n.ts} +12 -10
- package/src/schema/{oneOf.ts → future/oneOf.ts} +19 -17
- package/src/schema/image.ts +1 -1
- package/src/schema/index.ts +16 -13
- package/src/schema/keyOf.ts +167 -0
- package/src/schema/record.ts +103 -0
- package/src/schema/union.ts +1 -1
- package/src/schema/validation.test.ts +195 -130
- package/src/selector/SelectorProxy.ts +15 -15
- package/src/selector/array.ts +2 -26
- package/src/selector/file.ts +1 -9
- package/src/selector/{ExprProxy.test.ts → future/ExprProxy.test.ts} +2 -2
- package/src/selector/{ExprProxy.ts → future/ExprProxy.ts} +9 -2
- package/src/selector/{SelectorProxy.test.ts → future/SelectorProxy.test.ts} +4 -4
- package/src/selector/future/SelectorProxy.ts +238 -0
- package/src/selector/future/array.ts +37 -0
- package/src/selector/future/boolean.ts +4 -0
- package/src/selector/future/file.ts +14 -0
- package/src/selector/{i18n.ts → future/i18n.ts} +2 -2
- package/src/selector/future/index.ts +165 -0
- package/src/selector/future/number.ts +4 -0
- package/src/selector/future/object.ts +22 -0
- package/src/selector/future/primitive.ts +17 -0
- package/src/selector/{remote.ts → future/remote.ts} +1 -1
- package/src/selector/{selector.test.ts → future/selector.test.ts} +8 -28
- package/src/selector/{selectorOf.ts → future/selectorOf.ts} +1 -1
- package/src/selector/future/string.ts +4 -0
- package/src/selector/index.ts +4 -46
- package/src/selector/object.ts +2 -19
- package/src/selector/primitive.ts +3 -16
- package/src/source/{i18n.ts → future/i18n.ts} +2 -2
- package/src/source/{remote.ts → future/remote.ts} +3 -3
- package/src/source/index.ts +3 -2
- package/src/val/array.ts +1 -1
- package/src/val/index.ts +2 -2
- package/src/val/object.ts +1 -1
- package/dist/declarations/src/fetchVal.d.ts +0 -5
- package/dist/declarations/src/schema/i18n.d.ts +0 -22
- package/dist/declarations/src/schema/oneOf.d.ts +0 -23
package/src/module.ts
CHANGED
@@ -4,16 +4,15 @@ import {
|
|
4
4
|
GenericSelector,
|
5
5
|
SelectorOf,
|
6
6
|
SelectorSource,
|
7
|
-
|
7
|
+
GetSource,
|
8
|
+
GetSchema,
|
9
|
+
Path,
|
8
10
|
} from "./selector";
|
9
|
-
import { Source
|
10
|
-
import { newSelectorProxy } from "./selector/SelectorProxy";
|
11
|
+
import { Source } from "./source";
|
11
12
|
import { ModuleId, ModulePath, SourcePath } from "./val";
|
12
13
|
import { Expr } from "./expr";
|
13
14
|
import { ArraySchema, SerializedArraySchema } from "./schema/array";
|
14
|
-
import { I18nSchema, SerializedI18nSchema } from "./schema/i18n";
|
15
15
|
import { UnionSchema, SerializedUnionSchema } from "./schema/union";
|
16
|
-
import { OneOfSchema, SerializedOneOfSchema } from "./schema/oneOf";
|
17
16
|
import { Json } from "./Json";
|
18
17
|
import { RichTextSchema, SerializedRichTextSchema } from "./schema/richtext";
|
19
18
|
import {
|
@@ -43,11 +42,15 @@ export function content<T extends Schema<SelectorSource>>(
|
|
43
42
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
44
43
|
source: SchemaTypeOf<T>
|
45
44
|
): ValModule<SchemaTypeOf<T>> {
|
46
|
-
return
|
45
|
+
return {
|
46
|
+
[GetSource]: source,
|
47
|
+
[GetSchema]: schema,
|
48
|
+
[Path]: id as SourcePath,
|
49
|
+
} as unknown as ValModule<SchemaTypeOf<T>>;
|
47
50
|
}
|
48
51
|
|
49
|
-
export function
|
50
|
-
const sourceOrExpr = valModule[
|
52
|
+
export function getSource(valModule: ValModule<SelectorSource>): Source {
|
53
|
+
const sourceOrExpr = valModule[GetSource];
|
51
54
|
if (sourceOrExpr instanceof Expr) {
|
52
55
|
throw Error("Cannot get raw source of an Expr");
|
53
56
|
}
|
@@ -100,14 +103,14 @@ function isArraySchema(
|
|
100
103
|
);
|
101
104
|
}
|
102
105
|
|
103
|
-
function isI18nSchema(
|
104
|
-
|
105
|
-
): schema is I18nSchema<readonly string[]> | SerializedI18nSchema {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
}
|
106
|
+
// function isI18nSchema(
|
107
|
+
// schema: Schema<SelectorSource> | SerializedSchema
|
108
|
+
// ): schema is I18nSchema<readonly string[]> | SerializedI18nSchema {
|
109
|
+
// return (
|
110
|
+
// schema instanceof I18nSchema ||
|
111
|
+
// (typeof schema === "object" && "type" in schema && schema.type === "i18n")
|
112
|
+
// );
|
113
|
+
// }
|
111
114
|
|
112
115
|
function isUnionSchema(
|
113
116
|
schema: Schema<SelectorSource> | SerializedSchema
|
@@ -144,15 +147,14 @@ function isImageSchema(
|
|
144
147
|
);
|
145
148
|
}
|
146
149
|
|
147
|
-
//
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
}
|
150
|
+
// function isOneOfSchema(
|
151
|
+
// schema: Schema<SelectorSource> | SerializedSchema
|
152
|
+
// ): schema is OneOfSchema<GenericSelector<SourceArray>> | SerializedOneOfSchema {
|
153
|
+
// return (
|
154
|
+
// schema instanceof OneOfSchema ||
|
155
|
+
// (typeof schema === "object" && "type" in schema && schema.type === "oneOf")
|
156
|
+
// );
|
157
|
+
// }
|
156
158
|
|
157
159
|
export function resolvePath(
|
158
160
|
path: ModulePath,
|
@@ -204,23 +206,23 @@ export function resolvePath(
|
|
204
206
|
}
|
205
207
|
resolvedSource = resolvedSource[part];
|
206
208
|
resolvedSchema = resolvedSchema.items[part];
|
207
|
-
|
208
|
-
if (!resolvedSchema.locales.includes(part)) {
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
}
|
215
|
-
if (!Object.keys(resolvedSource).includes(part)) {
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
}
|
222
|
-
resolvedSource = resolvedSource[part];
|
223
|
-
resolvedSchema = resolvedSchema.item;
|
209
|
+
// } else if (isI18nSchema(resolvedSchema)) {
|
210
|
+
// if (!resolvedSchema.locales.includes(part)) {
|
211
|
+
// throw Error(
|
212
|
+
// `Invalid path: i18n schema ${resolvedSchema} supports locales ${resolvedSchema.locales.join(
|
213
|
+
// ", "
|
214
|
+
// )}, but found: ${part}`
|
215
|
+
// );
|
216
|
+
// }
|
217
|
+
// if (!Object.keys(resolvedSource).includes(part)) {
|
218
|
+
// throw Error(
|
219
|
+
// `Schema type error: expected source to be type of i18n with locale ${part}, but got ${JSON.stringify(
|
220
|
+
// Object.keys(resolvedSource)
|
221
|
+
// )}`
|
222
|
+
// );
|
223
|
+
// }
|
224
|
+
// resolvedSource = resolvedSource[part];
|
225
|
+
// resolvedSchema = resolvedSchema.item;
|
224
226
|
} else if (isImageSchema(resolvedSchema)) {
|
225
227
|
return {
|
226
228
|
path: origParts
|
@@ -270,7 +272,15 @@ export function resolvePath(
|
|
270
272
|
throw Error(`Invalid path: ${parts.join(".")} is not a valid path`);
|
271
273
|
}
|
272
274
|
return {
|
273
|
-
path
|
275
|
+
path: origParts
|
276
|
+
.map((p) => {
|
277
|
+
if (!Number.isNaN(Number(p))) {
|
278
|
+
return p;
|
279
|
+
} else {
|
280
|
+
return JSON.stringify(p);
|
281
|
+
}
|
282
|
+
})
|
283
|
+
.join("."), // TODO: create a function generate path from parts (not sure if this always works)
|
274
284
|
schema: resolvedSchema,
|
275
285
|
source: resolvedSource,
|
276
286
|
};
|
package/src/patch/deref.test.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { file } from "../source/file";
|
2
2
|
import { result } from "../fp";
|
3
|
-
import { remote } from "../source/remote";
|
3
|
+
import { remote } from "../source/future/remote";
|
4
4
|
import { derefPatch, DerefPatchResult } from "./deref";
|
5
5
|
import { JSONOps } from "./json";
|
6
6
|
import { PatchError } from "./ops";
|
package/src/patch/deref.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { FILE_REF_PROP, isFile } from "../source/file";
|
2
2
|
import { result } from "../fp";
|
3
|
-
import { isRemote, REMOTE_REF_PROP } from "../source/remote";
|
3
|
+
import { isRemote, REMOTE_REF_PROP } from "../source/future/remote";
|
4
4
|
import { Ops, PatchError } from "./ops";
|
5
5
|
import { Patch } from "./patch";
|
6
6
|
|
package/src/patch/json.test.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
|
-
import { Schema, SchemaTypeOf, SerializedSchema } from "
|
3
|
-
import { I18nCompatibleSource, I18nSource } from "
|
4
|
-
import { SourcePath } from "
|
5
|
-
import { ValidationErrors } from "
|
2
|
+
import { Schema, SchemaTypeOf, SerializedSchema } from "..";
|
3
|
+
import { I18nCompatibleSource, I18nSource } from "../../source/future/i18n";
|
4
|
+
import { SourcePath } from "../../val";
|
5
|
+
import { ValidationErrors } from "../validation/ValidationError";
|
6
6
|
|
7
7
|
export type SerializedI18nSchema = {
|
8
8
|
type: "i18n";
|
@@ -43,12 +43,14 @@ export class I18nSchema<Locales extends readonly string[]> extends Schema<
|
|
43
43
|
}
|
44
44
|
|
45
45
|
serialize(): SerializedSchema {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
throw new Error("Method not implemented.");
|
47
|
+
|
48
|
+
// return {
|
49
|
+
// type: "i18n",
|
50
|
+
// item: this.item.serialize(),
|
51
|
+
// locales: this.locales,
|
52
|
+
// opt: this.opt,
|
53
|
+
// };
|
52
54
|
}
|
53
55
|
}
|
54
56
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
|
-
import { Schema, SerializedSchema } from "
|
3
|
-
import { ValModuleBrand } from "
|
4
|
-
import { GenericSelector } from "
|
5
|
-
import { Source, SourceArray } from "
|
6
|
-
import {
|
7
|
-
import { ValidationErrors } from "
|
2
|
+
import { Schema, SerializedSchema } from "..";
|
3
|
+
import { ValModuleBrand } from "../../module";
|
4
|
+
import { GenericSelector } from "../../selector/future";
|
5
|
+
import { Source, SourceArray } from "../../source";
|
6
|
+
import { SourcePath } from "../../val";
|
7
|
+
import { ValidationErrors } from "../validation/ValidationError";
|
8
8
|
|
9
9
|
export type SerializedOneOfSchema = {
|
10
10
|
type: "oneOf";
|
@@ -38,17 +38,19 @@ export class OneOfSchema<
|
|
38
38
|
}
|
39
39
|
|
40
40
|
serialize(): SerializedSchema {
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
throw new Error("Method not implemented.");
|
42
|
+
|
43
|
+
// const path = getValPath(this.selector);
|
44
|
+
// if (!path) {
|
45
|
+
// throw new Error(
|
46
|
+
// "Cannot serialize oneOf schema with empty selector. Make sure a Val module is used."
|
47
|
+
// );
|
48
|
+
// }
|
49
|
+
// return {
|
50
|
+
// type: "oneOf",
|
51
|
+
// selector: path,
|
52
|
+
// opt: this.opt,
|
53
|
+
// };
|
52
54
|
}
|
53
55
|
}
|
54
56
|
|
package/src/schema/image.ts
CHANGED
@@ -117,7 +117,7 @@ export const image = (
|
|
117
117
|
return new ImageSchema(options);
|
118
118
|
};
|
119
119
|
|
120
|
-
export const
|
120
|
+
export const convertFileSource = (
|
121
121
|
src: FileSource<ImageMetadata>
|
122
122
|
): { url: string; metadata?: ImageMetadata } => {
|
123
123
|
// TODO: /public should be configurable
|
package/src/schema/index.ts
CHANGED
@@ -1,44 +1,47 @@
|
|
1
|
-
import { Json } from "../Json";
|
2
1
|
import { SelectorSource } from "../selector";
|
3
|
-
import { RemoteCompatibleSource, RemoteSource } from "../source/remote";
|
2
|
+
// import { RemoteCompatibleSource, RemoteSource } from "../source/remote";
|
4
3
|
import { SourcePath } from "../val";
|
5
4
|
import { SerializedArraySchema } from "./array";
|
6
5
|
import { SerializedBooleanSchema } from "./boolean";
|
7
|
-
import { SerializedI18nSchema } from "./i18n";
|
8
6
|
import { SerializedImageSchema } from "./image";
|
7
|
+
import { SerializedKeyOfSchema } from "./keyOf";
|
9
8
|
import { SerializedLiteralSchema } from "./literal";
|
10
9
|
import { SerializedNumberSchema } from "./number";
|
11
10
|
import { SerializedObjectSchema } from "./object";
|
12
|
-
import {
|
11
|
+
import { SerializedRecordSchema } from "./record";
|
13
12
|
import { SerializedRichTextSchema } from "./richtext";
|
14
13
|
import { SerializedStringSchema } from "./string";
|
15
14
|
import { SerializedUnionSchema } from "./union";
|
16
15
|
import { ValidationErrors } from "./validation/ValidationError";
|
16
|
+
// import { SerializedI18nSchema } from "./future/i18n";
|
17
|
+
// import { SerializedOneOfSchema } from "./future/oneOf";
|
17
18
|
|
18
19
|
export type SerializedSchema =
|
20
|
+
// | SerializedOneOfSchema
|
21
|
+
// | SerializedI18nSchema
|
19
22
|
| SerializedStringSchema
|
20
23
|
| SerializedLiteralSchema
|
21
24
|
| SerializedBooleanSchema
|
22
25
|
| SerializedNumberSchema
|
23
26
|
| SerializedObjectSchema
|
24
|
-
| SerializedOneOfSchema
|
25
27
|
| SerializedArraySchema
|
26
28
|
| SerializedUnionSchema
|
27
29
|
| SerializedRichTextSchema
|
28
|
-
|
|
29
|
-
|
|
30
|
+
| SerializedRecordSchema
|
31
|
+
| SerializedKeyOfSchema
|
32
|
+
| SerializedImageSchema;
|
30
33
|
|
31
34
|
export abstract class Schema<Src extends SelectorSource> {
|
32
35
|
abstract validate(path: SourcePath, src: Src): ValidationErrors;
|
33
36
|
abstract assert(src: Src): boolean; // TODO: false | Record<SourcePath, string[]>;
|
34
37
|
abstract optional(): Schema<Src | null>;
|
35
38
|
abstract serialize(): SerializedSchema;
|
36
|
-
remote(): Src extends RemoteCompatibleSource
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
}
|
39
|
+
// remote(): Src extends RemoteCompatibleSource
|
40
|
+
// ? Schema<RemoteSource<Src>>
|
41
|
+
// : never {
|
42
|
+
// // TODO: Schema<never, "Cannot create remote schema from non-remote source.">
|
43
|
+
// throw new Error("You need Val Ultra to use .remote()");
|
44
|
+
// }
|
42
45
|
|
43
46
|
/** MUTATES! since internal and perf sensitive */
|
44
47
|
protected appendValidationError(
|
@@ -0,0 +1,167 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
|
+
import { Schema, SerializedSchema } from "../schema";
|
3
|
+
import { ValModuleBrand } from "../module";
|
4
|
+
import { GenericSelector, GetSchema } from "../selector";
|
5
|
+
import { SourceArray, SourceObject } from "../source";
|
6
|
+
import { SourcePath, getValPath } from "../val";
|
7
|
+
import { ValidationErrors } from "./validation/ValidationError";
|
8
|
+
|
9
|
+
export type SerializedKeyOfSchema = {
|
10
|
+
type: "keyOf";
|
11
|
+
selector: SourcePath;
|
12
|
+
opt: boolean;
|
13
|
+
};
|
14
|
+
|
15
|
+
type KeyOfSelector<Sel extends GenericSelector<SourceArray | SourceObject>> =
|
16
|
+
Sel extends GenericSelector<infer S>
|
17
|
+
? // TODO: remove any:
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
19
|
+
S extends readonly any[]
|
20
|
+
? number
|
21
|
+
: S extends SourceObject
|
22
|
+
? keyof S
|
23
|
+
: // TODO: remove any:
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
25
|
+
S extends Record<string, any>
|
26
|
+
? string
|
27
|
+
: never
|
28
|
+
: never;
|
29
|
+
|
30
|
+
export class KeyOfSchema<
|
31
|
+
Sel extends GenericSelector<SourceArray | SourceObject>
|
32
|
+
> extends Schema<KeyOfSelector<Sel>> {
|
33
|
+
constructor(readonly selector: Sel, readonly opt: boolean = false) {
|
34
|
+
super();
|
35
|
+
}
|
36
|
+
validate(path: SourcePath, src: KeyOfSelector<Sel>): ValidationErrors {
|
37
|
+
if (this.opt && (src === null || src === undefined)) {
|
38
|
+
return false;
|
39
|
+
}
|
40
|
+
const schema = this.selector[GetSchema];
|
41
|
+
if (!schema) {
|
42
|
+
return {
|
43
|
+
[path]: [
|
44
|
+
{
|
45
|
+
message: `Schema not found for module. keyOf must be used with a Val Module`,
|
46
|
+
},
|
47
|
+
],
|
48
|
+
};
|
49
|
+
}
|
50
|
+
const serializedSchema = schema.serialize();
|
51
|
+
|
52
|
+
if (
|
53
|
+
!(
|
54
|
+
serializedSchema.type === "array" ||
|
55
|
+
serializedSchema.type === "object" ||
|
56
|
+
serializedSchema.type === "record"
|
57
|
+
)
|
58
|
+
) {
|
59
|
+
return {
|
60
|
+
[path]: [
|
61
|
+
{
|
62
|
+
message: `Schema in keyOf must be an 'array', 'object' or 'record'. Found '${serializedSchema.type}'`,
|
63
|
+
},
|
64
|
+
],
|
65
|
+
};
|
66
|
+
}
|
67
|
+
if (serializedSchema.opt && (src === null || src === undefined)) {
|
68
|
+
return false;
|
69
|
+
}
|
70
|
+
if (serializedSchema.type === "array" && typeof src !== "number") {
|
71
|
+
return {
|
72
|
+
[path]: [
|
73
|
+
{
|
74
|
+
message: "Type of value in keyof (array) must be 'number'",
|
75
|
+
},
|
76
|
+
],
|
77
|
+
};
|
78
|
+
}
|
79
|
+
if (serializedSchema.type === "record" && typeof src !== "string") {
|
80
|
+
return {
|
81
|
+
[path]: [
|
82
|
+
{
|
83
|
+
message: "Type of value in keyof (record) must be 'string'",
|
84
|
+
},
|
85
|
+
],
|
86
|
+
};
|
87
|
+
}
|
88
|
+
if (serializedSchema.type === "object") {
|
89
|
+
const keys = Object.keys(serializedSchema.items);
|
90
|
+
if (!keys.includes(src as string)) {
|
91
|
+
return {
|
92
|
+
[path]: [
|
93
|
+
{
|
94
|
+
message: `Value of keyOf (object) must be: ${keys.join(
|
95
|
+
", "
|
96
|
+
)}. Found: ${src}`,
|
97
|
+
},
|
98
|
+
],
|
99
|
+
};
|
100
|
+
}
|
101
|
+
}
|
102
|
+
return false;
|
103
|
+
}
|
104
|
+
|
105
|
+
assert(src: KeyOfSelector<Sel>): boolean {
|
106
|
+
if (this.opt && (src === null || src === undefined)) {
|
107
|
+
return true;
|
108
|
+
}
|
109
|
+
const schema = this.selector[GetSchema];
|
110
|
+
if (!schema) {
|
111
|
+
return false;
|
112
|
+
}
|
113
|
+
const serializedSchema = schema.serialize();
|
114
|
+
|
115
|
+
if (
|
116
|
+
!(
|
117
|
+
serializedSchema.type === "array" ||
|
118
|
+
serializedSchema.type === "object" ||
|
119
|
+
serializedSchema.type === "record"
|
120
|
+
)
|
121
|
+
) {
|
122
|
+
return false;
|
123
|
+
}
|
124
|
+
if (serializedSchema.opt && (src === null || src === undefined)) {
|
125
|
+
return true;
|
126
|
+
}
|
127
|
+
if (serializedSchema.type === "array" && typeof src !== "number") {
|
128
|
+
return false;
|
129
|
+
}
|
130
|
+
if (serializedSchema.type === "record" && typeof src !== "string") {
|
131
|
+
return false;
|
132
|
+
}
|
133
|
+
if (serializedSchema.type === "object") {
|
134
|
+
const keys = Object.keys(serializedSchema.items);
|
135
|
+
if (!keys.includes(src as string)) {
|
136
|
+
return false;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
return true;
|
140
|
+
}
|
141
|
+
|
142
|
+
optional(): Schema<KeyOfSelector<Sel> | null> {
|
143
|
+
return new KeyOfSchema(this.selector, true);
|
144
|
+
}
|
145
|
+
|
146
|
+
serialize(): SerializedSchema {
|
147
|
+
const path = getValPath(this.selector);
|
148
|
+
if (!path) {
|
149
|
+
throw new Error(
|
150
|
+
"Cannot serialize oneOf schema with empty selector. keyOf must be used with a Val Module."
|
151
|
+
);
|
152
|
+
}
|
153
|
+
return {
|
154
|
+
type: "keyOf",
|
155
|
+
selector: path,
|
156
|
+
opt: this.opt,
|
157
|
+
};
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
export const keyOf = <
|
162
|
+
Src extends GenericSelector<SourceArray | SourceObject> & ValModuleBrand // ValModuleBrand enforces call site to pass in a val module - selectors are not allowed. The reason is that this should make it easier to patch. We might be able to relax this constraint in the future
|
163
|
+
>(
|
164
|
+
valModule: Src
|
165
|
+
): Schema<KeyOfSelector<Src>> => {
|
166
|
+
return new KeyOfSchema(valModule);
|
167
|
+
};
|
@@ -0,0 +1,103 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
|
+
import { Schema, SchemaTypeOf, SerializedSchema } from ".";
|
3
|
+
import { initVal } from "../initVal";
|
4
|
+
import { SelectorSource } from "../selector";
|
5
|
+
import { createValPathOfItem } from "../selector/SelectorProxy";
|
6
|
+
import { SourcePath } from "../val";
|
7
|
+
import { string } from "./string";
|
8
|
+
import { ValidationErrors } from "./validation/ValidationError";
|
9
|
+
|
10
|
+
export type SerializedRecordSchema = {
|
11
|
+
type: "record";
|
12
|
+
item: SerializedSchema;
|
13
|
+
opt: boolean;
|
14
|
+
};
|
15
|
+
|
16
|
+
export class RecordSchema<T extends Schema<SelectorSource>> extends Schema<
|
17
|
+
Record<string, SchemaTypeOf<T>>
|
18
|
+
> {
|
19
|
+
constructor(readonly item: T, readonly opt: boolean = false) {
|
20
|
+
super();
|
21
|
+
}
|
22
|
+
|
23
|
+
validate(
|
24
|
+
path: SourcePath,
|
25
|
+
src: Record<string, SchemaTypeOf<T>>
|
26
|
+
): ValidationErrors {
|
27
|
+
let error: ValidationErrors = false;
|
28
|
+
|
29
|
+
if (this.opt && (src === null || src === undefined)) {
|
30
|
+
return false;
|
31
|
+
}
|
32
|
+
|
33
|
+
if (typeof src !== "object") {
|
34
|
+
return {
|
35
|
+
[path]: [{ message: `Expected 'object', got '${typeof src}'` }],
|
36
|
+
} as ValidationErrors;
|
37
|
+
}
|
38
|
+
if (Array.isArray(src)) {
|
39
|
+
return {
|
40
|
+
[path]: [{ message: `Expected 'object', got 'array'` }],
|
41
|
+
} as ValidationErrors;
|
42
|
+
}
|
43
|
+
Object.entries(src).forEach(([key, elem]) => {
|
44
|
+
const subPath = createValPathOfItem(path, key);
|
45
|
+
if (!subPath) {
|
46
|
+
error = this.appendValidationError(
|
47
|
+
error,
|
48
|
+
path,
|
49
|
+
`Internal error: could not create path at ${
|
50
|
+
!path && typeof path === "string" ? "<empty string>" : path
|
51
|
+
} at key ${elem}`, // Should! never happen
|
52
|
+
src
|
53
|
+
);
|
54
|
+
} else {
|
55
|
+
const subError = this.item.validate(subPath, elem as SelectorSource);
|
56
|
+
if (subError && error) {
|
57
|
+
error = {
|
58
|
+
...subError,
|
59
|
+
...error,
|
60
|
+
};
|
61
|
+
} else if (subError) {
|
62
|
+
error = subError;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
});
|
66
|
+
|
67
|
+
return error;
|
68
|
+
}
|
69
|
+
|
70
|
+
assert(src: Record<string, SchemaTypeOf<T>>): boolean {
|
71
|
+
if (this.opt && (src === null || src === undefined)) {
|
72
|
+
return true;
|
73
|
+
}
|
74
|
+
if (!src) {
|
75
|
+
return false;
|
76
|
+
}
|
77
|
+
|
78
|
+
for (const [, item] of Object.entries(src)) {
|
79
|
+
if (!this.item.assert(item)) {
|
80
|
+
return false;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
return typeof src === "object" && !Array.isArray(src);
|
84
|
+
}
|
85
|
+
|
86
|
+
optional(): Schema<Record<string, SchemaTypeOf<T>> | null> {
|
87
|
+
return new RecordSchema(this.item, true);
|
88
|
+
}
|
89
|
+
|
90
|
+
serialize(): SerializedRecordSchema {
|
91
|
+
return {
|
92
|
+
type: "record",
|
93
|
+
item: this.item.serialize(),
|
94
|
+
opt: this.opt,
|
95
|
+
};
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
export const record = <S extends Schema<SelectorSource>>(
|
100
|
+
schema: S
|
101
|
+
): Schema<Record<string, SchemaTypeOf<S>>> => {
|
102
|
+
return new RecordSchema(schema);
|
103
|
+
};
|
package/src/schema/union.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
2
|
import { Schema, SerializedSchema } from ".";
|
3
|
-
import { SelectorSource } from "../selector";
|
3
|
+
import { SelectorSource } from "../selector/index";
|
4
4
|
import { SourceObject } from "../source";
|
5
5
|
import { SourcePath } from "../val";
|
6
6
|
import { ValidationErrors } from "./validation/ValidationError";
|