@travetto/schema 7.0.0-rc.1 → 7.0.0-rc.2

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/README.md CHANGED
@@ -64,28 +64,28 @@ User:
64
64
 
65
65
  ### Fields
66
66
  This schema provides a powerful base for data binding and validation at runtime. Additionally there may be types that cannot be detected, or some information that the programmer would like to override. Below are the supported field decorators:
67
- * [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L21) defines a field that will be serialized.
68
- * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L47) defines a that field should be required
69
- * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L56) defines the allowable values that a field can have
70
- * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L82) defines a regular expression that the field value should match
71
- * [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L91) enforces min length of a string
72
- * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L102) enforces max length of a string
73
- * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L91) enforces min value for a date or a number
74
- * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L102) enforces max value for a date or a number
75
- * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L132) ensures string field matches basic email regex
76
- * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L140) ensures string field matches basic telephone regex
77
- * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L148) ensures string field matches basic url regex
67
+ * [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L23) defines a field that will be serialized.
68
+ * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L49) defines a that field should be required
69
+ * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L58) defines the allowable values that a field can have
70
+ * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L84) defines a regular expression that the field value should match
71
+ * [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L93) enforces min length of a string
72
+ * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L104) enforces max length of a string
73
+ * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L93) enforces min value for a date or a number
74
+ * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L104) enforces max value for a date or a number
75
+ * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L134) ensures string field matches basic email regex
76
+ * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L142) ensures string field matches basic telephone regex
77
+ * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L150) ensures string field matches basic url regex
78
78
  * [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/common.ts#L41) exclude from auto schema registration
79
- * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L164) ensures number passed in is only a whole number
80
- * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L171) ensures number passed in allows fractional values
81
- * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L185) provides support for standard currency
82
- * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L66) indicates that a field is expecting natural language input, not just discrete values
83
- * [@LongText](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L73) same as text, but expects longer form content
84
- * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L37) defines a that field should not be bindable external to the class
85
- * [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L30) defines a that field should not be exported in serialization, but that it can be bound to
86
- * [@Secret](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L45) marks a field as being sensitive. This is used by certain logging activities to ensure sensitive information is not logged out.
87
- * [@Specifier](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L193) attributes additional specifiers to a field, allowing for more specification beyond just the field's type.
88
- * [@DiscriminatorField](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L200) allows for promoting a given field as the owner of the sub type discriminator.
79
+ * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L166) ensures number passed in is only a whole number
80
+ * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L173) ensures number passed in allows fractional values
81
+ * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L187) provides support for standard currency
82
+ * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L68) indicates that a field is expecting natural language input, not just discrete values
83
+ * [@LongText](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L75) same as text, but expects longer form content
84
+ * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L39) defines a that field should not be bindable external to the class
85
+ * [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L32) defines a that field should not be exported in serialization, but that it can be bound to
86
+ * [@Secret](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L47) marks a field as being sensitive. This is used by certain logging activities to ensure sensitive information is not logged out.
87
+ * [@Specifier](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L195) attributes additional specifiers to a field, allowing for more specification beyond just the field's type.
88
+ * [@DiscriminatorField](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L202) allows for promoting a given field as the owner of the sub type discriminator.
89
89
 
90
90
  Additionally, schemas can be nested to form more complex data structures that are able to bound and validated.
91
91
 
@@ -249,11 +249,11 @@ Within the schema framework, it is possible to add custom validators class level
249
249
  import { Schema, Validator, ValidationError } from '@travetto/schema';
250
250
 
