@travetto/schema 3.1.7 → 3.1.9
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/package.json +2 -2
- package/src/bind-util.ts +26 -22
- package/src/validate/validator.ts +11 -3
- package/support/transform-util.ts +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/schema",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.9",
|
|
4
4
|
"description": "Data type registry for runtime validation, reflection and binding.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"schema",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"directory": "module/schema"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/registry": "^3.1.
|
|
30
|
+
"@travetto/registry": "^3.1.5"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@travetto/transformer": "^3.1.5"
|
package/src/bind-util.ts
CHANGED
|
@@ -55,10 +55,10 @@ export class BindUtil {
|
|
|
55
55
|
let sub = out;
|
|
56
56
|
while (parts.length > 0) {
|
|
57
57
|
const part = parts.shift()!;
|
|
58
|
-
const
|
|
58
|
+
const partArr = part.indexOf('[') > 0;
|
|
59
59
|
const name = part.split(/[^A-Za-z_0-9]/)[0];
|
|
60
|
-
const idx =
|
|
61
|
-
const key =
|
|
60
|
+
const idx = partArr ? part.split(/[\[\]]/)[1] : '';
|
|
61
|
+
const key = partArr ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined)) : undefined;
|
|
62
62
|
|
|
63
63
|
if (!(name in sub)) {
|
|
64
64
|
sub[name] = typeof key === 'number' ? [] : {};
|
|
@@ -73,31 +73,31 @@ export class BindUtil {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
const arr = last.indexOf('[') > 0;
|
|
77
|
+
|
|
78
|
+
if (!arr) {
|
|
77
79
|
if (sub[last] && ObjectUtil.isPlainObject(val)) {
|
|
78
80
|
sub[last] = DataUtil.deepAssign(sub[last], val, 'coerce');
|
|
79
81
|
} else {
|
|
80
82
|
sub[last] = val;
|
|
81
83
|
}
|
|
82
84
|
} else {
|
|
83
|
-
const arr = last.indexOf('[') > 0;
|
|
84
85
|
const name = last.split(/[^A-Za-z_0-9]/)[0];
|
|
85
|
-
const idx =
|
|
86
|
+
const idx = last.split(/[\[\]]/)[1];
|
|
87
|
+
|
|
88
|
+
let key = (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined));
|
|
89
|
+
sub[name] ??= (typeof key === 'string') ? {} : [];
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
92
|
+
const arrSub = sub[name] as Record<string, unknown>;
|
|
93
|
+
if (key === undefined) {
|
|
90
94
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
sub[key] = DataUtil.deepAssign(sub[key], val, 'coerce');
|
|
98
|
-
} else {
|
|
99
|
-
sub[key] = val;
|
|
100
|
-
}
|
|
95
|
+
key = arrSub.length as number;
|
|
96
|
+
}
|
|
97
|
+
if (arrSub[key] && ObjectUtil.isPlainObject(val) && ObjectUtil.isPlainObject(arrSub[key])) {
|
|
98
|
+
arrSub[key] = DataUtil.deepAssign(arrSub[key], val, 'coerce');
|
|
99
|
+
} else {
|
|
100
|
+
arrSub[key] = val;
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -109,8 +109,8 @@ export class BindUtil {
|
|
|
109
109
|
* @param conf The object to flatten the paths for
|
|
110
110
|
* @param val The starting prefix
|
|
111
111
|
*/
|
|
112
|
-
static flattenPaths(data: Record<string, unknown>, prefix: string = ''): Record<string,
|
|
113
|
-
const out: Record<string,
|
|
112
|
+
static flattenPaths<V extends string = string>(data: Record<string, unknown>, prefix: string = ''): Record<string, V> {
|
|
113
|
+
const out: Record<string, V> = {};
|
|
114
114
|
for (const [key, value] of Object.entries(data)) {
|
|
115
115
|
const pre = `${prefix}${key}`;
|
|
116
116
|
if (ObjectUtil.isPlainObject(value)) {
|
|
@@ -126,7 +126,8 @@ export class BindUtil {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
} else {
|
|
129
|
-
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
130
|
+
out[pre] = (value ?? '') as V;
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
return out;
|
|
@@ -273,6 +274,9 @@ export class BindUtil {
|
|
|
273
274
|
if ((val === undefined || val === null) && applyDefaults) {
|
|
274
275
|
val = field.default;
|
|
275
276
|
}
|
|
277
|
+
if (!field.required && (val === undefined || val === null)) {
|
|
278
|
+
return val;
|
|
279
|
+
}
|
|
276
280
|
const complex = SchemaRegistry.has(field.type);
|
|
277
281
|
if (field.array) {
|
|
278
282
|
const valArr = !Array.isArray(val) ? [val] : val;
|
|
@@ -56,8 +56,16 @@ export class SchemaValidator {
|
|
|
56
56
|
* @param relative The relative path of object traversal
|
|
57
57
|
*/
|
|
58
58
|
static #validateFieldSchema(fieldSchema: FieldConfig, val: unknown, relative: string = ''): ValidationError[] {
|
|
59
|
-
|
|
59
|
+
return this.#validateFieldSchemaRaw(fieldSchema, val, `${relative}${relative ? '.' : ''}${fieldSchema.name}`);
|
|
60
|
+
}
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Validate a single field config against a passed in value
|
|
64
|
+
* @param fieldSchema The field schema configuration
|
|
65
|
+
* @param val The raw value, could be an array or not
|
|
66
|
+
* @param path The current path of validation traversal
|
|
67
|
+
*/
|
|
68
|
+
static #validateFieldSchemaRaw(fieldSchema: FieldConfig, val: unknown, path: string = ''): ValidationError[] {
|
|
61
69
|
const hasValue = !(val === undefined || val === null || (typeof val === 'string' && val === '') || (Array.isArray(val) && val.length === 0));
|
|
62
70
|
|
|
63
71
|
if (!hasValue) {
|
|
@@ -316,10 +324,10 @@ export class SchemaValidator {
|
|
|
316
324
|
* @param method The method being invoked
|
|
317
325
|
* @param params The params to validate
|
|
318
326
|
*/
|
|
319
|
-
static async validateMethod<T>(cls: Class<T>, method: string, params: unknown[]): Promise<void> {
|
|
327
|
+
static async validateMethod<T>(cls: Class<T>, method: string, params: unknown[], prefixes: (string | undefined)[] = []): Promise<void> {
|
|
320
328
|
const errors: ValidationError[] = [];
|
|
321
329
|
for (const field of SchemaRegistry.getMethodSchema(cls, method)) {
|
|
322
|
-
errors.push(...this.#
|
|
330
|
+
errors.push(...this.#validateFieldSchemaRaw(field, params[field.index!], prefixes[field.index!]));
|
|
323
331
|
}
|
|
324
332
|
if (errors.length) {
|
|
325
333
|
throw new ValidationResultError(errors);
|
|
@@ -111,7 +111,8 @@ export class SchemaTransformUtil {
|
|
|
111
111
|
// If we have a union type
|
|
112
112
|
if (typeExpr.key === 'union') {
|
|
113
113
|
const values = typeExpr.subTypes.map(x => x.key === 'literal' ? x.value : undefined)
|
|
114
|
-
.filter(x => x !== undefined && x !== null)
|
|
114
|
+
.filter(x => x !== undefined && x !== null)
|
|
115
|
+
.sort();
|
|
115
116
|
|
|
116
117
|
if (values.length === typeExpr.subTypes.length) {
|
|
117
118
|
attrs.push(state.factory.createPropertyAssignment('enum', state.fromLiteral({
|
|
@@ -210,11 +211,11 @@ export class SchemaTransformUtil {
|
|
|
210
211
|
const { out, type } = this.unwrapType(anyType);
|
|
211
212
|
switch (type?.key) {
|
|
212
213
|
case 'managed': out.type = state.typeToIdentifier(type); break;
|
|
213
|
-
case 'shape': out.type =
|
|
214
|
+
case 'shape': out.type = this.toConcreteType(state, type, target); break;
|
|
214
215
|
case 'literal': {
|
|
215
216
|
if (type.ctor) {
|
|
216
217
|
out.type = out.array ?
|
|
217
|
-
|
|
218
|
+
this.toConcreteType(state, type, target) :
|
|
218
219
|
state.factory.createIdentifier(type.ctor.name);
|
|
219
220
|
}
|
|
220
221
|
}
|