@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "version": "3.1.7",
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.4"
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 arr = part.indexOf('[') > 0;
58
+ const partArr = part.indexOf('[') > 0;
59
59
  const name = part.split(/[^A-Za-z_0-9]/)[0];
60
- const idx = arr ? part.split(/[\[\]]/)[1] : '';
61
- const key = arr ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined)) : undefined;
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
- if (last.indexOf('[') < 0) {
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 = arr ? last.split(/[\[\]]/)[1] : '';
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
- let key = arr ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined)) : undefined;
88
- if (sub[name] === undefined) {
89
- sub[name] = (typeof key === 'string') ? {} : [];
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
- sub = sub[name] as Record<string, unknown>;
92
- if (key === undefined) {
93
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
94
- key = sub.length as number;
95
- }
96
- if (sub[key] && ObjectUtil.isPlainObject(val) && ObjectUtil.isPlainObject(sub[key])) {
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, unknown> {
113
- const out: Record<string, unknown> = {};
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
- out[pre] = value ?? '';
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
- const path = `${relative}${relative && '.'}${fieldSchema.name}`;
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.#validateFieldSchema(field, params[field.index!]));
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 = SchemaTransformUtil.toConcreteType(state, type, target); break;
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
- SchemaTransformUtil.toConcreteType(state, type, target) :
218
+ this.toConcreteType(state, type, target) :
218
219
  state.factory.createIdentifier(type.ctor.name);
219
220
  }
220
221
  }