@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 +10 -5
- package/package.json +3 -3
- package/src/decorator/common.ts +3 -2
- package/src/service/registry.ts +20 -24
- package/src/service/types.ts +4 -0
- package/src/validate/types.ts +8 -0
- package/src/validate/validator.ts +9 -4
- package/support/transform-util.ts +6 -0
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"), [
|
|
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
|
+
"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.
|
|
30
|
+
"@travetto/registry": "^3.1.0-rc.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/transformer": "^3.0.
|
|
33
|
+
"@travetto/transformer": "^3.1.0-rc.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@travetto/transformer": {
|
package/src/decorator/common.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
package/src/service/registry.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
package/src/service/types.ts
CHANGED
|
@@ -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
|
/**
|
package/src/validate/types.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 =
|
|
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);
|