@travetto/schema 3.0.3 → 3.1.0-rc.1

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
@@ -13,7 +13,7 @@ npm install @travetto/schema
13
13
  yarn add @travetto/schema
14
14
  ```
15
15
 
16
- This module's purpose is to allow for proper declaration and validation of data types, in the course of running a program. The framework defined here, is leveraged in the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support"), [Application](https://github.com/travetto/travetto/tree/main/module/app#readme "Application registration/management and run support."), [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module."), [OpenAPI Specification](https://github.com/travetto/travetto/tree/main/module/openapi#readme "OpenAPI integration support for the Travetto framework") and [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") modules. The schema is the backbone of all data transfer, as it helps to provide validation on correctness of input, whether it is a rest request, command line inputs, or a configuration file.
16
+ This module's purpose is to allow for proper declaration and validation of data types, in the course of running a program. The framework defined here, is leveraged in the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support"), [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework"), [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module."), [OpenAPI Specification](https://github.com/travetto/travetto/tree/main/module/openapi#readme "OpenAPI integration support for the Travetto framework") and [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") modules. The schema is the backbone of all data transfer, as it helps to provide validation on correctness of input, whether it is a rest request, command line inputs, or a configuration file.
17
17
 
18
18
  This module provides a mechanism for registering classes and field level information as well the ability to apply that information at runtime.
19
19
 
@@ -203,12 +203,13 @@ Validation Failed {
203
203
  "errors": [
204
204
  {
205
205
  "kind": "type",
206
+ "type": "number",
206
207
  "message": "age is not a valid number",
207
- "path": "age",
208
- "type": "number"
208
+ "path": "age"
209
209
  },
210
210
  {
211
211
  "kind": "required",
212
+ "active": true,
212
213
  "message": "address.street2 is required",
213
214
  "path": "address.street2"
214
215
  }
@@ -269,6 +270,10 @@ export interface ValidationError {
269
270
  * Regular expression to match
270
271
  */
271
272
  re?: string;
273
+ /**
274
+ * Number to compare against
275
+ */
276
+ n?: number | Date;
272
277
  /**
273
278
  * The type of the field
274
279
  */
@@ -341,9 +346,9 @@ Validation Failed {
341
346
  "errors": [
342
347
  {
343
348
  "kind": "type",
349
+ "type": "PointImpl",
344
350
  "message": "point is not a valid PointImpl",
345
- "path": "point",
346
- "type": "PointImpl"
351
+ "path": "point"
347
352
  }
348
353
  ]
349
354
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "version": "3.0.3",
3
+ "version": "3.1.0-rc.1",
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": "^3.0.3"
30
+ "@travetto/registry": "^3.1.0-rc.0"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^3.0.3"
33
+ "@travetto/transformer": "^3.1.0-rc.0"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
@@ -16,9 +16,10 @@ export function Describe(config: Partial<DescribableConfig>) {
16
16
  return (target: Class | ClassInstance, property?: string, descOrIdx?: PropertyDescriptor | number): void => {
17
17
  if (isClassInstance(target, property)) {
18
18
  if (descOrIdx !== undefined && typeof descOrIdx === 'number') {
19
- property = `${property}.${descOrIdx}`;
19
+ SchemaRegistry.registerPendingParamFacet(target.constructor, property!, descOrIdx, config);
20
+ } else {
21
+ SchemaRegistry.registerPendingFieldFacet(target.constructor, property!, config);
20
22
  }
21
- SchemaRegistry.registerPendingFieldFacet(target.constructor, property!, config);
22
23
  } else {
23
24
  SchemaRegistry.register(target, config);
24
25
  }
@@ -28,7 +28,6 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
28
28
  #subTypes = new Map<Class, Map<string, Class>>();
29
29
  #typeKeys = new Map<Class, string>();
30
30
  #pendingViews = new Map<Class, Map<string, ViewFieldsConfig<unknown>>>();
31
- #methodSchemas = new Map<Class, Map<string, FieldConfig[]>>();
32
31
 
33
32
  constructor() {
34
33
  super(RootRegistry);
@@ -191,6 +190,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
191
190
  validators: [],
192
191
  subType: false,
193
192
  metadata: {},
193
+ methods: {},
194
194
  views: {
195
195
  [AllViewⲐ]: {
196
196
  schema: {},
@@ -225,22 +225,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
225
225
  * @param method
226
226
  */
227
227
  getMethodSchema<T>(cls: Class<T>, method: string): FieldConfig[] {
228
- if (!this.#methodSchemas.has(cls)) {
229
- this.#methodSchemas.set(cls, new Map());
230
- }
231
- const cache = this.#methodSchemas.get(cls)!;
232
- if (!cache.has(method) && this.has(cls)) {
233
- const { fields, schema } = this.getViewSchema(cls);
234
- const out = [];
235
- for (const el of fields) {
236
- if (el.startsWith(`${method}.`)) {
237
- out.push(schema[el]);
238
- }
239
- }
240
- out.sort((a, b) => a.index! - b.index!);
241
- cache.set(method, out);
242
- }
243
- return cache.get(method)! ?? [];
228
+ return (this.get(cls)?.methods?.[method] ?? []).filter(x => !!x).sort((a, b) => a.index! - b.index!);
244
229
  }
245
230
 
246
231
  /**
@@ -265,9 +250,18 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
265
250
  * @param idx The param index
266
251
  * @param config The config to register
267
252
  */
268
- registerPendingParamFacet(target: Class, prop: string, idx: number, config: Partial<FieldConfig>): Class {
269
- config.index = idx;
270
- return this.registerPendingFieldFacet(target, `${prop}.${idx}`, config);
253
+ registerPendingParamFacet(target: Class, method: string, idx: number, config: Partial<FieldConfig>): Class {
254
+ const methods = this.getOrCreatePending(target)!.methods!;
255
+ const params = (methods[method] ??= []);
256
+ params[idx] = {
257
+ // @ts-expect-error
258
+ name: `${method}.${idx}`,
259
+ ...params[idx] ?? {},
260
+ owner: target,
261
+ index: idx,
262
+ ...config,
263
+ };
264
+ return target;
271
265
  }
272
266
 
273
267
  /**
@@ -300,9 +294,11 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
300
294
  * @param conf Extra config
301
295
  */
302
296
  registerPendingParamConfig(target: Class, method: string, idx: number, type: ClassList, conf?: Partial<FieldConfig>): Class {
303
- conf ??= {};
304
- conf.index = idx;
305
- return this.registerPendingFieldConfig(target, `${method}.${idx}`, type, conf);
297
+ return this.registerPendingParamFacet(target, method, idx, {
298
+ ...conf,
299
+ array: Array.isArray(type),
300
+ type: Array.isArray(type) ? type[0] : type,
301
+ });
306
302
  }
307
303
 
308
304
  /**
@@ -334,6 +330,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
334
330
  schema: { ...dest.views[AllViewⲐ].schema, ...src.views?.[AllViewⲐ].schema },
335
331
  fields: [...dest.views[AllViewⲐ].fields, ...src.views?.[AllViewⲐ].fields ?? []]
336
332
  };
333
+ dest.methods = { ...src.methods ?? {}, ...dest.methods ?? {} };
337
334
  dest.metadata = { ...src.metadata ?? {}, ...dest.metadata ?? {} };
338
335
  dest.subType = src.subType || dest.subType;
339
336
  dest.title = src.title || dest.title;
@@ -410,7 +407,6 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
410
407
  // Recompute subtypes
411
408
  this.#subTypes.clear();
412
409
  this.#typeKeys.delete(cls);
413
- this.#methodSchemas.delete(cls);
414
410
  this.#accessorDescriptors.delete(cls);
415
411
 
416
412
  // Recompute subtype mappings
@@ -71,6 +71,10 @@ export interface ClassConfig extends DescribableConfig {
71
71
  * Metadata that is related to the schema structure
72
72
  */
73
73
  metadata?: Record<symbol, unknown>;
74
+ /**
75
+ * Method parameter configs
76
+ */
77
+ methods: Record<string, FieldConfig[]>;
74
78
  }
75
79
 
76
80
  /**
@@ -25,6 +25,10 @@ export interface ValidationError {
25
25
  * Regular expression to match
26
26
  */
27
27
  re?: string;
28
+ /**
29
+ * Number to compare against
30
+ */
31
+ n?: number | Date;
28
32
  /**
29
33
  * The type of the field
30
34
  */
@@ -55,6 +59,10 @@ export interface ValidationResult {
55
59
  * Potential regular expression for the result
56
60
  */
57
61
  re?: RegExp;
62
+ /**
63
+ * Number to compare against
64
+ */
65
+ n?: number | Date;
58
66
  }
59
67
 
60
68
  /**
@@ -107,20 +107,24 @@ export class SchemaValidator {
107
107
  static #validateRange(field: FieldConfig, key: 'min' | 'max', value: string | number | Date): boolean {
108
108
 
109
109
  const f = field[key]!;
110
- if (typeof f.n === 'number') {
110
+ const fn = f.n;
111
+ if (typeof fn === 'number') {
111
112
  if (typeof value === 'string') {
112
113
  value = parseInt(value, 10);
113
114
  }
114
115
  if (field.type === Date) {
115
116
  value = new Date(value);
116
117
  }
117
- if (key === 'min' && value < f.n || key === 'max' && value > f.n) {
118
+ const valN = typeof value === 'number' ? value : value.getTime();
119
+ if (key === 'min' && valN < fn || key === 'max' && valN > fn) {
118
120
  return true;
119
121
  }
120
122
  } else {
121
- const date = f.n.getTime();
123
+ const date = fn.getTime();
122
124
  if (typeof value === 'string') {
123
125
  value = Date.parse(value);
126
+ } else if (value instanceof Date) {
127
+ value = value.getTime();
124
128
  }
125
129
  if (key === 'min' && value < date || key === 'max' && value > date) {
126
130
  return true;
@@ -202,6 +206,7 @@ export class SchemaValidator {
202
206
  const out: ValidationError[] = [];
203
207
  for (const res of results) {
204
208
  const err: ValidationError = {
209
+ ...res,
205
210
  kind: res.kind,
206
211
  value: res.value,
207
212
  message: '',
@@ -311,7 +316,7 @@ export class SchemaValidator {
311
316
  * @param method The method being invoked
312
317
  * @param params The params to validate
313
318
  */
314
- static validateMethod<T>(cls: Class<T>, method: string, params: unknown[]): void {
319
+ static async validateMethod<T>(cls: Class<T>, method: string, params: unknown[]): Promise<void> {
315
320
  const errors: ValidationError[] = [];
316
321
  for (const field of SchemaRegistry.getMethodSchema(cls, method)) {
317
322
  errors.push(...this.#validateFieldSchema(field, params[field.index!]));
@@ -128,6 +128,12 @@ export class SchemaTransformUtil {
128
128
  }
129
129
  }
130
130
 
131
+ const tags = ts.getJSDocTags(node);
132
+ const aliases = tags.filter(x => x.tagName.getText() === 'alias');
133
+ if (aliases.length) {
134
+ attrs.push(state.factory.createPropertyAssignment('aliases', state.fromLiteral(aliases.map(x => x.comment).filter(x => !!x))));
135
+ }
136
+
131
137
  const params: ts.Expression[] = [];
132
138
 
133
139
  const existing = state.findDecorator('@travetto/schema', node, 'Field', FIELD_MOD);