@travetto/schema 7.0.0-rc.0 → 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 +31 -31
- package/package.json +3 -3
- package/src/bind-util.ts +99 -99
- package/src/data.ts +40 -39
- package/src/decorator/common.ts +5 -5
- package/src/decorator/field.ts +9 -7
- package/src/decorator/input.ts +11 -9
- package/src/decorator/method.ts +3 -1
- package/src/decorator/schema.ts +2 -2
- package/src/internal/types.ts +3 -3
- package/src/name.ts +3 -3
- package/src/service/changes.ts +33 -33
- package/src/service/registry-adapter.ts +59 -59
- package/src/service/registry-index.ts +27 -37
- package/src/service/types.ts +5 -5
- package/src/types.ts +1 -1
- package/src/validate/error.ts +2 -2
- package/src/validate/regexp.ts +4 -4
- package/src/validate/validator.ts +78 -78
- package/support/transformer/util.ts +26 -23
- package/support/transformer.schema.ts +7 -7
package/src/data.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isNumberObject
|
|
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
|
|
15
|
+
* @param value Object to check
|
|
16
16
|
*/
|
|
17
|
-
static isPlainObject(
|
|
18
|
-
return typeof
|
|
19
|
-
&&
|
|
20
|
-
&&
|
|
21
|
-
&&
|
|
22
|
-
&& Object.prototype.toString.call(
|
|
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
|
|
27
|
+
* @param value Value to check
|
|
28
28
|
*/
|
|
29
|
-
static isPrimitive(
|
|
30
|
-
switch (typeof
|
|
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 !!
|
|
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(
|
|
41
|
-
return this.isPrimitive(
|
|
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
|
|
72
|
-
const
|
|
73
|
-
for (let i = 0; i <
|
|
74
|
-
|
|
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
|
|
91
|
-
const
|
|
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(
|
|
95
|
+
for (const key of Object.keys(bObject)) {
|
|
94
96
|
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
95
97
|
continue;
|
|
96
98
|
}
|
|
97
|
-
|
|
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,
|
|
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
|
|
231
|
+
* @param value Object to clone
|
|
230
232
|
*/
|
|
231
|
-
static shallowClone<T>(
|
|
232
|
-
return castTo(Array.isArray(
|
|
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
|
|
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>(
|
|
255
|
-
if (Array.isArray(
|
|
256
|
-
return castTo(
|
|
257
|
-
} else if (
|
|
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(
|
|
260
|
-
if (!exclude.some(
|
|
261
|
-
const
|
|
262
|
-
if (typeof
|
|
263
|
-
out[key] = this.filterByKeys(
|
|
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] =
|
|
267
|
+
out[key] = value;
|
|
266
268
|
}
|
|
267
269
|
}
|
|
268
270
|
}
|
|
269
271
|
return asFull(out);
|
|
270
272
|
} else {
|
|
271
|
-
return
|
|
273
|
+
return input;
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
|
-
|
|
275
276
|
}
|
package/src/decorator/common.ts
CHANGED
|
@@ -10,14 +10,14 @@ import { SchemaRegistryIndex } from '../service/registry-index.ts';
|
|
|
10
10
|
* @kind decorator
|
|
11
11
|
*/
|
|
12
12
|
export function Describe(config: Partial<Omit<SchemaCoreConfig, 'metadata'>>) {
|
|
13
|
-
return (instanceOrCls: Class | ClassInstance, property?: string
|
|
13
|
+
return (instanceOrCls: Class | ClassInstance, property?: string, descriptorOrIdx?: PropertyDescriptor | number): void => {
|
|
14
14
|
const adapter = SchemaRegistryIndex.getForRegister(getClass(instanceOrCls));
|
|
15
15
|
if (!property) {
|
|
16
16
|
adapter.register(config);
|
|
17
17
|
} else {
|
|
18
|
-
if (
|
|
19
|
-
adapter.registerParameter(property,
|
|
20
|
-
} else if (typeof
|
|
18
|
+
if (descriptorOrIdx !== undefined && typeof descriptorOrIdx === 'number') {
|
|
19
|
+
adapter.registerParameter(property, descriptorOrIdx, config);
|
|
20
|
+
} else if (typeof descriptorOrIdx === 'object' && typeof descriptorOrIdx.value === 'function') {
|
|
21
21
|
adapter.registerMethod(property, config);
|
|
22
22
|
} else {
|
|
23
23
|
adapter.registerField(property, config);
|
|
@@ -31,7 +31,7 @@ export function Describe(config: Partial<Omit<SchemaCoreConfig, 'metadata'>>) {
|
|
|
31
31
|
* @augments `@travetto/schema:Input`
|
|
32
32
|
* @kind decorator
|
|
33
33
|
*/
|
|
34
|
-
export const IsPrivate = (): (instanceOrCls: Class | ClassInstance, property?: string
|
|
34
|
+
export const IsPrivate = (): (instanceOrCls: Class | ClassInstance, property?: string) => void => Describe({ private: true });
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Mark a field/method as ignored
|
package/src/decorator/field.ts
CHANGED
|
@@ -3,23 +3,25 @@ import { Any, ClassInstance, getClass } from '@travetto/runtime';
|
|
|
3
3
|
import { SchemaFieldConfig } from '../service/types.ts';
|
|
4
4
|
import { SchemaRegistryIndex } from '../service/registry-index.ts';
|
|
5
5
|
|
|
6
|
-
type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(
|
|
6
|
+
type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(
|
|
7
|
+
instance: T, property: K, idx?: TypedPropertyDescriptor<Any> | number
|
|
8
|
+
) => void);
|
|
7
9
|
|
|
8
|
-
function field<V>(...
|
|
9
|
-
return (instance: ClassInstance, property: string
|
|
10
|
-
SchemaRegistryIndex.getForRegister(getClass(instance)).registerField(property, ...
|
|
10
|
+
function field<V>(...configs: Partial<SchemaFieldConfig>[]): PropType<V> {
|
|
11
|
+
return (instance: ClassInstance, property: string): void => {
|
|
12
|
+
SchemaRegistryIndex.getForRegister(getClass(instance)).registerField(property, ...configs);
|
|
11
13
|
};
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Registering a field
|
|
16
18
|
* @param type The type for the field
|
|
17
|
-
* @param
|
|
19
|
+
* @param configs The field configuration
|
|
18
20
|
* @augments `@travetto/schema:Input`
|
|
19
21
|
* @kind decorator
|
|
20
22
|
*/
|
|
21
|
-
export function Field(type: Pick<SchemaFieldConfig, 'type' | 'array'>, ...
|
|
22
|
-
return field(type, ...
|
|
23
|
+
export function Field(type: Pick<SchemaFieldConfig, 'type' | 'array'>, ...configs: Partial<SchemaFieldConfig>[]): PropType<unknown> {
|
|
24
|
+
return field(type, ...configs);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/**
|
package/src/decorator/input.ts
CHANGED
|
@@ -4,16 +4,18 @@ import { CommonRegExp } from '../validate/regexp.ts';
|
|
|
4
4
|
import { CONSTRUCTOR_PROPERTY, SchemaInputConfig } from '../service/types.ts';
|
|
5
5
|
import { SchemaRegistryIndex } from '../service/registry-index.ts';
|
|
6
6
|
|
|
7
|
-
type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(
|
|
7
|
+
type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(
|
|
8
|
+
instance: T, property: K, idx?: TypedPropertyDescriptor<Any> | number
|
|
9
|
+
) => void);
|
|
8
10
|
|
|
9
|
-
function input<V>(...
|
|
10
|
-
return (instanceOrCls: ClassInstance | Class, property: string
|
|
11
|
+
function input<V>(...configs: Partial<SchemaInputConfig>[]): PropType<V> {
|
|
12
|
+
return (instanceOrCls: ClassInstance | Class, property: string, idx?: number | TypedPropertyDescriptor<Any>): void => {
|
|
11
13
|
const adapter = SchemaRegistryIndex.getForRegister(getClass(instanceOrCls));
|
|
12
14
|
const propertyKey = property ?? CONSTRUCTOR_PROPERTY;
|
|
13
15
|
if (typeof idx === 'number') {
|
|
14
|
-
adapter.registerParameter(propertyKey, idx, ...
|
|
16
|
+
adapter.registerParameter(propertyKey, idx, ...configs);
|
|
15
17
|
} else {
|
|
16
|
-
adapter.registerField(propertyKey, ...
|
|
18
|
+
adapter.registerField(propertyKey, ...configs);
|
|
17
19
|
}
|
|
18
20
|
};
|
|
19
21
|
}
|
|
@@ -21,12 +23,12 @@ function input<V>(...obj: Partial<SchemaInputConfig>[]): PropType<V> {
|
|
|
21
23
|
/**
|
|
22
24
|
* Registering an input
|
|
23
25
|
* @param type The type for the input
|
|
24
|
-
* @param
|
|
26
|
+
* @param configs The input configuration
|
|
25
27
|
* @augments `@travetto/schema:Input`
|
|
26
28
|
* @kind decorator
|
|
27
29
|
*/
|
|
28
|
-
export function Input(type: Pick<SchemaInputConfig, 'type' | 'array'>, ...
|
|
29
|
-
return input(type, ...
|
|
30
|
+
export function Input(type: Pick<SchemaInputConfig, 'type' | 'array'>, ...configs: Partial<SchemaInputConfig>[]): PropType<unknown> {
|
|
31
|
+
return input(type, ...configs);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -197,7 +199,7 @@ export function Specifier(...specifiers: string[]): PropType<unknown> { return i
|
|
|
197
199
|
* @augments `@travetto/schema:Input`
|
|
198
200
|
* @kind decorator
|
|
199
201
|
*/
|
|
200
|
-
export function DiscriminatorField(): ((
|
|
202
|
+
export function DiscriminatorField(): ((instance: ClassInstance, property: string) => void) {
|
|
201
203
|
return (instance: ClassInstance, property: string): void => {
|
|
202
204
|
SchemaRegistryIndex.getForRegister(getClass(instance)).register({
|
|
203
205
|
discriminatedBase: true,
|
package/src/decorator/method.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { SchemaMethodConfig } from '../service/types';
|
|
|
4
4
|
import { SchemaRegistryIndex } from '../service/registry-index';
|
|
5
5
|
import { MethodValidatorFn } from '../validate/types';
|
|
6
6
|
|
|
7
|
+
type MethodDecorator = (instance: ClassInstance, property: string, descriptor: PropertyDescriptor) => PropertyDescriptor | void;
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Registering a method
|
|
9
11
|
* @param config The method configuration
|
|
@@ -11,7 +13,7 @@ import { MethodValidatorFn } from '../validate/types';
|
|
|
11
13
|
* @kind decorator
|
|
12
14
|
*/
|
|
13
15
|
export function Method(...config: Partial<SchemaMethodConfig>[]) {
|
|
14
|
-
return (instanceOrCls: ClassInstance, property: string
|
|
16
|
+
return (instanceOrCls: ClassInstance, property: string): void => {
|
|
15
17
|
SchemaRegistryIndex.getForRegister(getClass(instanceOrCls)).registerMethod(property, ...config);
|
|
16
18
|
};
|
|
17
19
|
}
|
package/src/decorator/schema.ts
CHANGED
|
@@ -16,12 +16,12 @@ type ValidStringField<T> = { [K in Extract<keyof T, string>]: T[K] extends strin
|
|
|
16
16
|
* @augments `@travetto/schema:Schema`
|
|
17
17
|
* @kind decorator
|
|
18
18
|
*/
|
|
19
|
-
export function Schema(
|
|
19
|
+
export function Schema(config?: Partial<Pick<SchemaClassConfig, 'validators' | 'methods'>>) {
|
|
20
20
|
return <T, U extends Class<T>>(cls: U): void => {
|
|
21
21
|
cls.from ??= function <V>(this: Class<V>, data: DeepPartial<V>, view?: string): V {
|
|
22
22
|
return BindUtil.bindSchema(this, data, { view });
|
|
23
23
|
};
|
|
24
|
-
SchemaRegistryIndex.getForRegister(cls).registerClass(
|
|
24
|
+
SchemaRegistryIndex.getForRegister(cls).registerClass(config);
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
package/src/internal/types.ts
CHANGED
|
@@ -5,7 +5,7 @@ const InvalidSymbol = Symbol();
|
|
|
5
5
|
/**
|
|
6
6
|
* Point Implementation
|
|
7
7
|
*/
|
|
8
|
-
export class
|
|
8
|
+
export class PointImplementation {
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Validate we have an actual point
|
|
@@ -20,7 +20,7 @@ export class PointImpl {
|
|
|
20
20
|
*/
|
|
21
21
|
static bindSchema(input: unknown): [number, number] | typeof InvalidSymbol | undefined {
|
|
22
22
|
if (Array.isArray(input) && input.length === 2) {
|
|
23
|
-
const [a, b] = input.map(
|
|
23
|
+
const [a, b] = input.map(value => DataUtil.coerceType(value, Number, false));
|
|
24
24
|
return [a, b];
|
|
25
25
|
} else {
|
|
26
26
|
return InvalidSymbol;
|
|
@@ -28,4 +28,4 @@ export class PointImpl {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
Object.defineProperty(
|
|
31
|
+
Object.defineProperty(PointImplementation, 'name', { value: 'Point' });
|
package/src/name.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SchemaClassConfig } from './service/types.ts';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const ID_REGEX = /(\d{1,100})Δ$/;
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Name resolver, specifically for synthetic types
|
|
@@ -17,9 +17,9 @@ export class SchemaNameResolver {
|
|
|
17
17
|
getName(schema: SchemaClassConfig): string {
|
|
18
18
|
const cls = schema.class;
|
|
19
19
|
const id = cls.Ⲑid;
|
|
20
|
-
if (
|
|
20
|
+
if (ID_REGEX.test(cls.name)) {
|
|
21
21
|
if (!this.#schemaIdToName.has(id)) {
|
|
22
|
-
const name = cls.name.replace(
|
|
22
|
+
const name = cls.name.replace(ID_REGEX, (_, uniqueId) => uniqueId.slice(-this.#digits));
|
|
23
23
|
this.#schemaIdToName.set(id, name);
|
|
24
24
|
}
|
|
25
25
|
return this.#schemaIdToName.get(id)!;
|
package/src/service/changes.ts
CHANGED
|
@@ -40,17 +40,17 @@ class $SchemaChangeListener {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* On schema change, emit the change event for the whole schema
|
|
43
|
-
* @param
|
|
43
|
+
* @param handler The function to call on schema change
|
|
44
44
|
*/
|
|
45
|
-
onSchemaChange(handler: (
|
|
45
|
+
onSchemaChange(handler: (event: SchemaChangeEvent) => void): void {
|
|
46
46
|
this.#emitter.on('schema', handler);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* On schema field change, emit the change event for the whole schema
|
|
51
|
-
* @param
|
|
51
|
+
* @param handler The function to call on schema field change
|
|
52
52
|
*/
|
|
53
|
-
onFieldChange(handler: (
|
|
53
|
+
onFieldChange(handler: (event: FieldChangeEvent) => void): void {
|
|
54
54
|
this.#emitter.on('field', handler);
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -63,13 +63,13 @@ class $SchemaChangeListener {
|
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* Track a specific class for dependencies
|
|
66
|
-
* @param
|
|
66
|
+
* @param cls The target class
|
|
67
67
|
* @param parent The parent class
|
|
68
68
|
* @param path The path within the object hierarchy to arrive at the class
|
|
69
69
|
* @param config The configuration or the class
|
|
70
70
|
*/
|
|
71
|
-
trackSchemaDependency(
|
|
72
|
-
const idValue =
|
|
71
|
+
trackSchemaDependency(cls: Class, parent: Class, path: SchemaFieldConfig[], config: SchemaClassConfig): void {
|
|
72
|
+
const idValue = cls.Ⲑid;
|
|
73
73
|
if (!this.#mapping.has(idValue)) {
|
|
74
74
|
this.#mapping.set(idValue, new Map());
|
|
75
75
|
}
|
|
@@ -86,13 +86,13 @@ class $SchemaChangeListener {
|
|
|
86
86
|
const clsId = cls.Ⲑid;
|
|
87
87
|
|
|
88
88
|
if (this.#mapping.has(clsId)) {
|
|
89
|
-
const
|
|
90
|
-
for (const
|
|
91
|
-
if (!updates.has(
|
|
92
|
-
updates.set(
|
|
89
|
+
const dependencies = this.#mapping.get(clsId)!;
|
|
90
|
+
for (const dependencyClsId of dependencies.keys()) {
|
|
91
|
+
if (!updates.has(dependencyClsId)) {
|
|
92
|
+
updates.set(dependencyClsId, { config: dependencies.get(dependencyClsId)!.config, subs: [] });
|
|
93
93
|
}
|
|
94
|
-
const
|
|
95
|
-
updates.get(
|
|
94
|
+
const childDependency = dependencies.get(dependencyClsId)!;
|
|
95
|
+
updates.get(dependencyClsId)!.subs.push({ path: [...childDependency.path], fields: changes });
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -103,49 +103,49 @@ class $SchemaChangeListener {
|
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Emit field level changes in the schema
|
|
106
|
-
* @param
|
|
107
|
-
* @param
|
|
106
|
+
* @param previous The previous class config
|
|
107
|
+
* @param current The current class config
|
|
108
108
|
*/
|
|
109
|
-
emitFieldChanges(
|
|
110
|
-
const
|
|
111
|
-
const
|
|
109
|
+
emitFieldChanges(event: ChangeEvent<SchemaClassConfig>): void {
|
|
110
|
+
const previous = 'previous' in event ? event.previous : undefined;
|
|
111
|
+
const current = 'current' in event ? event.current : undefined;
|
|
112
112
|
|
|
113
|
-
const
|
|
114
|
-
const
|
|
113
|
+
const previousFields = new Set(Object.keys(previous?.fields ?? {}));
|
|
114
|
+
const currentFields = new Set(Object.keys(current?.fields ?? {}));
|
|
115
115
|
|
|
116
116
|
const changes: ChangeEvent<SchemaFieldConfig>[] = [];
|
|
117
117
|
|
|
118
|
-
for (const
|
|
119
|
-
if (!
|
|
120
|
-
changes.push({
|
|
118
|
+
for (const field of currentFields) {
|
|
119
|
+
if (!previousFields.has(field) && current) {
|
|
120
|
+
changes.push({ current: current.fields[field], type: 'added' });
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
for (const
|
|
125
|
-
if (!
|
|
126
|
-
changes.push({
|
|
124
|
+
for (const field of previousFields) {
|
|
125
|
+
if (!currentFields.has(field) && previous) {
|
|
126
|
+
changes.push({ previous: previous.fields[field], type: 'removing' });
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
// Handle class references changing, but keeping same id
|
|
131
131
|
const compareTypes = (a: Class, b: Class): boolean => a.Ⲑid ? a.Ⲑid === b.Ⲑid : a === b;
|
|
132
132
|
|
|
133
|
-
for (const
|
|
134
|
-
if (
|
|
135
|
-
const prevSchema =
|
|
136
|
-
const currSchema =
|
|
133
|
+
for (const field of currentFields) {
|
|
134
|
+
if (previousFields.has(field) && previous && current) {
|
|
135
|
+
const prevSchema = previous.fields[field];
|
|
136
|
+
const currSchema = current.fields[field];
|
|
137
137
|
if (
|
|
138
138
|
JSON.stringify(prevSchema) !== JSON.stringify(currSchema) ||
|
|
139
139
|
!compareTypes(prevSchema.type, currSchema.type)
|
|
140
140
|
) {
|
|
141
|
-
changes.push({
|
|
141
|
+
changes.push({ previous: previous.fields[field], current: current.fields[field], type: 'changed' });
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// Send field changes
|
|
147
|
-
this.#emitter.emit('field', { cls:
|
|
148
|
-
this.emitSchemaChanges({ cls:
|
|
147
|
+
this.#emitter.emit('field', { cls: current!.class, changes });
|
|
148
|
+
this.emitSchemaChanges({ cls: current!.class, changes });
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|