@travetto/schema 5.0.14 → 5.0.15
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 +3 -3
- package/src/bind-util.ts +3 -3
- package/src/decorator/schema.ts +0 -1
- package/src/global.d.ts +3 -15
- package/src/internal/types.ts +1 -1
- package/src/name.ts +6 -8
- package/src/service/changes.ts +4 -4
- package/src/service/registry.ts +16 -16
- package/src/service/types.ts +2 -2
- package/src/validate/regexp.ts +5 -8
- package/src/validate/validator.ts +2 -7
- package/support/transformer/util.ts +7 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/schema",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.15",
|
|
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": "^5.0.
|
|
30
|
+
"@travetto/registry": "^5.0.15"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/transformer": "^5.0.
|
|
33
|
+
"@travetto/transformer": "^5.0.12"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@travetto/transformer": {
|
package/src/bind-util.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { castTo, Class, classConstruct, asFull, TypedObject, castKey } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { DataUtil } from './data';
|
|
4
|
-
import {
|
|
4
|
+
import { AllViewSymbol } from './internal/types';
|
|
5
5
|
import { SchemaRegistry } from './service/registry';
|
|
6
6
|
import { FieldConfig } from './service/types';
|
|
7
7
|
|
|
8
8
|
type BindConfig = {
|
|
9
|
-
view?: string | typeof
|
|
9
|
+
view?: string | typeof AllViewSymbol;
|
|
10
10
|
filterField?: (field: FieldConfig) => boolean;
|
|
11
11
|
filterValue?: (value: unknown, field: FieldConfig) => boolean;
|
|
12
12
|
};
|
|
@@ -169,7 +169,7 @@ export class BindUtil {
|
|
|
169
169
|
* @param cfg The bind configuration
|
|
170
170
|
*/
|
|
171
171
|
static bindSchemaToObject<T>(cons: Class<T>, obj: T, data?: object, cfg: BindConfig = {}): T {
|
|
172
|
-
const view = cfg.view ??
|
|
172
|
+
const view = cfg.view ?? AllViewSymbol; // Does not convey
|
|
173
173
|
delete cfg.view;
|
|
174
174
|
|
|
175
175
|
if (!!data && isInstance<T>(data)) {
|
package/src/decorator/schema.ts
CHANGED
package/src/global.d.ts
CHANGED
|
@@ -4,23 +4,11 @@ declare global {
|
|
|
4
4
|
interface Function {
|
|
5
5
|
/**
|
|
6
6
|
* Will produce a new instance of this class with the provided data bound to it
|
|
7
|
+
*
|
|
8
|
+
* NOTE: Only applies to Schema-based classes, will throw a runtime error otherwise
|
|
7
9
|
* @param data The data to bind
|
|
8
10
|
* @param view The optional view to limit the bind to
|
|
9
11
|
*/
|
|
10
12
|
from<T>(this: Class<T>, data: DeepPartial<T>, view?: string): T;
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
namespace NodeJS {
|
|
14
|
-
/**
|
|
15
|
-
* @concrete node:stream#Readable
|
|
16
|
-
*/
|
|
17
|
-
interface ReadableStream { }
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
declare module 'stream' {
|
|
22
|
-
/**
|
|
23
|
-
* @concrete node:stream#Readable
|
|
24
|
-
*/
|
|
25
|
-
interface Readable { }
|
|
26
|
-
}
|
|
14
|
+
}
|
package/src/internal/types.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const AllViewSymbol: unique symbol = Symbol.for('@travetto/schema:all');
|
|
2
2
|
export class UnknownType { }
|
package/src/name.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { describeFunction } from '@travetto/runtime';
|
|
2
1
|
import { ClassConfig } from './service/types';
|
|
3
2
|
|
|
4
|
-
const
|
|
3
|
+
const ID_RE = /(\d{1,100})Δ$/;
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Name resolver, specifically for synthetic types
|
|
@@ -10,23 +9,22 @@ export class SchemaNameResolver {
|
|
|
10
9
|
|
|
11
10
|
#schemaIdToName = new Map<string, string>();
|
|
12
11
|
#digits: number;
|
|
13
|
-
#base: number;
|
|
14
12
|
|
|
15
13
|
constructor(digits = 5) {
|
|
16
14
|
this.#digits = digits;
|
|
17
|
-
this.#base = 10 ** this.#digits;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
getName(schema: ClassConfig): string {
|
|
21
|
-
const
|
|
22
|
-
|
|
18
|
+
const cls = schema.class;
|
|
19
|
+
const id = cls.Ⲑid;
|
|
20
|
+
if (ID_RE.test(cls.name)) {
|
|
23
21
|
if (!this.#schemaIdToName.has(id)) {
|
|
24
|
-
const name =
|
|
22
|
+
const name = cls.name.replace(ID_RE, (_, uid) => uid.slice(-this.#digits));
|
|
25
23
|
this.#schemaIdToName.set(id, name);
|
|
26
24
|
}
|
|
27
25
|
return this.#schemaIdToName.get(id)!;
|
|
28
26
|
} else {
|
|
29
|
-
return
|
|
27
|
+
return cls.name;
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
30
|
}
|
package/src/service/changes.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Class } from '@travetto/runtime';
|
|
|
4
4
|
import { ChangeEvent } from '@travetto/registry';
|
|
5
5
|
|
|
6
6
|
import { FieldConfig, ClassConfig } from './types';
|
|
7
|
-
import {
|
|
7
|
+
import { AllViewSymbol } from '../internal/types';
|
|
8
8
|
|
|
9
9
|
const id = (c: Class | string): string => typeof c === 'string' ? c : c.Ⲑid;
|
|
10
10
|
|
|
@@ -111,8 +111,8 @@ class $SchemaChangeListener {
|
|
|
111
111
|
*/
|
|
112
112
|
emitFieldChanges({ prev, curr }: ChangeEvent<ClassConfig>): void {
|
|
113
113
|
|
|
114
|
-
const prevView = prev?.views[
|
|
115
|
-
const currView = curr!.views[
|
|
114
|
+
const prevView = prev?.views[AllViewSymbol] || { fields: [], schema: {} };
|
|
115
|
+
const currView = curr!.views[AllViewSymbol];
|
|
116
116
|
|
|
117
117
|
const prevFields = new Set(prevView.fields);
|
|
118
118
|
const currFields = new Set(currView.fields);
|
|
@@ -132,7 +132,7 @@ class $SchemaChangeListener {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// Handle class references changing, but keeping same id
|
|
135
|
-
const compareTypes = (a: Class, b: Class): boolean =>
|
|
135
|
+
const compareTypes = (a: Class, b: Class): boolean => a.Ⲑid ? a.Ⲑid === b.Ⲑid : a === b;
|
|
136
136
|
|
|
137
137
|
for (const c of currFields) {
|
|
138
138
|
if (prevFields.has(c)) {
|
package/src/service/registry.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Class, AppError, describeFunction, castTo, classConstruct, asFull, castKey
|
|
1
|
+
import { Class, AppError, describeFunction, castTo, classConstruct, asFull, castKey } from '@travetto/runtime';
|
|
2
2
|
import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
|
|
3
3
|
|
|
4
4
|
import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig, ViewConfig, SchemaMethodConfig } from './types';
|
|
5
5
|
import { SchemaChangeListener } from './changes';
|
|
6
|
-
import {
|
|
6
|
+
import { AllViewSymbol } from '../internal/types';
|
|
7
7
|
import { MethodValidatorFn } from '../validate/types';
|
|
8
8
|
|
|
9
9
|
const classToSubTypeName = (cls: Class): string => cls.name
|
|
@@ -73,7 +73,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
73
73
|
ensureInstanceTypeField<T>(cls: Class, o: T): void {
|
|
74
74
|
const schema = this.get(cls);
|
|
75
75
|
const typeField = castKey<T>(schema.subTypeField);
|
|
76
|
-
if (schema.subTypeName && typeField in schema.views[
|
|
76
|
+
if (schema.subTypeName && typeField in schema.views[AllViewSymbol].schema && !o[typeField]) { // Do we have a type field defined
|
|
77
77
|
o[typeField] = castTo(schema.subTypeName); // Assign if missing
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -149,7 +149,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
149
149
|
this.#subTypes.get(base)!.set(config.subTypeName!, cls);
|
|
150
150
|
}
|
|
151
151
|
if (base !== cls) {
|
|
152
|
-
while (base &&
|
|
152
|
+
while (base && base.Ⲑid) {
|
|
153
153
|
this.#subTypes.get(base)!.set(config.subTypeName!, cls);
|
|
154
154
|
const parent = this.getParentClass(base);
|
|
155
155
|
base = parent ? this.getBaseSchema(parent) : undefined;
|
|
@@ -169,7 +169,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
169
169
|
SchemaChangeListener.trackSchemaDependency(curr, cls, path, this.get(cls));
|
|
170
170
|
|
|
171
171
|
// Read children
|
|
172
|
-
const view = config.views[
|
|
172
|
+
const view = config.views[AllViewSymbol];
|
|
173
173
|
for (const k of view.fields) {
|
|
174
174
|
if (this.has(view.schema[k].type) && view.schema[k].type !== cls) {
|
|
175
175
|
this.trackSchemaDependencies(cls, view.schema[k].type, [...path, view.schema[k]]);
|
|
@@ -186,7 +186,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
186
186
|
metadata: {},
|
|
187
187
|
methods: {},
|
|
188
188
|
views: {
|
|
189
|
-
[
|
|
189
|
+
[AllViewSymbol]: {
|
|
190
190
|
schema: {},
|
|
191
191
|
fields: []
|
|
192
192
|
}
|
|
@@ -199,8 +199,8 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
199
199
|
* @param cls The class to retrieve the schema for
|
|
200
200
|
* @param view The view name
|
|
201
201
|
*/
|
|
202
|
-
getViewSchema<T>(cls: Class<T>, view?: string | typeof
|
|
203
|
-
view = view ??
|
|
202
|
+
getViewSchema<T>(cls: Class<T>, view?: string | typeof AllViewSymbol): ViewConfig {
|
|
203
|
+
view = view ?? AllViewSymbol;
|
|
204
204
|
|
|
205
205
|
const schema = this.get(cls)!;
|
|
206
206
|
if (!schema) {
|
|
@@ -296,7 +296,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
296
296
|
* @param config The config to register
|
|
297
297
|
*/
|
|
298
298
|
registerPendingFieldFacet(target: Class, prop: string, config: Partial<FieldConfig>): Class {
|
|
299
|
-
const allViewConf = this.getOrCreatePending(target).views![
|
|
299
|
+
const allViewConf = this.getOrCreatePending(target).views![AllViewSymbol];
|
|
300
300
|
|
|
301
301
|
if (!allViewConf.schema[prop]) {
|
|
302
302
|
allViewConf.fields.push(prop);
|
|
@@ -359,9 +359,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
359
359
|
* @param src Source config
|
|
360
360
|
*/
|
|
361
361
|
mergeConfigs(dest: ClassConfig, src: Partial<ClassConfig>, inherited = false): ClassConfig {
|
|
362
|
-
dest.views[
|
|
363
|
-
schema: { ...dest.views[
|
|
364
|
-
fields: [...dest.views[
|
|
362
|
+
dest.views[AllViewSymbol] = {
|
|
363
|
+
schema: { ...dest.views[AllViewSymbol].schema, ...src.views?.[AllViewSymbol].schema },
|
|
364
|
+
fields: [...dest.views[AllViewSymbol].fields, ...src.views?.[AllViewSymbol].fields ?? []]
|
|
365
365
|
};
|
|
366
366
|
if (!inherited) {
|
|
367
367
|
dest.baseType = src.baseType ?? dest.baseType;
|
|
@@ -381,7 +381,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
381
381
|
* @param conf The class config
|
|
382
382
|
*/
|
|
383
383
|
finalizeViews<T>(target: Class<T>, conf: ClassConfig): ClassConfig {
|
|
384
|
-
const allViewConf = conf.views![
|
|
384
|
+
const allViewConf = conf.views![AllViewSymbol];
|
|
385
385
|
const pending = this.#pendingViews.get(target) ?? new Map<string, ViewFieldsConfig<string>>();
|
|
386
386
|
this.#pendingViews.delete(target);
|
|
387
387
|
|
|
@@ -427,9 +427,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
427
427
|
// Write views out
|
|
428
428
|
config = this.finalizeViews(cls, config);
|
|
429
429
|
|
|
430
|
-
if (config.subTypeName && config.subTypeField in config.views[
|
|
431
|
-
const field = config.views[
|
|
432
|
-
config.views[
|
|
430
|
+
if (config.subTypeName && config.subTypeField in config.views[AllViewSymbol].schema) {
|
|
431
|
+
const field = config.views[AllViewSymbol].schema[config.subTypeField];
|
|
432
|
+
config.views[AllViewSymbol].schema[config.subTypeField] = {
|
|
433
433
|
...field,
|
|
434
434
|
enum: {
|
|
435
435
|
values: [config.subTypeName],
|
package/src/service/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Any, Class, Primitive } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { AllViewSymbol } from '../internal/types';
|
|
4
4
|
import { MethodValidatorFn, ValidatorFn } from '../validate/types';
|
|
5
5
|
|
|
6
6
|
export type ClassList = Class | [Class];
|
|
@@ -69,7 +69,7 @@ export interface ClassConfig extends DescribableConfig {
|
|
|
69
69
|
/**
|
|
70
70
|
* List of all views
|
|
71
71
|
*/
|
|
72
|
-
views: Record<string, ViewConfig> & { [
|
|
72
|
+
views: Record<string, ViewConfig> & { [AllViewSymbol]: ViewConfig };
|
|
73
73
|
/**
|
|
74
74
|
* Global validators
|
|
75
75
|
*/
|
package/src/validate/regexp.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { TypedObject } from '@travetto/runtime';
|
|
2
2
|
import { Messages } from './messages';
|
|
3
3
|
|
|
4
|
-
declare global {
|
|
5
|
-
interface RegExp {
|
|
6
|
-
name?: string;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
4
|
/**
|
|
11
5
|
* List of common regular expressions for fields
|
|
12
6
|
*/
|
|
@@ -18,8 +12,11 @@ export const CommonRegExp = {
|
|
|
18
12
|
postalCode: /^\d{5}(?:[-\s]\d{4})?$/
|
|
19
13
|
};
|
|
20
14
|
|
|
15
|
+
export const CommonRegExpToName = new Map<RegExp, string>();
|
|
16
|
+
|
|
21
17
|
// Rebind regexes
|
|
22
18
|
for (const k of TypedObject.keys(CommonRegExp)) {
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const name = `[[:${k}:]]`;
|
|
20
|
+
CommonRegExpToName.set(CommonRegExp[k], name);
|
|
21
|
+
Messages.set(name, Messages.get(k)!);
|
|
25
22
|
}
|
|
@@ -6,6 +6,7 @@ import { ValidationError, ValidationKindCore, ValidationResult } from './types';
|
|
|
6
6
|
import { Messages } from './messages';
|
|
7
7
|
import { isValidationError, TypeMismatchError, ValidationResultError } from './error';
|
|
8
8
|
import { DataUtil } from '../data';
|
|
9
|
+
import { CommonRegExpToName } from './regexp';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Get the schema config for Class/Schema config, including support for polymorphism
|
|
@@ -26,12 +27,6 @@ function isRangeValue(o: unknown): o is number | string | Date {
|
|
|
26
27
|
return typeof o === 'string' || typeof o === 'number' || o instanceof Date;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
declare global {
|
|
30
|
-
interface RegExp {
|
|
31
|
-
name?: string;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
30
|
/**
|
|
36
31
|
* The schema validator applies the schema constraints to a given object and looks
|
|
37
32
|
* for errors
|
|
@@ -195,7 +190,7 @@ export class SchemaValidator {
|
|
|
195
190
|
kind: res.kind,
|
|
196
191
|
value: res.value,
|
|
197
192
|
message: '',
|
|
198
|
-
re: res.re
|
|
193
|
+
re: CommonRegExpToName.get(res.re!) ?? res.re?.source ?? '',
|
|
199
194
|
path,
|
|
200
195
|
type: (typeof res.type === 'function' ? res.type.name : res.type)
|
|
201
196
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import {
|
|
3
3
|
type AnyType, DeclarationUtil, LiteralUtil,
|
|
4
|
-
DecoratorUtil, DocUtil, ParamDocumentation, TransformerState, transformCast
|
|
4
|
+
DecoratorUtil, DocUtil, ParamDocumentation, TransformerState, transformCast,
|
|
5
5
|
} from '@travetto/transformer';
|
|
6
6
|
|
|
7
7
|
const SCHEMA_MOD = '@travetto/schema/src/decorator/schema';
|
|
@@ -33,10 +33,10 @@ export class SchemaTransformUtil {
|
|
|
33
33
|
return state.createAccess(imp.ident, 'UnknownType');
|
|
34
34
|
}
|
|
35
35
|
case 'shape': {
|
|
36
|
-
const uniqueId = state.generateUniqueIdentifier(node, type);
|
|
36
|
+
const uniqueId = state.generateUniqueIdentifier(node, type, 'Δ');
|
|
37
37
|
|
|
38
38
|
// Build class on the fly
|
|
39
|
-
const [id, existing] = state.
|
|
39
|
+
const [id, existing] = state.registerIdentifier(uniqueId);
|
|
40
40
|
if (!existing) {
|
|
41
41
|
const cls = state.factory.createClassDeclaration(
|
|
42
42
|
[
|
|
@@ -52,7 +52,7 @@ export class SchemaTransformUtil {
|
|
|
52
52
|
Object.entries(type.fieldTypes)
|
|
53
53
|
.map(([k, v]) =>
|
|
54
54
|
this.computeField(state, state.factory.createPropertyDeclaration(
|
|
55
|
-
[], k,
|
|
55
|
+
[], /\W/.test(k) ? state.factory.createComputedPropertyName(state.fromLiteral(k)) : k,
|
|
56
56
|
v.undefinable || v.nullable ? state.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
|
|
57
57
|
v.key === 'unknown' ? state.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) : undefined, undefined
|
|
58
58
|
), { type: v, root })
|
|
@@ -68,7 +68,7 @@ export class SchemaTransformUtil {
|
|
|
68
68
|
}
|
|
69
69
|
return id;
|
|
70
70
|
}
|
|
71
|
-
case '
|
|
71
|
+
case 'composition': {
|
|
72
72
|
if (type.commonType) {
|
|
73
73
|
return this.toConcreteType(state, type.commonType, node, root);
|
|
74
74
|
}
|
|
@@ -127,8 +127,8 @@ export class SchemaTransformUtil {
|
|
|
127
127
|
const primaryExpr = typeExpr.key === 'literal' && typeExpr.typeArguments?.[0] ? typeExpr.typeArguments[0] : typeExpr;
|
|
128
128
|
|
|
129
129
|
// We need to ensure we aren't being tripped up by the wrapper for arrays, sets, etc.
|
|
130
|
-
// If we have a
|
|
131
|
-
if (primaryExpr.key === '
|
|
130
|
+
// If we have a composition type
|
|
131
|
+
if (primaryExpr.key === 'composition') {
|
|
132
132
|
const values = primaryExpr.subTypes.map(x => x.key === 'literal' ? x.value : undefined)
|
|
133
133
|
.filter(x => x !== undefined && x !== null);
|
|
134
134
|
|