@travetto/schema 6.0.1 → 7.0.0-rc.0
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 +30 -26
- package/__index__.ts +4 -2
- package/package.json +3 -3
- package/src/bind-util.ts +40 -37
- package/src/decorator/common.ts +32 -16
- package/src/decorator/field.ts +21 -184
- package/src/decorator/input.ts +207 -0
- package/src/decorator/method.ts +28 -0
- package/src/decorator/schema.ts +36 -29
- package/src/name.ts +2 -2
- package/src/service/changes.ts +27 -30
- package/src/service/registry-adapter.ts +340 -0
- package/src/service/registry-index.ts +240 -0
- package/src/service/types.ts +113 -63
- package/src/validate/types.ts +4 -0
- package/src/validate/validator.ts +70 -64
- package/support/transformer/util.ts +147 -61
- package/support/transformer.schema.ts +137 -49
- package/src/service/registry.ts +0 -501
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ This module's purpose is to allow for proper declaration and validation of data
|
|
|
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
|
|
|
20
20
|
## Registration
|
|
21
|
-
The registry's schema information is defined by [Typescript](https://typescriptlang.org) AST and only applies to classes registered with the [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#
|
|
21
|
+
The registry's schema information is defined by [Typescript](https://typescriptlang.org) AST and only applies to classes registered with the [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L19) decoration.
|
|
22
22
|
|
|
23
23
|
### Classes
|
|
24
24
|
The module utilizes AST transformations to collect schema information, and facilitate the registration process without user intervention. The class can also be described using providing a:
|
|
@@ -26,7 +26,7 @@ The module utilizes AST transformations to collect schema information, and facil
|
|
|
26
26
|
* `description` - detailed description of the schema
|
|
27
27
|
* `examples` - A set of examples as [JSON](https://www.json.org) or [YAML](https://en.wikipedia.org/wiki/YAML)
|
|
28
28
|
|
|
29
|
-
The `title` will be picked up from the [JSDoc](http://usejsdoc.org/about-getting-started.html) comments, and additionally all fields can be set using the [@Describe](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/common.ts#
|
|
29
|
+
The `title` will be picked up from the [JSDoc](http://usejsdoc.org/about-getting-started.html) comments, and additionally all fields can be set using the [@Describe](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/common.ts#L12) decorator.
|
|
30
30
|
|
|
31
31
|
**Code: Sample User Schema**
|
|
32
32
|
```typescript
|
|
@@ -64,28 +64,28 @@ User:
|
|
|
64
64
|
|
|
65
65
|
### Fields
|
|
66
66
|
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:
|
|
67
|
-
* [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
68
|
-
* [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
69
|
-
* [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
70
|
-
* [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
71
|
-
* [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
72
|
-
* [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
73
|
-
* [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
74
|
-
* [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
75
|
-
* [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
76
|
-
* [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
77
|
-
* [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
78
|
-
* [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
79
|
-
* [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
80
|
-
* [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
81
|
-
* [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
82
|
-
* [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
83
|
-
* [@LongText](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
84
|
-
* [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
85
|
-
* [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
86
|
-
* [@Secret](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
87
|
-
* [@Specifier](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/
|
|
88
|
-
* [@
|
|
67
|
+
* [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L21) defines a field that will be serialized.
|
|
68
|
+
* [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L47) defines a that field should be required
|
|
69
|
+
* [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L56) defines the allowable values that a field can have
|
|
70
|
+
* [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L82) defines a regular expression that the field value should match
|
|
71
|
+
* [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L91) enforces min length of a string
|
|
72
|
+
* [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L102) enforces max length of a string
|
|
73
|
+
* [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L91) enforces min value for a date or a number
|
|
74
|
+
* [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L102) enforces max value for a date or a number
|
|
75
|
+
* [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L132) ensures string field matches basic email regex
|
|
76
|
+
* [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L140) ensures string field matches basic telephone regex
|
|
77
|
+
* [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L148) ensures string field matches basic url regex
|
|
78
|
+
* [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/common.ts#L41) exclude from auto schema registration
|
|
79
|
+
* [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L164) ensures number passed in is only a whole number
|
|
80
|
+
* [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L171) ensures number passed in allows fractional values
|
|
81
|
+
* [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L185) provides support for standard currency
|
|
82
|
+
* [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L66) indicates that a field is expecting natural language input, not just discrete values
|
|
83
|
+
* [@LongText](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L73) same as text, but expects longer form content
|
|
84
|
+
* [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L37) defines a that field should not be bindable external to the class
|
|
85
|
+
* [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L30) defines a that field should not be exported in serialization, but that it can be bound to
|
|
86
|
+
* [@Secret](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L45) marks a field as being sensitive. This is used by certain logging activities to ensure sensitive information is not logged out.
|
|
87
|
+
* [@Specifier](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L193) attributes additional specifiers to a field, allowing for more specification beyond just the field's type.
|
|
88
|
+
* [@DiscriminatorField](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L200) allows for promoting a given field as the owner of the sub type discriminator.
|
|
89
89
|
|
|
90
90
|
Additionally, schemas can be nested to form more complex data structures that are able to bound and validated.
|
|
91
91
|
|
|
@@ -93,7 +93,7 @@ Just like the class, all fields can be defined with
|
|
|
93
93
|
* `description` - detailed description of the schema
|
|
94
94
|
* `examples` - A set of examples as [JSON](https://www.json.org) or [YAML](https://en.wikipedia.org/wiki/YAML)
|
|
95
95
|
|
|
96
|
-
And similarly, the `description` will be picked up from the [JSDoc](http://usejsdoc.org/about-getting-started.html) comments, and additionally all fields can be set using the [@Describe](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/common.ts#
|
|
96
|
+
And similarly, the `description` will be picked up from the [JSDoc](http://usejsdoc.org/about-getting-started.html) comments, and additionally all fields can be set using the [@Describe](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/common.ts#L12) decorator.
|
|
97
97
|
|
|
98
98
|
### Parameters
|
|
99
99
|
Parameters are available in certain scenarios (e.g. [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative support for creating Web Applications") endpoints and [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") main methods). In these scenarios, all of the field decorators are valid, but need to be called slightly differently to pass the typechecker. The simple solution is to use the `Arg` field of the decorator to convince Typescript its the correct type.
|
|
@@ -115,7 +115,7 @@ export class ParamUsage {
|
|
|
115
115
|
At runtime, once a schema is registered, a programmer can utilize this structure to perform specific operations. Specifically binding and validation.
|
|
116
116
|
|
|
117
117
|
### Binding
|
|
118
|
-
Binding is a very simple operation, as it takes in a class registered as as [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#
|
|
118
|
+
Binding is a very simple operation, as it takes in a class registered as as [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L19) and a JS object that will be the source of the binding. Given the schema:
|
|
119
119
|
|
|
120
120
|
**Code: Sub Schemas via Address**
|
|
121
121
|
```typescript
|
|
@@ -302,6 +302,10 @@ export interface ValidationError {
|
|
|
302
302
|
* The type of the field
|
|
303
303
|
*/
|
|
304
304
|
type?: string;
|
|
305
|
+
/**
|
|
306
|
+
* Source of the error
|
|
307
|
+
*/
|
|
308
|
+
source?: string;
|
|
305
309
|
}
|
|
306
310
|
```
|
|
307
311
|
|
package/__index__.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { } from './src/global.d.ts';
|
|
2
2
|
export * from './src/decorator/field.ts';
|
|
3
|
+
export * from './src/decorator/input.ts';
|
|
3
4
|
export * from './src/decorator/schema.ts';
|
|
5
|
+
export * from './src/decorator/method.ts';
|
|
4
6
|
export * from './src/decorator/common.ts';
|
|
5
7
|
export * from './src/service/changes.ts';
|
|
6
|
-
export * from './src/service/registry.ts';
|
|
7
8
|
export * from './src/service/types.ts';
|
|
8
9
|
export * from './src/validate/messages.ts';
|
|
9
10
|
export * from './src/validate/regexp.ts';
|
|
@@ -13,4 +14,5 @@ export * from './src/validate/types.ts';
|
|
|
13
14
|
export * from './src/bind-util.ts';
|
|
14
15
|
export * from './src/data.ts';
|
|
15
16
|
export * from './src/name.ts';
|
|
16
|
-
export * from './src/types.ts';
|
|
17
|
+
export * from './src/types.ts';
|
|
18
|
+
export * from './src/service/registry-index.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/schema",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-rc.0",
|
|
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": "^
|
|
30
|
+
"@travetto/registry": "^7.0.0-rc.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/transformer": "^
|
|
33
|
+
"@travetto/transformer": "^7.0.0-rc.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@travetto/transformer": {
|
package/src/bind-util.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { castTo, Class, classConstruct, asFull, TypedObject, castKey } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { DataUtil } from './data.ts';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { SchemaInputConfig, SchemaParameterConfig, SchemaFieldMap } from './service/types.ts';
|
|
5
|
+
import { SchemaRegistryIndex } from './service/registry-index.ts';
|
|
6
6
|
|
|
7
7
|
type BindConfig = {
|
|
8
8
|
view?: string;
|
|
9
|
-
|
|
10
|
-
filterValue?: (value: unknown,
|
|
9
|
+
filterInput?: (input: SchemaInputConfig) => boolean;
|
|
10
|
+
filterValue?: (value: unknown, input: SchemaInputConfig) => boolean;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
function isInstance<T>(o: unknown): o is T {
|
|
@@ -24,7 +24,7 @@ export class BindUtil {
|
|
|
24
24
|
* @param conf The field config to coerce to
|
|
25
25
|
* @param val The provided value
|
|
26
26
|
*/
|
|
27
|
-
static #coerceType<T>(conf:
|
|
27
|
+
static #coerceType<T>(conf: SchemaInputConfig, val: unknown): T | null | undefined {
|
|
28
28
|
if (conf.type?.bindSchema) {
|
|
29
29
|
val = conf.type.bindSchema(val);
|
|
30
30
|
} else {
|
|
@@ -144,19 +144,21 @@ export class BindUtil {
|
|
|
144
144
|
if (data === null || data === undefined) {
|
|
145
145
|
return data;
|
|
146
146
|
}
|
|
147
|
-
|
|
148
|
-
if (data instanceof cls) {
|
|
147
|
+
if (data instanceof cons) {
|
|
149
148
|
return castTo(data);
|
|
150
149
|
} else {
|
|
151
|
-
const
|
|
152
|
-
|
|
150
|
+
const cls = SchemaRegistryIndex.resolveInstanceType<T>(cons, asFull<T>(data));
|
|
151
|
+
const instance = classConstruct<T & { type?: string }>(cls);
|
|
153
152
|
|
|
154
|
-
for (const k of TypedObject.keys(
|
|
155
|
-
if (
|
|
156
|
-
delete
|
|
153
|
+
for (const k of TypedObject.keys(instance)) { // Do not retain undefined fields
|
|
154
|
+
if (instance[k] === undefined) {
|
|
155
|
+
delete instance[k];
|
|
157
156
|
}
|
|
158
157
|
}
|
|
159
|
-
|
|
158
|
+
|
|
159
|
+
const out = this.bindSchemaToObject(cls, instance, data, cfg);
|
|
160
|
+
SchemaRegistryIndex.get(cls).ensureInstanceTypeField(out);
|
|
161
|
+
return out;
|
|
160
162
|
}
|
|
161
163
|
}
|
|
162
164
|
|
|
@@ -172,7 +174,8 @@ export class BindUtil {
|
|
|
172
174
|
delete cfg.view;
|
|
173
175
|
|
|
174
176
|
if (!!data && isInstance<T>(data)) {
|
|
175
|
-
const
|
|
177
|
+
const adapter = SchemaRegistryIndex.get(cons);
|
|
178
|
+
const conf = adapter.get();
|
|
176
179
|
|
|
177
180
|
// If no configuration
|
|
178
181
|
if (!conf) {
|
|
@@ -180,19 +183,17 @@ export class BindUtil {
|
|
|
180
183
|
obj[k] = data[k];
|
|
181
184
|
}
|
|
182
185
|
} else {
|
|
183
|
-
let
|
|
186
|
+
let schema: SchemaFieldMap = conf.fields;
|
|
184
187
|
if (view) {
|
|
185
|
-
|
|
186
|
-
if (!
|
|
188
|
+
schema = adapter.getSchema(view);
|
|
189
|
+
if (!schema) {
|
|
187
190
|
throw new Error(`View not found: ${view.toString()}`);
|
|
188
191
|
}
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
for (const schemaFieldName of
|
|
192
|
-
const field = viewConf.schema[schemaFieldName];
|
|
193
|
-
|
|
194
|
+
for (const [schemaFieldName, field] of Object.entries(schema)) {
|
|
194
195
|
let inboundField: string | undefined = undefined;
|
|
195
|
-
if (field.access === 'readonly' || cfg.
|
|
196
|
+
if (field.access === 'readonly' || cfg.filterInput?.(field) === false) {
|
|
196
197
|
continue; // Skip trying to write readonly fields
|
|
197
198
|
}
|
|
198
199
|
if (schemaFieldName in data) {
|
|
@@ -227,7 +228,7 @@ export class BindUtil {
|
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
if (
|
|
231
|
+
if (SchemaRegistryIndex.has(field.type)) {
|
|
231
232
|
if (field.array && Array.isArray(v)) {
|
|
232
233
|
v = v.map(el => this.bindSchema(field.type, el, cfg));
|
|
233
234
|
} else {
|
|
@@ -244,7 +245,7 @@ export class BindUtil {
|
|
|
244
245
|
|
|
245
246
|
if (field.accessor) {
|
|
246
247
|
Object.defineProperty(obj, schemaFieldName, {
|
|
247
|
-
...
|
|
248
|
+
...adapter.getAccessorDescriptor(schemaFieldName),
|
|
248
249
|
enumerable: true
|
|
249
250
|
});
|
|
250
251
|
}
|
|
@@ -257,31 +258,32 @@ export class BindUtil {
|
|
|
257
258
|
|
|
258
259
|
/**
|
|
259
260
|
* Coerce field to type
|
|
260
|
-
* @param
|
|
261
|
+
* @param cfg
|
|
261
262
|
* @param val
|
|
262
263
|
* @param applyDefaults
|
|
263
264
|
* @returns
|
|
264
265
|
*/
|
|
265
|
-
static
|
|
266
|
+
static coerceInput(cfg: SchemaInputConfig, val: unknown, applyDefaults = false): unknown {
|
|
266
267
|
if ((val === undefined || val === null) && applyDefaults) {
|
|
267
|
-
val = Array.isArray(
|
|
268
|
+
val = Array.isArray(cfg.default) ? cfg.default.slice(0) : cfg.default;
|
|
268
269
|
}
|
|
269
|
-
if (
|
|
270
|
+
if (cfg.required?.active === false && (val === undefined || val === null)) {
|
|
270
271
|
return val;
|
|
271
272
|
}
|
|
272
|
-
const complex =
|
|
273
|
-
|
|
273
|
+
const complex = SchemaRegistryIndex.has(cfg.type);
|
|
274
|
+
const bindCfg: BindConfig | undefined = (complex && 'view' in cfg && typeof cfg.view === 'string') ? { view: cfg.view } : undefined;
|
|
275
|
+
if (cfg.array) {
|
|
274
276
|
const valArr = !Array.isArray(val) ? [val] : val;
|
|
275
277
|
if (complex) {
|
|
276
|
-
val = valArr.map(x => this.bindSchema(
|
|
278
|
+
val = valArr.map(x => this.bindSchema(cfg.type, x, bindCfg));
|
|
277
279
|
} else {
|
|
278
|
-
val = valArr.map(x => DataUtil.coerceType(x,
|
|
280
|
+
val = valArr.map(x => DataUtil.coerceType(x, cfg.type, false));
|
|
279
281
|
}
|
|
280
282
|
} else {
|
|
281
283
|
if (complex) {
|
|
282
|
-
val = this.bindSchema(
|
|
284
|
+
val = this.bindSchema(cfg.type, val, bindCfg);
|
|
283
285
|
} else {
|
|
284
|
-
val = DataUtil.coerceType(val,
|
|
286
|
+
val = DataUtil.coerceType(val, cfg.type, false);
|
|
285
287
|
}
|
|
286
288
|
}
|
|
287
289
|
return val;
|
|
@@ -293,11 +295,11 @@ export class BindUtil {
|
|
|
293
295
|
* @param params
|
|
294
296
|
* @returns
|
|
295
297
|
*/
|
|
296
|
-
static
|
|
298
|
+
static coerceParameters(fields: SchemaParameterConfig[], params: unknown[], applyDefaults = true): unknown[] {
|
|
297
299
|
params = [...params];
|
|
298
300
|
// Coerce types
|
|
299
301
|
for (const el of fields) {
|
|
300
|
-
params[el.index!] = this.
|
|
302
|
+
params[el.index!] = this.coerceInput(el, params[el.index!], applyDefaults);
|
|
301
303
|
}
|
|
302
304
|
return params;
|
|
303
305
|
}
|
|
@@ -309,7 +311,8 @@ export class BindUtil {
|
|
|
309
311
|
* @param params
|
|
310
312
|
* @returns
|
|
311
313
|
*/
|
|
312
|
-
static coerceMethodParams<T>(cls: Class<T>, method: string, params: unknown[], applyDefaults = true): unknown[] {
|
|
313
|
-
|
|
314
|
+
static coerceMethodParams<T>(cls: Class<T>, method: string | symbol, params: unknown[], applyDefaults = true): unknown[] {
|
|
315
|
+
const paramConfigs = SchemaRegistryIndex.getMethodConfig(cls, method).parameters;
|
|
316
|
+
return this.coerceParameters(paramConfigs, params, applyDefaults);
|
|
314
317
|
}
|
|
315
318
|
}
|
package/src/decorator/common.ts
CHANGED
|
@@ -1,27 +1,43 @@
|
|
|
1
|
-
import { Class, ClassInstance } from '@travetto/runtime';
|
|
1
|
+
import { Class, ClassInstance, getClass } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
function isClassInstance(o: Class | ClassInstance, property?: string): o is ClassInstance {
|
|
7
|
-
return !!property;
|
|
8
|
-
}
|
|
3
|
+
import { SchemaCoreConfig } from '../service/types.ts';
|
|
4
|
+
import { SchemaRegistryIndex } from '../service/registry-index.ts';
|
|
9
5
|
|
|
10
6
|
/**
|
|
11
7
|
* Describe a model or a field
|
|
12
8
|
* @param config The describe configuration
|
|
13
|
-
* @augments `@travetto/schema:
|
|
9
|
+
* @augments `@travetto/schema:Input`
|
|
10
|
+
* @kind decorator
|
|
14
11
|
*/
|
|
15
|
-
export function Describe(config: Partial<
|
|
16
|
-
return (
|
|
17
|
-
|
|
12
|
+
export function Describe(config: Partial<Omit<SchemaCoreConfig, 'metadata'>>) {
|
|
13
|
+
return (instanceOrCls: Class | ClassInstance, property?: string | symbol, descOrIdx?: PropertyDescriptor | number): void => {
|
|
14
|
+
const adapter = SchemaRegistryIndex.getForRegister(getClass(instanceOrCls));
|
|
15
|
+
if (!property) {
|
|
16
|
+
adapter.register(config);
|
|
17
|
+
} else {
|
|
18
18
|
if (descOrIdx !== undefined && typeof descOrIdx === 'number') {
|
|
19
|
-
|
|
19
|
+
adapter.registerParameter(property, descOrIdx, config);
|
|
20
|
+
} else if (typeof descOrIdx === 'object' && typeof descOrIdx.value === 'function') {
|
|
21
|
+
adapter.registerMethod(property, config);
|
|
20
22
|
} else {
|
|
21
|
-
|
|
23
|
+
adapter.registerField(property, config);
|
|
22
24
|
}
|
|
23
|
-
} else {
|
|
24
|
-
SchemaRegistry.register(target, config);
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Mark a field/method as private
|
|
31
|
+
* @augments `@travetto/schema:Input`
|
|
32
|
+
* @kind decorator
|
|
33
|
+
*/
|
|
34
|
+
export const IsPrivate = (): (instanceOrCls: Class | ClassInstance, property?: string | symbol) => void => Describe({ private: true });
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Mark a field/method as ignored
|
|
38
|
+
* @augments `@travetto/schema:Ignore`
|
|
39
|
+
* @kind decorator
|
|
40
|
+
*/
|
|
41
|
+
export function Ignore(): PropertyDecorator {
|
|
42
|
+
return () => { };
|
|
43
|
+
}
|
package/src/decorator/field.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import { Any, ClassInstance } from '@travetto/runtime';
|
|
1
|
+
import { Any, ClassInstance, getClass } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { ClassList, FieldConfig } from '../service/types.ts';
|
|
3
|
+
import { SchemaFieldConfig } from '../service/types.ts';
|
|
4
|
+
import { SchemaRegistryIndex } from '../service/registry-index.ts';
|
|
6
5
|
|
|
7
6
|
type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(t: T, k: K, idx?: TypedPropertyDescriptor<Any> | number) => void);
|
|
8
7
|
|
|
9
|
-
function
|
|
10
|
-
return (
|
|
11
|
-
|
|
12
|
-
SchemaRegistry.registerPendingParamFacet(t.constructor, k, idx, obj);
|
|
13
|
-
} else {
|
|
14
|
-
SchemaRegistry.registerPendingFieldFacet(t.constructor, k, obj);
|
|
15
|
-
}
|
|
8
|
+
function field<V>(...obj: Partial<SchemaFieldConfig>[]): PropType<V> {
|
|
9
|
+
return (instance: ClassInstance, property: string | symbol): void => {
|
|
10
|
+
SchemaRegistryIndex.getForRegister(getClass(instance)).registerField(property, ...obj);
|
|
16
11
|
};
|
|
17
12
|
}
|
|
18
13
|
|
|
@@ -20,189 +15,31 @@ function prop<V>(obj: Partial<FieldConfig>): PropType<V> {
|
|
|
20
15
|
* Registering a field
|
|
21
16
|
* @param type The type for the field
|
|
22
17
|
* @param config The field configuration
|
|
23
|
-
* @augments `@travetto/schema:
|
|
18
|
+
* @augments `@travetto/schema:Input`
|
|
19
|
+
* @kind decorator
|
|
24
20
|
*/
|
|
25
|
-
export function Field(type:
|
|
26
|
-
return (
|
|
27
|
-
if (idx !== undefined && typeof idx === 'number') {
|
|
28
|
-
SchemaRegistry.registerPendingParamConfig(f.constructor, k, idx, type, Object.assign({}, ...config));
|
|
29
|
-
} else {
|
|
30
|
-
SchemaRegistry.registerPendingFieldConfig(f.constructor, k, type, Object.assign({}, ...config));
|
|
31
|
-
}
|
|
32
|
-
};
|
|
21
|
+
export function Field(type: Pick<SchemaFieldConfig, 'type' | 'array'>, ...config: Partial<SchemaFieldConfig>[]): PropType<unknown> {
|
|
22
|
+
return field(type, ...config);
|
|
33
23
|
}
|
|
34
24
|
|
|
35
|
-
/**
|
|
36
|
-
* Alias for the field
|
|
37
|
-
* @param aliases List of all aliases for a field
|
|
38
|
-
* @augments `@travetto/schema:Field`
|
|
39
|
-
*/
|
|
40
|
-
export function Alias(...aliases: string[]): PropType<unknown> { return prop({ aliases }); }
|
|
41
25
|
/**
|
|
42
26
|
* Mark a field as writeonly
|
|
43
|
-
* @augments `@travetto/schema:
|
|
27
|
+
* @augments `@travetto/schema:Input`
|
|
28
|
+
* @kind decorator
|
|
44
29
|
*/
|
|
45
|
-
export function Writeonly(): PropType<unknown> { return
|
|
30
|
+
export function Writeonly(): PropType<unknown> { return field({ access: 'writeonly' }); }
|
|
31
|
+
|
|
46
32
|
/**
|
|
47
33
|
* Mark a field as readonly
|
|
48
|
-
* @augments `@travetto/schema:
|
|
34
|
+
* @augments `@travetto/schema:Input`
|
|
35
|
+
* @kind decorator
|
|
49
36
|
*/
|
|
50
|
-
export function Readonly(): PropType<unknown> { return
|
|
37
|
+
export function Readonly(): PropType<unknown> { return field({ access: 'readonly' }); }
|
|
38
|
+
|
|
51
39
|
/**
|
|
52
40
|
* Mark a field as sensitive
|
|
53
41
|
* @param active This determines if this field is sensitive or not.
|
|
54
|
-
* @augments `@travetto/schema:
|
|
55
|
-
|
|
56
|
-
export function Secret(active = true): PropType<unknown> { return prop({ secret: active }); }
|
|
57
|
-
/**
|
|
58
|
-
* Mark a field as required
|
|
59
|
-
* @param active This determines if this field is required or not.
|
|
60
|
-
* @param message The error message when a the constraint fails.
|
|
61
|
-
* @augments `@travetto/schema:Field`
|
|
62
|
-
*/
|
|
63
|
-
export function Required(active = true, message?: string): PropType<unknown> { return prop({ required: { active, message } }); }
|
|
64
|
-
/**
|
|
65
|
-
* Define a field as a set of enumerated values
|
|
66
|
-
* @param values The list of values allowed for the enumeration
|
|
67
|
-
* @param message The error message to show when the constraint fails
|
|
68
|
-
* @augments `@travetto/schema:Field`
|
|
69
|
-
*/
|
|
70
|
-
export function Enum(values: string[], message?: string): PropType<string | number> {
|
|
71
|
-
message = message || `{path} is only allowed to be "${values.join('" or "')}"`;
|
|
72
|
-
return prop({ enum: { values, message } });
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Mark the field as indicating it's storing textual data
|
|
76
|
-
* @augments `@travetto/schema:Field`
|
|
77
|
-
*/
|
|
78
|
-
export function Text(): PropType<string | string[]> { return prop({ specifiers: ['text'] }); }
|
|
79
|
-
/**
|
|
80
|
-
* Mark the field to indicate it's for long form text
|
|
81
|
-
* @augments `@travetto/schema:Field`
|
|
82
|
-
*/
|
|
83
|
-
export function LongText(): PropType<string | string[]> { return prop({ specifiers: ['text', 'long'] }); }
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Require the field to match a specific RegExp
|
|
87
|
-
* @param re The regular expression to match against
|
|
88
|
-
* @param message The message to show when the constraint fails
|
|
89
|
-
* @augments `@travetto/schema:Field`
|
|
42
|
+
* @augments `@travetto/schema:Input`
|
|
43
|
+
* @kind decorator
|
|
90
44
|
*/
|
|
91
|
-
export function
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* The minimum length for the string or array
|
|
95
|
-
* @param n The minimum length
|
|
96
|
-
* @param message The message to show when the constraint fails
|
|
97
|
-
* @augments `@travetto/schema:Field`
|
|
98
|
-
*/
|
|
99
|
-
export function MinLength(n: number, message?: string): PropType<string | unknown[]> {
|
|
100
|
-
return prop({ minlength: { n, message }, ...(n === 0 ? { required: { active: false } } : {}) });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* The maximum length for the string or array
|
|
105
|
-
* @param n The maximum length
|
|
106
|
-
* @param message The message to show when the constraint fails
|
|
107
|
-
* @augments `@travetto/schema:Field`
|
|
108
|
-
*/
|
|
109
|
-
export function MaxLength(n: number, message?: string): PropType<string | unknown[]> { return prop({ maxlength: { n, message } }); }
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* The minimum value
|
|
113
|
-
* @param n The minimum value
|
|
114
|
-
* @param message The message to show when the constraint fails
|
|
115
|
-
* @augments `@travetto/schema:Field`
|
|
116
|
-
*/
|
|
117
|
-
export function Min<T extends number | Date>(n: T, message?: string): PropType<Date | number> {
|
|
118
|
-
return prop({ min: { n, message } });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* The maximum value
|
|
123
|
-
* @param n The maximum value
|
|
124
|
-
* @param message The message to show when the constraint fails
|
|
125
|
-
* @augments `@travetto/schema:Field`
|
|
126
|
-
*/
|
|
127
|
-
export function Max<T extends number | Date>(n: T, message?: string): PropType<Date | number> {
|
|
128
|
-
return prop({ max: { n, message } });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Mark a field as an email
|
|
133
|
-
* @param message The message to show when the constraint fails
|
|
134
|
-
* @augments `@travetto/schema:Field`
|
|
135
|
-
*/
|
|
136
|
-
export function Email(message?: string): PropType<string | string[]> { return Match(CommonRegExp.email, message); }
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Mark a field as an telephone number
|
|
140
|
-
* @param message The message to show when the constraint fails
|
|
141
|
-
* @augments `@travetto/schema:Field`
|
|
142
|
-
*/
|
|
143
|
-
export function Telephone(message?: string): PropType<string | string[]> { return Match(CommonRegExp.telephone, message); }
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Mark a field as a url
|
|
147
|
-
* @param message The message to show when the constraint fails
|
|
148
|
-
* @augments `@travetto/schema:Field`
|
|
149
|
-
*/
|
|
150
|
-
export function Url(message?: string): PropType<string | string[]> { return Match(CommonRegExp.url, message); }
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Determine the numeric precision of the value
|
|
154
|
-
* @param digits The number of digits a number should have
|
|
155
|
-
* @param decimals The number of decimal digits to support
|
|
156
|
-
* @augments `@travetto/schema:Field`
|
|
157
|
-
*/
|
|
158
|
-
export function Precision(digits: number, decimals?: number): PropType<number> { return prop({ precision: [digits, decimals] }); }
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Mark a number as an integer
|
|
162
|
-
* @augments `@travetto/schema:Field`
|
|
163
|
-
*/
|
|
164
|
-
export function Integer(): PropType<number> { return Precision(0); }
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Mark a number as a float
|
|
168
|
-
* @augments `@travetto/schema:Field`
|
|
169
|
-
*/
|
|
170
|
-
export function Float(): PropType<number> { return Precision(10, 7); }
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Mark a number as a long value
|
|
174
|
-
* @augments `@travetto/schema:Field`
|
|
175
|
-
*/
|
|
176
|
-
export function Long(): PropType<number> { return Precision(19, 0); }
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Mark a number as a currency
|
|
180
|
-
* @augments `@travetto/schema:Field`
|
|
181
|
-
*/
|
|
182
|
-
export function Currency(): PropType<number> { return Precision(13, 2); }
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Mark a field as ignored
|
|
186
|
-
*
|
|
187
|
-
* @augments `@travetto/schema:Ignore`
|
|
188
|
-
*/
|
|
189
|
-
export function Ignore(): PropertyDecorator {
|
|
190
|
-
return () => { };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Specifier for the field
|
|
195
|
-
* @param specifiers The specifiers for a field
|
|
196
|
-
* @augments `@travetto/schema:Field`
|
|
197
|
-
*/
|
|
198
|
-
export function Specifier(...specifiers: string[]): PropType<unknown> { return prop({ specifiers }); }
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Sets the subtype field via a property decorator
|
|
202
|
-
* @augments `@travetto/schema:Field`
|
|
203
|
-
*/
|
|
204
|
-
export function SubTypeField(): ((t: ClassInstance, k: string) => void) {
|
|
205
|
-
return (t: ClassInstance, k: string): void => {
|
|
206
|
-
SchemaRegistry.register(t.constructor, { subTypeField: k });
|
|
207
|
-
};
|
|
208
|
-
}
|
|
45
|
+
export function Secret(active = true): PropType<unknown> { return field({ secret: active }); }
|