251
251
  const passwordValidator = (user: User): ValidationError | undefined => {
252
- const p = user.password;
253
- const hasNum = /\d/.test(p);
254
- const hasSpecial = /[!@#$%%^&*()<>?/,.;':"']/.test(p);
255
- const noRepeat = !/(.)(\1)/.test(p);
256
- if (!hasNum || !hasSpecial || !noRepeat) {
252
+ const password = user.password;
253
+ const hasNumber = /\d/.test(password);
254
+ const hasSpecial = /[!@#$%%^&*()<>?/,.;':"']/.test(password);
255
+ const noRepeat = !/(.)(\1)/.test(password);
256
+ if (!hasNumber || !hasSpecial || !noRepeat) {
257
257
  return {
258
258
  kind: 'password-rules',
259
259
  path: 'password',
@@ -322,7 +322,7 @@ This feature is meant to allow for simple Typescript types to be able to be back
322
322
  /**
323
323
  * Geometric Point as [number,number] with validation and binding support
324
324
  *
325
- * @concrete ./internal/types.ts#PointImpl
325
+ * @concrete ./internal/types.ts#PointImplementation
326
326
  */
327
327
  export type Point = [number, number];
328
328
  ```
@@ -336,7 +336,7 @@ const InvalidSymbol = Symbol();
336
336
  /**
337
337
  * Point Implementation
338
338
  */
339
- export class PointImpl {
339
+ export class PointImplementation {
340
340
 
341
341
  /**
342
342
  * Validate we have an actual point
@@ -351,7 +351,7 @@ export class PointImpl {
351
351
  */
352
352
  static bindSchema(input: unknown): [number, number] | typeof InvalidSymbol | undefined {
353
353
  if (Array.isArray(input) && input.length === 2) {
354
- const [a, b] = input.map(x => DataUtil.coerceType(x, Number, false));
354
+ const [a, b] = input.map(value => DataUtil.coerceType(value, Number, false));
355
355
  return [a, b];
356
356
  } else {
357
357
  return InvalidSymbol;
@@ -359,7 +359,7 @@ export class PointImpl {
359
359
  }
360
360
  }
361
361
 
362
- Object.defineProperty(PointImpl, 'name', { value: 'Point' });
362
+ Object.defineProperty(PointImplementation, 'name', { value: 'Point' });
363
363
  ```
364
364
 
365
365
  What you can see here is that the `Point` type is now backed by a class that supports:
@@ -410,4 +410,4 @@ Data utilities for binding values, and type conversion. Currently [DataUtil](htt
410
410
 
411
411
  * `coerceType(input: unknown, type: Class<unknown>, strict = true)` which allows for converting an input type into a specified `type` instance, or throw an error if the types are incompatible.
412
412
  * `shallowClone<T = unknown>(a: T): T` will shallowly clone a field
413
- * `filterByKeys<T>(obj: T, exclude: (string | RegExp)[]): T` will filter a given object, and return a plain object (if applicable) with fields excluded using the values in the `exclude` input
413
+ * `filterByKeys<T>(input: T, exclude: (string | RegExp)[]): T` will filter a given object, and return a plain object (if applicable) with fields excluded using the values in the `exclude` input
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "version": "7.0.0-rc.1",
3
+ "version": "7.0.0-rc.2",
4
4
  "description": "Data type registry for runtime validation, reflection and binding.",
5
5
  "keywords": [
6
6
  "schema",
@@ -27,10 +27,10 @@
27
27
  "directory": "module/schema"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/registry": "^7.0.0-rc.1"
30
+ "@travetto/registry": "^7.0.0-rc.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^7.0.0-rc.1"
33
+ "@travetto/transformer": "^7.0.0-rc.2"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
package/src/bind-util.ts CHANGED
@@ -10,8 +10,8 @@ type BindConfig = {
10
10
  filterValue?: (value: unknown, input: SchemaInputConfig) => boolean;
11
11
  };
12
12
 
13
- function isInstance<T>(o: unknown): o is T {
14
- return !!o && !DataUtil.isPrimitive(o);
13
+ function isInstance<T>(value: unknown): value is T {
14
+ return !!value && !DataUtil.isPrimitive(value);
15
15
  }
16
16
 
17
17
  /**
@@ -21,24 +21,24 @@ export class BindUtil {
21
21
 
22
22
  /**
23
23
  * Coerce a value to match the field config type
24
- * @param conf The field config to coerce to
25
- * @param val The provided value
24
+ * @param config The field config to coerce to
25
+ * @param value The provided value
26
26
  */
27
- static #coerceType<T>(conf: SchemaInputConfig, val: unknown): T | null | undefined {
28
- if (conf.type?.bindSchema) {
29
- val = conf.type.bindSchema(val);
27
+ static #coerceType<T>(config: SchemaInputConfig, value: unknown): T | null | undefined {
28
+ if (config.type?.bindSchema) {
29
+ value = config.type.bindSchema(value);
30
30
  } else {
31
- val = DataUtil.coerceType(val, conf.type, false);
31
+ value = DataUtil.coerceType(value, config.type, false);
32
32
 
33
- if (conf.type === Number && conf.precision && typeof val === 'number') {
34
- if (conf.precision[1]) { // Supports decimal
35
- val = +val.toFixed(conf.precision[1]);
33
+ if (config.type === Number && config.precision && typeof value === 'number') {
34
+ if (config.precision[1]) { // Supports decimal
35
+ value = +value.toFixed(config.precision[1]);
36
36
  } else { // 0 digits
37
- val = Math.trunc(val);
37
+ value = Math.trunc(value);
38
38
  }
39
39
  }
40
40
  }
41
- return castTo(val);
41
+ return castTo(value);
42
42
  }
43
43
 
44
44
  /**
@@ -46,22 +46,22 @@ export class BindUtil {
46
46
  *
47
47
  * This will convert `{ 'a.b[3].c[age]': 5 }` => `{ a : { b : [,,,{ c: { age: 5 }}]}}`
48
48
  *
49
- * @param obj The object to convert
49
+ * @param input The object to convert
50
50
  */
51
- static expandPaths(obj: Record<string, unknown>): Record<string, unknown> {
51
+ static expandPaths(input: Record<string, unknown>): Record<string, unknown> {
52
52
  const out: Record<string, unknown> = {};
53
- for (const k of Object.keys(obj)) {
54
- const objK = obj[k];
55
- const val = DataUtil.isPlainObject(objK) ? this.expandPaths(objK) : objK;
56
- const parts = k.split('.');
53
+ for (const property of Object.keys(input)) {
54
+ const valueInput = input[property];
55
+ const value = DataUtil.isPlainObject(valueInput) ? this.expandPaths(valueInput) : valueInput;
56
+ const parts = property.split('.');
57
57
  const last = parts.pop()!;
58
58
  let sub = out;
59
59
  while (parts.length > 0) {
60
60
  const part = parts.shift()!;
61
- const partArr = part.indexOf('[') > 0;
61
+ const partArrayIndex = part.indexOf('[') > 0;
62
62
  const name = part.split(/[^A-Za-z_0-9]/)[0];
63
- const idx = partArr ? part.split(/[\[\]]/)[1] : '';
64
- const key = partArr ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined)) : undefined;
63
+ const idx = partArrayIndex ? part.split(/[\[\]]/)[1] : '';
64
+ const key = partArrayIndex ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined)) : undefined;
65
65
 
66
66
  if (!(name in sub)) {
67
67
  sub[name] = typeof key === 'number' ? [] : {};
@@ -77,10 +77,10 @@ export class BindUtil {
77
77
  const arr = last.indexOf('[') > 0;
78
78
 
79
79
  if (!arr) {
80
- if (sub[last] && DataUtil.isPlainObject(val)) {
81
- sub[last] = DataUtil.deepAssign(sub[last], val, 'coerce');
80
+ if (sub[last] && DataUtil.isPlainObject(value)) {
81
+ sub[last] = DataUtil.deepAssign(sub[last], value, 'coerce');
82
82
  } else {
83
- sub[last] = val;
83
+ sub[last] = value;
84
84
  }
85
85
  } else {
86
86
  const name = last.split(/[^A-Za-z_0-9]/)[0];
@@ -93,10 +93,10 @@ export class BindUtil {
93
93
  if (key === undefined) {
94
94
  key = arrSub.length;
95
95
  }
96
- if (arrSub[key] && DataUtil.isPlainObject(val) && DataUtil.isPlainObject(arrSub[key])) {
97
- arrSub[key] = DataUtil.deepAssign(arrSub[key], val, 'coerce');
96
+ if (arrSub[key] && DataUtil.isPlainObject(value) && DataUtil.isPlainObject(arrSub[key])) {
97
+ arrSub[key] = DataUtil.deepAssign(arrSub[key], value, 'coerce');
98
98
  } else {
99
- arrSub[key] = val;
99
+ arrSub[key] = value;
100
100
  }
101
101
  }
102
102
  }
@@ -105,8 +105,8 @@ export class BindUtil {
105
105
 
106
106
  /**
107
107
  * Convert full object with nesting, into flat set of keys
108
- * @param conf The object to flatten the paths for
109
- * @param val The starting prefix
108
+ * @param data The object to flatten the paths for
109
+ * @param prefix The starting prefix
110
110
  */
111
111
  static flattenPaths<V extends string = string>(data: Record<string, unknown>, prefix: string = ''): Record<string, V> {
112
112
  const out: Record<string, V> = {};
@@ -117,11 +117,11 @@ export class BindUtil {
117
117
  );
118
118
  } else if (Array.isArray(value)) {
119
119
  for (let i = 0; i < value.length; i++) {
120
- const v = value[i];
121
- if (DataUtil.isPlainObject(v)) {
122
- Object.assign(out, this.flattenPaths(v, `${pre}[${i}].`));
120
+ const element = value[i];
121
+ if (DataUtil.isPlainObject(element)) {
122
+ Object.assign(out, this.flattenPaths(element, `${pre}[${i}].`));
123
123
  } else {
124
- out[`${pre}[${i}]`] = v ?? '';
124
+ out[`${pre}[${i}]`] = element ?? '';
125
125
  }
126
126
  }
127
127
  } else {
@@ -135,12 +135,12 @@ export class BindUtil {
135
135
  * Bind data to the schema for a class, with an optional view
136
136
  * @param cls The schema class to bind against
137
137
  * @param data The provided data to bind
138
- * @param cfg The bind configuration
138
+ * @param config The bind configuration
139
139
  */
140
- static bindSchema<T>(cls: Class<T>, data?: undefined, cfg?: BindConfig): undefined;
141
- static bindSchema<T>(cls: Class<T>, data?: null, cfg?: BindConfig): null;
142
- static bindSchema<T>(cls: Class<T>, data?: object | T, cfg?: BindConfig): T;
143
- static bindSchema<T>(cls: Class<T>, data?: object | T, cfg?: BindConfig): T | null | undefined {
140
+ static bindSchema<T>(cls: Class<T>, data?: undefined, config?: BindConfig): undefined;
141
+ static bindSchema<T>(cls: Class<T>, data?: null, config?: BindConfig): null;
142
+ static bindSchema<T>(cls: Class<T>, data?: object | T, config?: BindConfig): T;
143
+ static bindSchema<T>(cls: Class<T>, data?: object | T, config?: BindConfig): T | null | undefined {
144
144
  if (data === null || data === undefined) {
145
145
  return data;
146
146
  }
@@ -150,13 +150,13 @@ export class BindUtil {
150
150
  const resolvedCls = SchemaRegistryIndex.resolveInstanceType<T>(cls, asFull<T>(data));
151
151
  const instance = classConstruct<T & { type?: string }>(resolvedCls);
152
152
 
153
- for (const k of TypedObject.keys(instance)) { // Do not retain undefined fields
154
- if (instance[k] === undefined) {
155
- delete instance[k];
153
+ for (const key of TypedObject.keys(instance)) { // Do not retain undefined fields
154
+ if (instance[key] === undefined) {
155
+ delete instance[key];
156
156
  }
157
157
  }
158
158
 
159
- const out = this.bindSchemaToObject(resolvedCls, instance, data, cfg);
159
+ const out = this.bindSchemaToObject(resolvedCls, instance, data, config);
160
160
  SchemaRegistryIndex.get(resolvedCls).ensureInstanceTypeField(out);
161
161
  return out;
162
162
  }
@@ -165,35 +165,35 @@ export class BindUtil {
165
165
  /**
166
166
  * Bind the schema to the object
167
167
  * @param cls The schema class
168
- * @param obj The target object (instance of cls)
168
+ * @param input The target object (instance of cls)
169
169
  * @param data The data to bind
170
- * @param cfg The bind configuration
170
+ * @param config The bind configuration
171
171
  */
172
- static bindSchemaToObject<T>(cls: Class<T>, obj: T, data?: object, cfg: BindConfig = {}): T {
173
- const view = cfg.view; // Does not convey
174
- delete cfg.view;
172
+ static bindSchemaToObject<T>(cls: Class<T>, input: T, data?: object, config: BindConfig = {}): T {
173
+ const view = config.view; // Does not convey
174
+ delete config.view;
175
175
 
176
176
  if (!!data && isInstance<T>(data)) {
177
177
  const adapter = SchemaRegistryIndex.get(cls);
178
- const conf = adapter.get();
178
+ const schemaConfig = adapter.get();
179
179
 
180
180
  // If no configuration
181
- if (!conf) {
182
- for (const k of TypedObject.keys(data)) {
183
- obj[k] = data[k];
181
+ if (!schemaConfig) {
182
+ for (const key of TypedObject.keys(data)) {
183
+ input[key] = data[key];
184
184
  }
185
185
  } else {
186
- let schema: SchemaFieldMap = conf.fields;
186
+ let schema: SchemaFieldMap = schemaConfig.fields;
187
187
  if (view) {
188
188
  schema = adapter.getFields(view);
189
189
  if (!schema) {
190
- throw new Error(`View not found: ${view.toString()}`);
190
+ throw new Error(`View not found: ${view}`);
191
191
  }
192
192
  }
193
193
 
194
194
  for (const [schemaFieldName, field] of Object.entries(schema)) {
195
195
  let inboundField: string | undefined = undefined;
196
- if (field.access === 'readonly' || cfg.filterInput?.(field) === false) {
196
+ if (field.access === 'readonly' || config.filterInput?.(field) === false) {
197
197
  continue; // Skip trying to write readonly fields
198
198
  }
199
199
  if (schemaFieldName in data) {
@@ -211,40 +211,40 @@ export class BindUtil {
211
211
  continue;
212
212
  }
213
213
 
214
- let v: unknown = data[castKey<T>(inboundField)];
214
+ let value: unknown = data[castKey<T>(inboundField)];
215
215
 
216
216
  // Filtering values
217
- if (cfg.filterValue && !cfg.filterValue(v, field)) {
217
+ if (config.filterValue && !config.filterValue(value, field)) {
218
218
  continue;
219
219
  }
220
220
 
221
- if (v !== undefined && v !== null) {
221
+ if (value !== undefined && value !== null) {
222
222
  // Ensure its an array
223
- if (!Array.isArray(v) && field.array) {
224
- if (typeof v === 'string' && v.includes(',')) {
225
- v = v.split(/,/).map(x => x.trim());
223
+ if (!Array.isArray(value) && field.array) {
224
+ if (typeof value === 'string' && value.includes(',')) {
225
+ value = value.split(/,/).map(part => part.trim());
226
226
  } else {
227
- v = [v];
227
+ value = [value];
228
228
  }
229
229
  }
230
230
 
231
231
  if (SchemaRegistryIndex.has(field.type)) {
232
- if (field.array && Array.isArray(v)) {
233
- v = v.map(el => this.bindSchema(field.type, el, cfg));
232
+ if (field.array && Array.isArray(value)) {
233
+ value = value.map(item => this.bindSchema(field.type, item, config));
234
234
  } else {
235
- v = this.bindSchema(field.type, v, cfg);
235
+ value = this.bindSchema(field.type, value, config);
236
236
  }
237
- } else if (field.array && Array.isArray(v)) {
238
- v = v.map(el => this.#coerceType(field, el));
237
+ } else if (field.array && Array.isArray(value)) {
238
+ value = value.map(item => this.#coerceType(field, item));
239
239
  } else {
240
- v = this.#coerceType(field, v);
240
+ value = this.#coerceType(field, value);
241
241
  }
242
242
  }
243
243
 
244
- obj[castKey<T>(schemaFieldName)] = castTo(v);
244
+ input[castKey<T>(schemaFieldName)] = castTo(value);
245
245
 
246
246
  if (field.accessor) {
247
- Object.defineProperty(obj, schemaFieldName, {
247
+ Object.defineProperty(input, schemaFieldName, {
248
248
  ...adapter.getAccessorDescriptor(schemaFieldName),
249
249
  enumerable: true
250
250
  });
@@ -253,40 +253,40 @@ export class BindUtil {
253
253
  }
254
254
  }
255
255
 
256
- return obj;
256
+ return input;
257
257
  }
258
258
 
259
259
  /**
260
260
  * Coerce field to type
261
- * @param cfg
262
- * @param val
261
+ * @param config
262
+ * @param value
263
263
  * @param applyDefaults
264
264
  * @returns
265
265
  */
266
- static coerceInput(cfg: SchemaInputConfig, val: unknown, applyDefaults = false): unknown {
267
- if ((val === undefined || val === null) && applyDefaults) {
268
- val = Array.isArray(cfg.default) ? cfg.default.slice(0) : cfg.default;
266
+ static coerceInput(config: SchemaInputConfig, value: unknown, applyDefaults = false): unknown {
267
+ if ((value === undefined || value === null) && applyDefaults) {
268
+ value = Array.isArray(config.default) ? config.default.slice(0) : config.default;
269
269
  }
270
- if (cfg.required?.active === false && (val === undefined || val === null)) {
271
- return val;
270
+ if (config.required?.active === false && (value === undefined || value === null)) {
271
+ return value;
272
272
  }
273
- const complex = SchemaRegistryIndex.has(cfg.type);
274
- const bindCfg: BindConfig | undefined = (complex && 'view' in cfg && typeof cfg.view === 'string') ? { view: cfg.view } : undefined;
275
- if (cfg.array) {
276
- const valArr = !Array.isArray(val) ? [val] : val;
273
+ const complex = SchemaRegistryIndex.has(config.type);
274
+ const bindConfig: BindConfig | undefined = (complex && 'view' in config && typeof config.view === 'string') ? { view: config.view } : undefined;
275
+ if (config.array) {
276
+ const subValue = !Array.isArray(value) ? [value] : value;
277
277
  if (complex) {
278
- val = valArr.map(x => this.bindSchema(cfg.type, x, bindCfg));
278
+ value = subValue.map(item => this.bindSchema(config.type, item, bindConfig));
279
279
  } else {
280
- val = valArr.map(x => DataUtil.coerceType(x, cfg.type, false));
280
+ value = subValue.map(item => DataUtil.coerceType(item, config.type, false));
281
281
  }
282
282
  } else {
283
283
  if (complex) {
284
- val = this.bindSchema(cfg.type, val, bindCfg);
284
+ value = this.bindSchema(config.type, value, bindConfig);
285
285
  } else {
286
- val = DataUtil.coerceType(val, cfg.type, false);
286
+ value = DataUtil.coerceType(value, config.type, false);
287
287
  }
288
288
  }
289
- return val;
289
+ return value;
290
290
  }
291
291
 
292
292
  /**
@@ -298,8 +298,8 @@ export class BindUtil {
298
298
  static coerceParameters(fields: SchemaParameterConfig[], params: unknown[], applyDefaults = true): unknown[] {
299
299
  params = [...params];
300
300
  // Coerce types
301
- for (const el of fields) {
302
- params[el.index!] = this.coerceInput(el, params[el.index!], applyDefaults);
301
+ for (const field of fields) {
302
+ params[field.index!] = this.coerceInput(field, params[field.index!], applyDefaults);
303
303
  }
304
304
  return params;
305
305
  }
@@ -311,7 +311,7 @@ export class BindUtil {
311
311
  * @param params
312
312
  * @returns
313
313
  */
314
- static coerceMethodParams<T>(cls: Class<T>, method: string | symbol, params: unknown[], applyDefaults = true): unknown[] {
314
+ static coerceMethodParams<T>(cls: Class<T>, method: string, params: unknown[], applyDefaults = true): unknown[] {
315
315
  const paramConfigs = SchemaRegistryIndex.get(cls).getMethod(method).parameters;
316
316
  return this.coerceParameters(paramConfigs, params, applyDefaults);
317
317
  }
package/src/data.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { isNumberObject as isNum, isBooleanObject as isBool, isStringObject as isStr } from 'node:util/types';
1
+ import { isNumberObject, isBooleanObject, isStringObject } from 'node:util/types';
2
2
 
3
3
  import { asConstructable, castTo, Class, asFull, TypedObject } from '@travetto/runtime';
4
4
  import { UnknownType } from './types.ts';
@@ -12,24 +12,26 @@ export class DataUtil {
12
12
 
13
13
  /**
14
14
  * Is a value a plain JS object, created using {}
15
- * @param obj Object to check
15
+ * @param value Object to check
16
16
  */
17
- static isPlainObject(obj: unknown): obj is Record<string, unknown> {
18
- return typeof obj === 'object' // separate from primitives
19
- && obj !== undefined
20
- && obj !== null // is obvious
21
- && obj.constructor === Object // separate instances (Array, DOM, ...)
22
- && Object.prototype.toString.call(obj) === '[object Object]'; // separate build-in like Math
17
+ static isPlainObject(value: unknown): value is Record<string, unknown> {
18
+ return typeof value === 'object' // separate from primitives
19
+ && value !== undefined
20
+ && value !== null // is obvious
21
+ && value.constructor === Object // separate instances (Array, DOM, ...)
22
+ && Object.prototype.toString.call(value) === '[object Object]'; // separate build-in like Math
23
23
  }
24
24
 
25
25
  /**
26
26
  * Is a value of primitive type
27
- * @param el Value to check
27
+ * @param value Value to check
28
28
  */
29
- static isPrimitive(el: unknown): el is (string | boolean | number | RegExp) {
30
- switch (typeof el) {
29
+ static isPrimitive(value: unknown): value is (string | boolean | number | RegExp) {
30
+ switch (typeof value) {
31
31
  case 'string': case 'boolean': case 'number': case 'bigint': return true;
32
- case 'object': return !!el && (el instanceof RegExp || el instanceof Date || isStr(el) || isNum(el) || isBool(el));
32
+ case 'object': return !!value && (
33
+ value instanceof RegExp || value instanceof Date || isStringObject(value) || isNumberObject(value) || isBooleanObject(value)
34
+ );
33
35
  default: return false;
34
36
  }
35
37
  }
@@ -37,8 +39,8 @@ export class DataUtil {
37
39
  /**
38
40
  * Is simple, as a primitive, function or class
39
41
  */
40
- static isSimpleValue(a: unknown): a is Function | Class | string | number | RegExp | Date {
41
- return this.isPrimitive(a) || typeof a === 'function';
42
+ static isSimpleValue(value: unknown): value is Function | Class | string | number | RegExp | Date {
43
+ return this.isPrimitive(value) || typeof value === 'function';
42
44
  }
43
45
 
44
46
  static #deepAssignRaw(a: unknown, b: unknown, mode: 'replace' | 'loose' | 'strict' | 'coerce' = 'loose'): unknown {
@@ -68,10 +70,10 @@ export class DataUtil {
68
70
  if (mode === 'replace') {
69
71
  value = b;
70
72
  } else {
71
- const retArr: unknown[] = castTo(value);
72
- const bArr = b;
73
- for (let i = 0; i < bArr.length; i++) {
74
- retArr[i] = this.#deepAssignRaw(retArr[i], bArr[i], mode);
73
+ const valueArray: unknown[] = castTo(value);
74
+ const bArray = b;
75
+ for (let i = 0; i < bArray.length; i++) {
76
+ valueArray[i] = this.#deepAssignRaw(valueArray[i], bArray[i], mode);
75
77
  }
76
78
  }
77
79
  } else if (isSimpB) { // Scalars
@@ -87,14 +89,14 @@ export class DataUtil {
87
89
  }
88
90
  } else { // Object merge
89
91
  value = a;
90
- const bObj: Record<string, unknown> = castTo(b);
91
- const retObj: Record<string, unknown> = castTo(value);
92
+ const bObject: Record<string, unknown> = castTo(b);
93
+ const valueObject: Record<string, unknown> = castTo(value);
92
94
 
93
- for (const key of Object.keys(bObj)) {
95
+ for (const key of Object.keys(bObject)) {
94
96
  if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
95
97
  continue;
96
98
  }
97
- retObj[key] = this.#deepAssignRaw(retObj[key], bObj[key], mode);
99
+ valueObject[key] = this.#deepAssignRaw(valueObject[key], bObject[key], mode);
98
100
  }
99
101
  }
100
102
  }
@@ -119,7 +121,7 @@ export class DataUtil {
119
121
  /**
120
122
  * Coerce an input of any type to the class provided
121
123
  * @param input Input value
122
- * @param type Class to coerce to (String, Boolean, Number, Date, RegEx, Object)
124
+ * @param type Class to coerce to (String, Boolean, Number, Date, RegExp, Object)
123
125
  * @param strict Should a failure to coerce throw an error?
124
126
  */
125
127
  static coerceType(input: unknown, type: typeof String, strict?: boolean): string;
@@ -226,10 +228,10 @@ export class DataUtil {
226
228
 
227
229
  /**
228
230
  * Clone top level properties to a new object
229
- * @param o Object to clone
231
+ * @param value Object to clone
230
232
  */
231
- static shallowClone<T>(a: T): T {
232
- return castTo(Array.isArray(a) ? a.slice(0) : (this.isSimpleValue(a) ? a : { ...castTo<object>(a) }));
233
+ static shallowClone<T>(value: T): T {
234
+ return castTo(Array.isArray(value) ? value.slice(0) : (this.isSimpleValue(value) ? value : { ...castTo<object>(value) }));
233
235
  }
234
236
 
235
237
  /**
@@ -247,29 +249,28 @@ export class DataUtil {
247
249
 
248
250
  /**
249
251
  * Filter object by excluding specific keys
250
- * @param obj A value to filter, primitives will be untouched
252
+ * @param input A value to filter, primitives will be untouched
251
253
  * @param exclude Strings or patterns to exclude against
252
254
  * @returns
253
255
  */
254
- static filterByKeys<T>(obj: T, exclude: (string | RegExp)[]): T {
255
- if (Array.isArray(obj)) {
256
- return castTo(obj.map(x => this.filterByKeys(x, exclude)));
257
- } else if (obj !== null && obj !== undefined && typeof obj === 'object') {
256
+ static filterByKeys<T>(input: T, exclude: (string | RegExp)[]): T {
257
+ if (Array.isArray(input)) {
258
+ return castTo(input.map(value => this.filterByKeys(value, exclude)));
259
+ } else if (input !== null && input !== undefined && typeof input === 'object') {
258
260
  const out: Partial<T> = {};
259
- for (const key of TypedObject.keys(obj)) {
260
- if (!exclude.some(r => typeof key === 'string' && (typeof r === 'string' ? r === key : r.test(key)))) {
261
- const val = obj[key];
262
- if (typeof val === 'object') {
263
- out[key] = this.filterByKeys(val, exclude);
261
+ for (const key of TypedObject.keys(input)) {
262
+ if (!exclude.some(toMatch => typeof key === 'string' && (typeof toMatch === 'string' ? toMatch === key : toMatch.test(key)))) {
263
+ const value = input[key];
264
+ if (typeof value === 'object') {
265
+ out[key] = this.filterByKeys(value, exclude);
264
266
  } else {
265
- out[key] = val;
267
+ out[key] = value;
266
268
  }
267
269
  }
268
270
  }
269
271
  return asFull(out);
270
272
  } else {
271
- return obj;
273
+ return input;
272
274
  }
273
275
  }
274
-
275
276
  }