@travetto/schema 5.0.0-rc.1 → 5.0.0-rc.11
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 +26 -26
- package/__index__.ts +1 -2
- package/package.json +3 -3
- package/src/bind-util.ts +20 -30
- package/src/data.ts +42 -27
- package/src/decorator/common.ts +1 -1
- package/src/decorator/field.ts +5 -6
- package/src/decorator/schema.ts +3 -4
- package/src/global.d.ts +1 -2
- package/src/internal/types.ts +2 -1
- package/src/name.ts +2 -2
- package/src/service/changes.ts +1 -1
- package/src/service/registry.ts +16 -19
- package/src/service/types.ts +2 -3
- package/src/validate/error.ts +1 -1
- package/src/validate/regexp.ts +1 -1
- package/src/validate/validator.ts +15 -13
- package/support/transformer/util.ts +24 -18
- package/src/types.ts +0 -7
package/README.md
CHANGED
|
@@ -63,28 +63,28 @@ User:
|
|
|
63
63
|
|
|
64
64
|
### Fields
|
|
65
65
|
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:
|
|
66
|
-
* [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
67
|
-
* [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
68
|
-
* [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
69
|
-
* [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
70
|
-
* [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
71
|
-
* [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
72
|
-
* [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
73
|
-
* [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
74
|
-
* [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
75
|
-
* [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
76
|
-
* [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
77
|
-
* [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
78
|
-
* [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
79
|
-
* [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
80
|
-
* [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
81
|
-
* [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
82
|
-
* [@LongText](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
83
|
-
* [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
84
|
-
* [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
85
|
-
* [@Secret](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
86
|
-
* [@Specifier](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
87
|
-
* [@SubTypeField](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
66
|
+
* [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L30) defines a field that will be serialized.
|
|
67
|
+
* [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L70) defines a that field should be required
|
|
68
|
+
* [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L77) defines the allowable values that a field can have
|
|
69
|
+
* [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L98) defines a regular expression that the field value should match
|
|
70
|
+
* [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) enforces min length of a string
|
|
71
|
+
* [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116) enforces max length of a string
|
|
72
|
+
* [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) enforces min value for a date or a number
|
|
73
|
+
* [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116) enforces max value for a date or a number
|
|
74
|
+
* [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L143) ensures string field matches basic email regex
|
|
75
|
+
* [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L150) ensures string field matches basic telephone regex
|
|
76
|
+
* [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L157) ensures string field matches basic url regex
|
|
77
|
+
* [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L196) exclude from auto schema registration
|
|
78
|
+
* [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L171) ensures number passed in is only a whole number
|
|
79
|
+
* [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L177) ensures number passed in allows fractional values
|
|
80
|
+
* [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L189) provides support for standard currency
|
|
81
|
+
* [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L85) indicates that a field is expecting natural language input, not just discrete values
|
|
82
|
+
* [@LongText](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L90) same as text, but expects longer form content
|
|
83
|
+
* [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L57) defines a that field should not be bindable external to the class
|
|
84
|
+
* [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L51) defines a that field should not be exported in serialization, but that it can be bound to
|
|
85
|
+
* [@Secret](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L63) marks a field as being sensitive. This is used by certain logging activities to ensure sensitive information is not logged out.
|
|
86
|
+
* [@Specifier](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L205) attributes additional specifiers to a field, allowing for more specification beyond just the field's type.
|
|
87
|
+
* [@SubTypeField](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L211) allows for promoting a given field as the owner of the sub type discriminator (defaults to `type`).
|
|
88
88
|
Additionally, schemas can be nested to form more complex data structures that are able to bound and validated.
|
|
89
89
|
|
|
90
90
|
Just like the class, all fields can be defined with
|
|
@@ -198,7 +198,6 @@ export async function validate(): Promise<void> {
|
|
|
198
198
|
|
|
199
199
|
const person = Person.from({
|
|
200
200
|
name: 'Test',
|
|
201
|
-
// @ts-expect-error
|
|
202
201
|
age: 'abc',
|
|
203
202
|
address: {
|
|
204
203
|
street1: '1234 Fun'
|
|
@@ -313,7 +312,7 @@ This feature is meant to allow for simple Typescript types to be able to be back
|
|
|
313
312
|
|
|
314
313
|
**Code: Simple Custom Type**
|
|
315
314
|
```typescript
|
|
316
|
-
import { DataUtil } from '@travetto/
|
|
315
|
+
import { DataUtil } from '@travetto/schema';
|
|
317
316
|
|
|
318
317
|
/**
|
|
319
318
|
* @concrete #PointImpl
|
|
@@ -330,7 +329,8 @@ export class PointImpl {
|
|
|
330
329
|
|
|
331
330
|
static bindSchema(input: unknown): [number, number] | typeof INVALID | undefined {
|
|
332
331
|
if (Array.isArray(input) && input.length === 2) {
|
|
333
|
-
|
|
332
|
+
const [a, b] = input.map(x => DataUtil.coerceType(x, Number, false));
|
|
333
|
+
return [a, b];
|
|
334
334
|
} else {
|
|
335
335
|
return INVALID;
|
|
336
336
|
}
|
|
@@ -379,7 +379,7 @@ Validation Failed {
|
|
|
379
379
|
```
|
|
380
380
|
|
|
381
381
|
## Data Utilities
|
|
382
|
-
Data utilities for binding values, and type conversion. Currently [DataUtil](https://github.com/travetto/travetto/tree/main/module/schema/src/data.ts#
|
|
382
|
+
Data utilities for binding values, and type conversion. Currently [DataUtil](https://github.com/travetto/travetto/tree/main/module/schema/src/data.ts#L11) includes:
|
|
383
383
|
* `deepAssign(a, b, mode ?)` which allows for deep assignment of `b` onto `a`, the `mode` determines how aggressive the assignment is, and how flexible it is. `mode` can have any of the following values:
|
|
384
384
|
* `loose`, which is the default is the most lenient. It will not error out, and overwrites will always happen
|
|
385
385
|
* `coerce`, will attempt to force values from `b` to fit the types of `a`, and if it can't it will error out
|
package/__index__.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/schema",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.11",
|
|
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.0-rc.
|
|
30
|
+
"@travetto/registry": "^5.0.0-rc.10"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/transformer": "^5.0.0-rc.
|
|
33
|
+
"@travetto/transformer": "^5.0.0-rc.7"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@travetto/transformer": {
|
package/src/bind-util.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Class,
|
|
1
|
+
import { castTo, Class, classConstruct, asFull, TypedObject, castKey } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { DataUtil } from './data';
|
|
4
4
|
import { AllViewⲐ } from './internal/types';
|
|
@@ -11,6 +11,10 @@ type BindConfig = {
|
|
|
11
11
|
filterValue?: (value: unknown, field: FieldConfig) => boolean;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
function isInstance<T>(o: unknown): o is T {
|
|
15
|
+
return !!o && !DataUtil.isPrimitive(o);
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
/**
|
|
15
19
|
* Utilities for binding objects to schemas
|
|
16
20
|
*/
|
|
@@ -35,8 +39,7 @@ export class BindUtil {
|
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
return val as T;
|
|
42
|
+
return castTo(val);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
/**
|
|
@@ -64,13 +67,11 @@ export class BindUtil {
|
|
|
64
67
|
if (!(name in sub)) {
|
|
65
68
|
sub[name] = typeof key === 'number' ? [] : {};
|
|
66
69
|
}
|
|
67
|
-
|
|
68
|
-
sub = sub[name] as Record<string, unknown>;
|
|
70
|
+
sub = castTo(sub[name]);
|
|
69
71
|
|
|
70
72
|
if (idx && key !== undefined) {
|
|
71
73
|
sub[key] ??= {};
|
|
72
|
-
|
|
73
|
-
sub = sub[key] as Record<string, unknown>;
|
|
74
|
+
sub = castTo(sub[key]);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -89,11 +90,9 @@ export class BindUtil {
|
|
|
89
90
|
let key = (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined));
|
|
90
91
|
sub[name] ??= (typeof key === 'string') ? {} : [];
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
const arrSub = sub[name] as Record<string, unknown>;
|
|
93
|
+
const arrSub: Record<string, unknown> & { length: number } = castTo(sub[name]);
|
|
94
94
|
if (key === undefined) {
|
|
95
|
-
|
|
96
|
-
key = arrSub.length as number;
|
|
95
|
+
key = arrSub.length;
|
|
97
96
|
}
|
|
98
97
|
if (arrSub[key] && DataUtil.isPlainObject(val) && DataUtil.isPlainObject(arrSub[key])) {
|
|
99
98
|
arrSub[key] = DataUtil.deepAssign(arrSub[key], val, 'coerce');
|
|
@@ -127,8 +126,7 @@ export class BindUtil {
|
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
128
|
} else {
|
|
130
|
-
|
|
131
|
-
out[pre] = (value ?? '') as V;
|
|
129
|
+
out[pre] = castTo(value ?? '');
|
|
132
130
|
}
|
|
133
131
|
}
|
|
134
132
|
return out;
|
|
@@ -147,24 +145,19 @@ export class BindUtil {
|
|
|
147
145
|
if (data === null || data === undefined) {
|
|
148
146
|
return data;
|
|
149
147
|
}
|
|
150
|
-
|
|
151
|
-
const cls = SchemaRegistry.resolveInstanceType<T>(cons, data as T);
|
|
148
|
+
const cls = SchemaRegistry.resolveInstanceType<T>(cons, asFull<T>(data));
|
|
152
149
|
if (data instanceof cls) {
|
|
153
|
-
|
|
154
|
-
return data as T;
|
|
150
|
+
return castTo(data);
|
|
155
151
|
} else {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
159
|
-
SchemaRegistry.ensureInstanceTypeField(cls, tgt as T & { type?: string });
|
|
152
|
+
const tgt = classConstruct<T & { type?: string }>(cls);
|
|
153
|
+
SchemaRegistry.ensureInstanceTypeField(cls, tgt);
|
|
160
154
|
|
|
161
155
|
for (const k of TypedObject.keys(tgt)) { // Do not retain undefined fields
|
|
162
156
|
if (tgt[k] === undefined) {
|
|
163
157
|
delete tgt[k];
|
|
164
158
|
}
|
|
165
159
|
}
|
|
166
|
-
|
|
167
|
-
return this.bindSchemaToObject(cls, tgt, data as object, cfg);
|
|
160
|
+
return this.bindSchemaToObject(cls, tgt, data, cfg);
|
|
168
161
|
}
|
|
169
162
|
}
|
|
170
163
|
|
|
@@ -179,14 +172,13 @@ export class BindUtil {
|
|
|
179
172
|
const view = cfg.view ?? AllViewⲐ; // Does not convey
|
|
180
173
|
delete cfg.view;
|
|
181
174
|
|
|
182
|
-
if (!!data &&
|
|
175
|
+
if (!!data && isInstance<T>(data)) {
|
|
183
176
|
const conf = SchemaRegistry.get(cons);
|
|
184
177
|
|
|
185
178
|
// If no configuration
|
|
186
179
|
if (!conf) {
|
|
187
180
|
for (const k of TypedObject.keys(data)) {
|
|
188
|
-
|
|
189
|
-
obj[k] = data[k as keyof typeof data];
|
|
181
|
+
obj[k] = data[k];
|
|
190
182
|
}
|
|
191
183
|
} else {
|
|
192
184
|
|
|
@@ -217,8 +209,7 @@ export class BindUtil {
|
|
|
217
209
|
continue;
|
|
218
210
|
}
|
|
219
211
|
|
|
220
|
-
|
|
221
|
-
let v: unknown = data[inboundField as keyof typeof data];
|
|
212
|
+
let v: unknown = data[castKey<T>(inboundField)];
|
|
222
213
|
|
|
223
214
|
// Filtering values
|
|
224
215
|
if (cfg.filterValue && !cfg.filterValue(v, field)) {
|
|
@@ -248,8 +239,7 @@ export class BindUtil {
|
|
|
248
239
|
}
|
|
249
240
|
}
|
|
250
241
|
|
|
251
|
-
|
|
252
|
-
obj[schemaFieldName as keyof typeof obj] = v as (typeof obj)[keyof typeof obj];
|
|
242
|
+
obj[castKey<T>(schemaFieldName)] = castTo(v);
|
|
253
243
|
|
|
254
244
|
if (field.accessor) {
|
|
255
245
|
Object.defineProperty(obj, schemaFieldName, {
|
package/src/data.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isNumberObject as isNum, isBooleanObject as isBool, isStringObject as isStr } from 'node:util/types';
|
|
2
2
|
|
|
3
|
-
import { Class,
|
|
3
|
+
import { asConstructable, castTo, Class, asFull, TypedObject } from '@travetto/runtime';
|
|
4
|
+
import { UnknownType } from './internal/types';
|
|
4
5
|
|
|
5
6
|
const REGEX_PAT = /[\/](.*)[\/](i|g|m|s)?/;
|
|
6
7
|
|
|
@@ -62,15 +63,13 @@ export class DataUtil {
|
|
|
62
63
|
if (isArrA !== isArrB || isSimpA !== isSimpB) {
|
|
63
64
|
throw new Error(`Cannot merge differing types ${a} and ${b}`);
|
|
64
65
|
}
|
|
65
|
-
if (
|
|
66
|
+
if (Array.isArray(b)) { // Arrays
|
|
66
67
|
ret = a; // Write onto A
|
|
67
68
|
if (mode === 'replace') {
|
|
68
69
|
ret = b;
|
|
69
70
|
} else {
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
73
|
-
const bArr = b as unknown[];
|
|
71
|
+
const retArr: unknown[] = castTo(ret);
|
|
72
|
+
const bArr = b;
|
|
74
73
|
for (let i = 0; i < bArr.length; i++) {
|
|
75
74
|
retArr[i] = this.#deepAssignRaw(retArr[i], bArr[i], mode);
|
|
76
75
|
}
|
|
@@ -83,16 +82,13 @@ export class DataUtil {
|
|
|
83
82
|
if (mode === 'strict') { // Bail on strict
|
|
84
83
|
throw new Error(`Cannot merge ${a} [${typeof a}] with ${b} [${typeof b}]`);
|
|
85
84
|
} else if (mode === 'coerce') { // Force on coerce
|
|
86
|
-
|
|
87
|
-
ret = this.coerceType(b, (a as ClassInstance).constructor, false);
|
|
85
|
+
ret = this.coerceType(b, asConstructable(a).constructor, false);
|
|
88
86
|
}
|
|
89
87
|
}
|
|
90
88
|
} else { // Object merge
|
|
91
89
|
ret = a;
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
95
|
-
const retObj = ret as Record<string, unknown>;
|
|
90
|
+
const bObj: Record<string, unknown> = castTo(b);
|
|
91
|
+
const retObj: Record<string, unknown> = castTo(ret);
|
|
96
92
|
|
|
97
93
|
for (const key of Object.keys(bObj)) {
|
|
98
94
|
retObj[key] = this.#deepAssignRaw(retObj[key], bObj[key], mode);
|
|
@@ -125,11 +121,13 @@ export class DataUtil {
|
|
|
125
121
|
*/
|
|
126
122
|
static coerceType(input: unknown, type: typeof String, strict?: boolean): string;
|
|
127
123
|
static coerceType(input: unknown, type: typeof Number, strict?: boolean): number;
|
|
124
|
+
static coerceType(input: unknown, type: typeof BigInt, strict?: boolean): bigint;
|
|
128
125
|
static coerceType(input: unknown, type: typeof Boolean, strict?: boolean): boolean;
|
|
129
126
|
static coerceType(input: unknown, type: typeof Date, strict?: boolean): Date;
|
|
130
127
|
static coerceType(input: unknown, type: typeof RegExp, strict?: boolean): RegExp;
|
|
131
|
-
static coerceType
|
|
132
|
-
static coerceType(input: unknown, type: Class<
|
|
128
|
+
static coerceType(input: unknown, type: typeof UnknownType, strict?: boolean): unknown;
|
|
129
|
+
static coerceType<T>(input: unknown, type: Class<T> | Function, strict?: boolean): T;
|
|
130
|
+
static coerceType(input: unknown, type: Class<unknown> | Function, strict = true): unknown {
|
|
133
131
|
// Do nothing
|
|
134
132
|
if (input === null || input === undefined) {
|
|
135
133
|
return input;
|
|
@@ -143,14 +141,17 @@ export class DataUtil {
|
|
|
143
141
|
case Date: {
|
|
144
142
|
let res: Date | undefined;
|
|
145
143
|
if (typeof input === 'object' && 'toDate' in input && typeof input.toDate === 'function') {
|
|
146
|
-
|
|
147
|
-
res = input.toDate() as Date;
|
|
144
|
+
res = castTo(input.toDate());
|
|
148
145
|
} else {
|
|
149
|
-
res =
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
res = input instanceof Date ?
|
|
147
|
+
input :
|
|
148
|
+
typeof input === 'number' ?
|
|
149
|
+
new Date(input) :
|
|
150
|
+
(typeof input === 'string' && /^[-]?\d+$/.test(input)) ?
|
|
151
|
+
new Date(parseInt(input, 10)) :
|
|
152
|
+
new Date(input.toString());
|
|
152
153
|
}
|
|
153
|
-
if (strict && Number.isNaN(res.getTime())) {
|
|
154
|
+
if (strict && res && Number.isNaN(res.getTime())) {
|
|
154
155
|
throw new Error(`Invalid date value: ${input}`);
|
|
155
156
|
}
|
|
156
157
|
return res;
|
|
@@ -162,6 +163,20 @@ export class DataUtil {
|
|
|
162
163
|
}
|
|
163
164
|
return res;
|
|
164
165
|
}
|
|
166
|
+
case BigInt: {
|
|
167
|
+
if (typeof input === 'bigint') {
|
|
168
|
+
return input;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
return BigInt((typeof input === 'boolean' || typeof input === 'number') ?
|
|
172
|
+
input : `${input}`.replace(/n$/i, ''));
|
|
173
|
+
} catch {
|
|
174
|
+
if (strict) {
|
|
175
|
+
throw new Error(`Invalid numeric value: ${input}`);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
165
180
|
case Boolean: {
|
|
166
181
|
const match = `${input}`.match(/^((?<TRUE>true|yes|1|on)|false|no|off|0)$/i);
|
|
167
182
|
if (strict && !match) {
|
|
@@ -186,6 +201,9 @@ export class DataUtil {
|
|
|
186
201
|
return;
|
|
187
202
|
}
|
|
188
203
|
}
|
|
204
|
+
case UnknownType: {
|
|
205
|
+
return input;
|
|
206
|
+
}
|
|
189
207
|
case Object: {
|
|
190
208
|
if (!strict || this.isPlainObject(input)) {
|
|
191
209
|
return input;
|
|
@@ -207,9 +225,8 @@ export class DataUtil {
|
|
|
207
225
|
* Clone top level properties to a new object
|
|
208
226
|
* @param o Object to clone
|
|
209
227
|
*/
|
|
210
|
-
static shallowClone<T
|
|
211
|
-
|
|
212
|
-
return (Array.isArray(a) ? a.slice(0) : (this.isSimpleValue(a) ? a : { ...(a as {}) })) as T;
|
|
228
|
+
static shallowClone<T>(a: T): T {
|
|
229
|
+
return castTo(Array.isArray(a) ? a.slice(0) : (this.isSimpleValue(a) ? a : { ...castTo<object>(a) }));
|
|
213
230
|
}
|
|
214
231
|
|
|
215
232
|
/**
|
|
@@ -222,8 +239,7 @@ export class DataUtil {
|
|
|
222
239
|
if (!a || this.isSimpleValue(a)) {
|
|
223
240
|
throw new Error(`Cannot merge onto a simple value, ${a}`);
|
|
224
241
|
}
|
|
225
|
-
|
|
226
|
-
return this.#deepAssignRaw(a, b, mode) as T & U;
|
|
242
|
+
return castTo(this.#deepAssignRaw(a, b, mode));
|
|
227
243
|
}
|
|
228
244
|
|
|
229
245
|
/**
|
|
@@ -245,8 +261,7 @@ export class DataUtil {
|
|
|
245
261
|
}
|
|
246
262
|
}
|
|
247
263
|
}
|
|
248
|
-
|
|
249
|
-
return out as T;
|
|
264
|
+
return asFull(out);
|
|
250
265
|
} else {
|
|
251
266
|
return obj;
|
|
252
267
|
}
|
package/src/decorator/common.ts
CHANGED
package/src/decorator/field.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { ClassInstance } from '@travetto/
|
|
1
|
+
import { Any, castTo, ClassInstance } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { SchemaRegistry } from '../service/registry';
|
|
4
4
|
import { CommonRegExp } from '../validate/regexp';
|
|
5
5
|
import { ClassList, FieldConfig } from '../service/types';
|
|
6
6
|
|
|
7
|
-
type PropType<V> = (<T extends Partial<Record<K, V>>, K extends string>(t: T, k: K, idx?: number) => void) & {
|
|
7
|
+
type PropType<V> = (<T extends Partial<Record<K, V>>, K extends string>(t: T, k: K, idx?: TypedPropertyDescriptor<Any> | number) => void) & {
|
|
8
8
|
Param: (t: unknown, k: string, idx: number) => void;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
function prop<V>(obj: Partial<FieldConfig>): PropType<V> {
|
|
12
|
-
const fn = (t: ClassInstance, k: string, idx?: number): void => {
|
|
12
|
+
const fn = (t: ClassInstance, k: string, idx?: number | TypedPropertyDescriptor<Any>): void => {
|
|
13
13
|
if (idx !== undefined && typeof idx === 'number') {
|
|
14
14
|
SchemaRegistry.registerPendingParamFacet(t.constructor, k, idx, obj);
|
|
15
15
|
} else {
|
|
@@ -18,8 +18,7 @@ function prop<V>(obj: Partial<FieldConfig>): PropType<V> {
|
|
|
18
18
|
};
|
|
19
19
|
fn.Param = fn;
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
return fn as PropType<V>;
|
|
21
|
+
return castTo(fn);
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/**
|
|
@@ -29,7 +28,7 @@ function prop<V>(obj: Partial<FieldConfig>): PropType<V> {
|
|
|
29
28
|
* @augments `@travetto/schema:Field`
|
|
30
29
|
*/
|
|
31
30
|
export function Field(type: ClassList, ...config: Partial<FieldConfig>[]) {
|
|
32
|
-
return (f: ClassInstance, k: string, idx?: number): void => {
|
|
31
|
+
return (f: ClassInstance, k: string, idx?: number | TypedPropertyDescriptor<Any>): void => {
|
|
33
32
|
if (idx !== undefined && typeof idx === 'number') {
|
|
34
33
|
SchemaRegistry.registerPendingParamConfig(f.constructor, k, idx, type, Object.assign({}, ...config));
|
|
35
34
|
} else {
|
package/src/decorator/schema.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Class } from '@travetto/
|
|
1
|
+
import { castTo, Class, DeepPartial } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { BindUtil } from '../bind-util';
|
|
4
4
|
import { SchemaRegistry } from '../service/registry';
|
|
5
5
|
import { ClassConfig, ViewFieldsConfig } from '../service/types';
|
|
6
|
-
import { DeepPartial } from '../types';
|
|
7
6
|
import { ValidatorFn } from '../validate/types';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Register a class as a Schema
|
|
11
10
|
*
|
|
11
|
+
*
|
|
12
12
|
* @augments `@travetto/schema:Schema`
|
|
13
13
|
*/
|
|
14
14
|
export function Schema(cfg?: Partial<Pick<ClassConfig, 'subTypeName' | 'subTypeField' | 'baseType'>>) { // Auto is used during compilation
|
|
@@ -28,8 +28,7 @@ export function Schema(cfg?: Partial<Pick<ClassConfig, 'subTypeName' | 'subTypeF
|
|
|
28
28
|
*/
|
|
29
29
|
export function Validator<T>(fn: ValidatorFn<T, string>) {
|
|
30
30
|
return (target: Class<T>): void => {
|
|
31
|
-
|
|
32
|
-
SchemaRegistry.getOrCreatePending(target).validators!.push(fn as ValidatorFn<unknown, unknown>);
|
|
31
|
+
SchemaRegistry.getOrCreatePending(target).validators!.push(castTo(fn));
|
|
33
32
|
};
|
|
34
33
|
}
|
|
35
34
|
|
package/src/global.d.ts
CHANGED
package/src/internal/types.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const AllViewⲐ: unique symbol = Symbol.for('@travetto/schema:all');
|
|
1
|
+
export const AllViewⲐ: unique symbol = Symbol.for('@travetto/schema:all');
|
|
2
|
+
export class UnknownType { }
|
package/src/name.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describeFunction } from '@travetto/runtime';
|
|
2
2
|
import { ClassConfig } from './service/types';
|
|
3
3
|
|
|
4
4
|
const SYN_RE = /(__)(\d+)Ⲑsyn$/;
|
|
@@ -19,7 +19,7 @@ export class SchemaNameResolver {
|
|
|
19
19
|
|
|
20
20
|
getName(schema: ClassConfig): string {
|
|
21
21
|
const id = schema.class.Ⲑid;
|
|
22
|
-
if (
|
|
22
|
+
if (describeFunction(schema.class)?.synthetic && SYN_RE.test(schema.class.name)) {
|
|
23
23
|
if (!this.#schemaIdToName.has(id)) {
|
|
24
24
|
const name = schema.class.name.replace(SYN_RE, (_, pref, uid) => `__${(+uid % this.#base).toString().padStart(this.#digits, '0')}`);
|
|
25
25
|
this.#schemaIdToName.set(id, name);
|
package/src/service/changes.ts
CHANGED
package/src/service/registry.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Class, AppError } from '@travetto/base';
|
|
1
|
+
import { Class, AppError, describeFunction, castTo, classConstruct, asFull, castKey } from '@travetto/runtime';
|
|
3
2
|
import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
|
|
4
3
|
|
|
5
4
|
import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig, ViewConfig } from './types';
|
|
@@ -52,8 +51,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
52
51
|
*/
|
|
53
52
|
getMetadata<K>(cls: Class, key: symbol): K | undefined {
|
|
54
53
|
const cfg = this.get(cls);
|
|
55
|
-
|
|
56
|
-
return cfg.metadata?.[key] as K;
|
|
54
|
+
return castTo(cfg.metadata?.[key]);
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
/**
|
|
@@ -65,8 +63,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
65
63
|
*/
|
|
66
64
|
getOrCreatePendingMetadata<K>(cls: Class, key: symbol, value: K): K {
|
|
67
65
|
const cfg = this.getOrCreatePending(cls);
|
|
68
|
-
|
|
69
|
-
return ((cfg.metadata ??= {})[key] ??= value) as K;
|
|
66
|
+
return castTo((cfg.metadata ??= {})[key] ??= value);
|
|
70
67
|
}
|
|
71
68
|
|
|
72
69
|
/**
|
|
@@ -74,11 +71,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
74
71
|
*/
|
|
75
72
|
ensureInstanceTypeField<T>(cls: Class, o: T): void {
|
|
76
73
|
const schema = this.get(cls);
|
|
77
|
-
|
|
78
|
-
const typeField = (schema.subTypeField) as keyof T;
|
|
74
|
+
const typeField = castKey<T>(schema.subTypeField);
|
|
79
75
|
if (schema.subTypeName && typeField in schema.views[AllViewⲐ].schema && !o[typeField]) { // Do we have a type field defined
|
|
80
|
-
//
|
|
81
|
-
o[typeField] = schema.subTypeName; // Assign if missing
|
|
76
|
+
o[typeField] = castTo(schema.subTypeName); // Assign if missing
|
|
82
77
|
}
|
|
83
78
|
}
|
|
84
79
|
|
|
@@ -113,11 +108,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
113
108
|
const baseSchema = this.get(base);
|
|
114
109
|
|
|
115
110
|
if (clsSchema.subTypeName || baseSchema.baseType) { // We have a sub type
|
|
116
|
-
|
|
117
|
-
const type = o[baseSchema.subTypeField] ?? clsSchema.subTypeName ?? baseSchema.subTypeName;
|
|
111
|
+
const type = castTo<string>(o[castKey<T>(baseSchema.subTypeField)]) ?? clsSchema.subTypeName ?? baseSchema.subTypeName;
|
|
118
112
|
const ret = this.#subTypes.get(base)!.get(type)!;
|
|
119
|
-
|
|
120
|
-
if (ret && !(new ret() instanceof cls)) {
|
|
113
|
+
if (ret && !(classConstruct(ret) instanceof cls)) {
|
|
121
114
|
throw new AppError(`Resolved class ${ret.name} is not assignable to ${cls.name}`);
|
|
122
115
|
}
|
|
123
116
|
return ret;
|
|
@@ -188,7 +181,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
188
181
|
class: cls,
|
|
189
182
|
validators: [],
|
|
190
183
|
subTypeField: 'type',
|
|
191
|
-
baseType:
|
|
184
|
+
baseType: describeFunction(cls)?.abstract,
|
|
192
185
|
metadata: {},
|
|
193
186
|
methods: {},
|
|
194
187
|
views: {
|
|
@@ -238,8 +231,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
238
231
|
if (!this.#pendingViews.has(target)) {
|
|
239
232
|
this.#pendingViews.set(target, new Map());
|
|
240
233
|
}
|
|
241
|
-
|
|
242
|
-
const generalConfig = fields as unknown as ViewFieldsConfig<unknown>;
|
|
234
|
+
const generalConfig: ViewFieldsConfig<unknown> = castTo(fields);
|
|
243
235
|
this.#pendingViews.get(target)!.set(view, generalConfig);
|
|
244
236
|
}
|
|
245
237
|
|
|
@@ -263,6 +255,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
263
255
|
if (config.specifiers) {
|
|
264
256
|
config.specifiers = [...params[idx]?.specifiers ?? [], ...config.specifiers];
|
|
265
257
|
}
|
|
258
|
+
if (config.enum?.values) {
|
|
259
|
+
config.enum.values = config.enum.values.slice().sort();
|
|
260
|
+
}
|
|
266
261
|
|
|
267
262
|
params[idx] = {
|
|
268
263
|
// @ts-expect-error
|
|
@@ -287,8 +282,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
287
282
|
if (!allViewConf.schema[prop]) {
|
|
288
283
|
allViewConf.fields.push(prop);
|
|
289
284
|
// Partial config while building
|
|
290
|
-
|
|
291
|
-
allViewConf.schema[prop] = {} as FieldConfig;
|
|
285
|
+
allViewConf.schema[prop] = asFull<FieldConfig>({});
|
|
292
286
|
}
|
|
293
287
|
if (config.aliases) {
|
|
294
288
|
config.aliases = [...allViewConf.schema[prop].aliases ?? [], ...config.aliases];
|
|
@@ -296,6 +290,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
|
|
|
296
290
|
if (config.specifiers) {
|
|
297
291
|
config.specifiers = [...allViewConf.schema[prop].specifiers ?? [], ...config.specifiers];
|
|
298
292
|
}
|
|
293
|
+
if (config.enum?.values) {
|
|
294
|
+
config.enum.values = config.enum.values.slice().sort();
|
|
295
|
+
}
|
|
299
296
|
|
|
300
297
|
Object.assign(allViewConf.schema[prop], config);
|
|
301
298
|
|
package/src/service/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Class } from '@travetto/
|
|
2
|
-
import { Primitive } from '@travetto/schema';
|
|
1
|
+
import { Any, Class, Primitive } from '@travetto/runtime';
|
|
3
2
|
|
|
4
3
|
import { AllViewⲐ } from '../internal/types';
|
|
5
4
|
import { ValidatorFn } from '../validate/types';
|
|
@@ -66,7 +65,7 @@ export interface ClassConfig extends DescribableConfig {
|
|
|
66
65
|
/**
|
|
67
66
|
* Global validators
|
|
68
67
|
*/
|
|
69
|
-
validators: ValidatorFn<
|
|
68
|
+
validators: ValidatorFn<Any, unknown>[];
|
|
70
69
|
/**
|
|
71
70
|
* Is the class a base type
|
|
72
71
|
*/
|
package/src/validate/error.ts
CHANGED
package/src/validate/regexp.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Class, ClassInstance, TypedObject } from '@travetto/
|
|
1
|
+
import { castKey, castTo, Class, ClassInstance, TypedObject } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { FieldConfig, SchemaConfig } from '../service/types';
|
|
4
4
|
import { SchemaRegistry } from '../service/registry';
|
|
@@ -18,6 +18,14 @@ function resolveSchema<T>(base: Class<T>, o: T, view?: string): SchemaConfig {
|
|
|
18
18
|
).schema;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function isClassInstance<T>(o: unknown): o is ClassInstance<T> {
|
|
22
|
+
return !DataUtil.isPlainObject(o) && o !== null && typeof o === 'object' && !!o.constructor;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isRangeValue(o: unknown): o is number | string | Date {
|
|
26
|
+
return typeof o === 'string' || typeof o === 'number' || o instanceof Date;
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
declare global {
|
|
22
30
|
interface RegExp {
|
|
23
31
|
name?: string;
|
|
@@ -42,8 +50,7 @@ export class SchemaValidator {
|
|
|
42
50
|
const fields = TypedObject.keys<SchemaConfig>(schema);
|
|
43
51
|
for (const field of fields) {
|
|
44
52
|
if (schema[field].access !== 'readonly') { // Do not validate readonly fields
|
|
45
|
-
|
|
46
|
-
errors = errors.concat(this.#validateFieldSchema(schema[field], o[field as keyof T], relative));
|
|
53
|
+
errors = errors.concat(this.#validateFieldSchema(schema[field], o[castKey<T>(field)], relative));
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -174,18 +181,15 @@ export class SchemaValidator {
|
|
|
174
181
|
criteria.push(['maxlength', field.maxlength]);
|
|
175
182
|
}
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
if (field.enum && !field.enum.values.includes(value as string)) {
|
|
184
|
+
if (field.enum && !field.enum.values.includes(castTo(value))) {
|
|
179
185
|
criteria.push(['enum', field.enum]);
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
|
|
183
|
-
if (field.min && this.#validateRange(field, 'min', value as number)) {
|
|
188
|
+
if (field.min && (!isRangeValue(value) || this.#validateRange(field, 'min', value))) {
|
|
184
189
|
criteria.push(['min', field.min]);
|
|
185
190
|
}
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
if (field.max && this.#validateRange(field, 'max', value as number)) {
|
|
192
|
+
if (field.max && (!isRangeValue(value) || this.#validateRange(field, 'max', value))) {
|
|
189
193
|
criteria.push(['max', field.max]);
|
|
190
194
|
}
|
|
191
195
|
|
|
@@ -268,10 +272,8 @@ export class SchemaValidator {
|
|
|
268
272
|
* @param view The optional view to limit the scope to
|
|
269
273
|
*/
|
|
270
274
|
static async validate<T>(cls: Class<T>, o: T, view?: string): Promise<T> {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
274
|
-
throw new TypeMismatchError(cls.name, (o as ClassInstance).constructor.name);
|
|
275
|
+
if (isClassInstance(o) && !(o instanceof cls || cls.Ⲑid === o.constructor.Ⲑid)) {
|
|
276
|
+
throw new TypeMismatchError(cls.name, o.constructor.name);
|
|
275
277
|
}
|
|
276
278
|
cls = SchemaRegistry.resolveInstanceType(cls, o);
|
|
277
279
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import {
|
|
3
3
|
type AnyType, DeclarationUtil, LiteralUtil,
|
|
4
|
-
DecoratorUtil, DocUtil, ParamDocumentation, TransformerState
|
|
4
|
+
DecoratorUtil, DocUtil, ParamDocumentation, TransformerState, transformCast
|
|
5
5
|
} from '@travetto/transformer';
|
|
6
6
|
|
|
7
7
|
const SCHEMA_MOD = '@travetto/schema/src/decorator/schema';
|
|
8
8
|
const FIELD_MOD = '@travetto/schema/src/decorator/field';
|
|
9
9
|
const COMMON_MOD = '@travetto/schema/src/decorator/common';
|
|
10
|
+
const TYPES_FILE = '@travetto/schema/src/internal/types';
|
|
10
11
|
|
|
11
12
|
export class SchemaTransformUtil {
|
|
12
13
|
|
|
@@ -27,6 +28,10 @@ export class SchemaTransformUtil {
|
|
|
27
28
|
}
|
|
28
29
|
break;
|
|
29
30
|
}
|
|
31
|
+
case 'unknown': {
|
|
32
|
+
const imp = state.importFile(TYPES_FILE);
|
|
33
|
+
return state.createAccess(imp.ident, 'UnknownType');
|
|
34
|
+
}
|
|
30
35
|
case 'shape': {
|
|
31
36
|
const uniqueId = state.generateUniqueIdentifier(node, type);
|
|
32
37
|
|
|
@@ -49,11 +54,16 @@ export class SchemaTransformUtil {
|
|
|
49
54
|
this.computeField(state, state.factory.createPropertyDeclaration(
|
|
50
55
|
[], k,
|
|
51
56
|
v.undefinable || v.nullable ? state.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
|
|
52
|
-
undefined, undefined
|
|
57
|
+
v.key === 'unknown' ? state.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) : undefined, undefined
|
|
53
58
|
), { type: v, root })
|
|
54
59
|
)
|
|
55
60
|
);
|
|
56
|
-
cls.getText = (): string =>
|
|
61
|
+
cls.getText = (): string => [
|
|
62
|
+
`class ${uniqueId} {`,
|
|
63
|
+
...Object.entries(type.fieldTypes)
|
|
64
|
+
.map(([k, v]) => ` ${k}${v.nullable ? '?' : ''}: ${v.name};`),
|
|
65
|
+
'}'
|
|
66
|
+
].join('\n');
|
|
57
67
|
state.addStatements([cls], root || node);
|
|
58
68
|
}
|
|
59
69
|
return id;
|
|
@@ -65,7 +75,6 @@ export class SchemaTransformUtil {
|
|
|
65
75
|
break;
|
|
66
76
|
}
|
|
67
77
|
case 'foreign':
|
|
68
|
-
case 'unknown':
|
|
69
78
|
default: {
|
|
70
79
|
// Object
|
|
71
80
|
}
|
|
@@ -121,8 +130,7 @@ export class SchemaTransformUtil {
|
|
|
121
130
|
// If we have a union type
|
|
122
131
|
if (primaryExpr.key === 'union') {
|
|
123
132
|
const values = primaryExpr.subTypes.map(x => x.key === 'literal' ? x.value : undefined)
|
|
124
|
-
.filter(x => x !== undefined && x !== null)
|
|
125
|
-
.sort();
|
|
133
|
+
.filter(x => x !== undefined && x !== null);
|
|
126
134
|
|
|
127
135
|
if (values.length === primaryExpr.subTypes.length) {
|
|
128
136
|
attrs.push(state.factory.createPropertyAssignment('enum', state.fromLiteral({
|
|
@@ -178,6 +186,7 @@ export class SchemaTransformUtil {
|
|
|
178
186
|
state.createDecorator(FIELD_MOD, 'Field', ...params)
|
|
179
187
|
];
|
|
180
188
|
|
|
189
|
+
let ret: unknown;
|
|
181
190
|
if (ts.isPropertyDeclaration(node)) {
|
|
182
191
|
const comments = DocUtil.describeDocs(node);
|
|
183
192
|
if (comments.description) {
|
|
@@ -186,22 +195,19 @@ export class SchemaTransformUtil {
|
|
|
186
195
|
})));
|
|
187
196
|
}
|
|
188
197
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
newModifiers, node.name, node.questionToken, node.type, node.initializer) as T;
|
|
198
|
+
ret = state.factory.updatePropertyDeclaration(node,
|
|
199
|
+
newModifiers, node.name, node.questionToken, node.type, node.initializer);
|
|
192
200
|
} else if (ts.isParameter(node)) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
newModifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer) as T;
|
|
201
|
+
ret = state.factory.updateParameterDeclaration(node,
|
|
202
|
+
newModifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer);
|
|
196
203
|
} else if (ts.isGetAccessorDeclaration(node)) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
newModifiers, node.name, node.parameters, node.type, node.body) as T;
|
|
204
|
+
ret = state.factory.updateGetAccessorDeclaration(node,
|
|
205
|
+
newModifiers, node.name, node.parameters, node.type, node.body);
|
|
200
206
|
} else {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
newModifiers, node.name, node.parameters, node.body) as T;
|
|
207
|
+
ret = state.factory.updateSetAccessorDeclaration(node,
|
|
208
|
+
newModifiers, node.name, node.parameters, node.body);
|
|
204
209
|
}
|
|
210
|
+
return transformCast(ret);
|
|
205
211
|
}
|
|
206
212
|
|
|
207
213
|
/**
|
package/src/types.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export type Primitive = number | bigint | boolean | string | Date;
|
|
2
|
-
|
|
3
|
-
export type DeepPartial<T> = {
|
|
4
|
-
[P in keyof T]?: (T[P] extends (Primitive | undefined) ? (T[P] | undefined) :
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
-
(T[P] extends any[] ? (DeepPartial<T[P][number]> | null | undefined)[] : DeepPartial<T[P]>));
|
|
7
|
-
};
|