@travetto/schema 3.0.0-rc.4 → 3.0.0-rc.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  <!-- This file was generated by @travetto/doc and should not be modified directly -->
2
- <!-- Please modify https://github.com/travetto/travetto/tree/main/module/schema/doc.ts and execute "npx trv doc" to rebuild -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/schema/DOC.ts and execute "npx trv doc" to rebuild -->
3
3
  # Schema
4
- ## Data type registry for runtime validation, reflection and binding.
4
+ ## Data type registry for runtime validation, reflection and binding.
5
5
 
6
6
  **Install: @travetto/schema**
7
7
  ```bash
@@ -9,13 +9,13 @@ npm install @travetto/schema
9
9
  ```
10
10
 
11
11
  This module's purpose is to allow for proper declaration and validation of data types, in the course of running a program. The framework defined here, is
12
- leveraged in the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Environment-aware config management using yaml files"), [Application](https://github.com/travetto/travetto/tree/main/module/app#readme "Application registration/management and run support."), [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module."), [OpenAPI Specification](https://github.com/travetto/travetto/tree/main/module/openapi#readme "OpenAPI integration support for the travetto framework") and [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") modules. The schema is the backbone of all data transfer, as it helps to
12
+ leveraged in the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support"), [Application](https://github.com/travetto/travetto/tree/main/module/app#readme "Application registration/management and run support."), [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module."), [OpenAPI Specification](https://github.com/travetto/travetto/tree/main/module/openapi#readme "OpenAPI integration support for the travetto framework") and [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") modules. The schema is the backbone of all data transfer, as it helps to
13
13
  provide validation on correctness of input, whether it is a rest request, command line inputs, or a configuration file.
14
14
 
15
15
  This module provides a mechanism for registering classes and field level information as well the ability to apply that information at runtime.
16
16
 
17
17
  ## Registration
18
- 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#L12) decoration.
18
+ 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#L14) decoration.
19
19
 
20
20
  ### Classes
21
21
  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:
@@ -65,22 +65,22 @@ This schema provides a powerful base for data binding and validation at runtime.
65
65
 
66
66
 
67
67
  * [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L38) defines a field that will be serialized.
68
- * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L72) defines a that field should be required
69
- * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L79) defines the allowable values that a field can have
70
- * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L100) defines a regular expression that the field value should match
71
- * [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L108) enforces min length of a string
72
- * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L118) enforces max length of a string
73
- * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L108) enforces min value for a date or a number
74
- * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L118) enforces max value for a date or a number
75
- * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L145) ensures string field matches basic email regex
76
- * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L152) ensures string field matches basic telephone regex
77
- * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L159) ensures string field matches basic url regex
78
- * [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L198) exclude from auto schema registration
79
- * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L173) ensures number passed in is only a whole number
80
- * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L179) ensures number passed in allows fractional values
81
- * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L191) provides support for standard currency
82
- * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L87) 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/field.ts#L92) same as text, but expects longer form content
68
+ * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L78) defines a that field should be required
69
+ * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L85) defines the allowable values that a field can have
70
+ * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) defines a regular expression that the field value should match
71
+ * [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L114) enforces min length of a string
72
+ * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L124) enforces max length of a string
73
+ * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L114) enforces min value for a date or a number
74
+ * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L124) enforces max value for a date or a number
75
+ * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L151) ensures string field matches basic email regex
76
+ * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L158) ensures string field matches basic telephone regex
77
+ * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L165) ensures string field matches basic url regex
78
+ * [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L204) exclude from auto schema registration
79
+ * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L179) ensures number passed in is only a whole number
80
+ * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L185) ensures number passed in allows fractional values
81
+ * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L197) provides support for standard currency
82
+ * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L93) 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/field.ts#L98) same as text, but expects longer form content
84
84
  * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L65) defines a that field should not be bindable external to the class
85
85
  * [@Writeonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L59) defines a that field should not be exported in serialization, but that it can be bound to
86
86
 
@@ -98,7 +98,7 @@ And similarly, the `description` will be picked up from the [JSDoc](http://usejs
98
98
  At runtime, once a schema is registered, a programmer can utilize this structure to perform specific operations. Specifically binding and validation.
99
99
 
100
100
  ### Binding
101
- 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#L12) and a JS object that will be the source of the binding. Given the schema:
101
+ 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#L14) and a JS object that will be the source of the binding. Given the schema:
102
102
 
103
103
  **Code: Sub Schemas via Address**
104
104
  ```typescript
@@ -140,13 +140,14 @@ and the output would be a `Person` instance with the following structure
140
140
 
141
141
  **Terminal: Sample data output after binding**
142
142
  ```bash
143
- $ node @travetto/base/bin/main ./doc/person-output.ts
143
+ $ trv main support/main.person-output.ts
144
144
 
145
145
  Person {
146
146
  name: 'Test',
147
147
  age: 19,
148
148
  address: Address { street1: '1234 Fun', street2: 'Unit 20' }
149
149
  }
150
+ 
150
151
  ```
151
152
 
152
153
  **Note**: Binding will attempt to convert/coerce types as much as possible to honor the pattern of Javascript and it's dynamic nature.
@@ -200,27 +201,9 @@ would produce an exception similar to following structure
200
201
 
201
202
  **Terminal: Sample error output**
202
203
  ```bash
203
- $ node @travetto/base/bin/main ./doc/person-invalid-output.ts
204
-
205
- Validation Failed {
206
- "message": "Validation errors have occurred",
207
- "category": "data",
208
- "type": "ValidationResultError",
209
- "at": "2022-03-14T04:00:00.618Z",
210
- "errors": [
211
- {
212
- "kind": "type",
213
- "message": "age is not a valid number",
214
- "path": "age",
215
- "type": "number"
216
- },
217
- {
218
- "kind": "required",
219
- "message": "address.street2 is required",
220
- "path": "address.street2"
221
- }
222
- ]
223
- }
204
+ $ trv main support/main.person-invalid-output.ts
205
+
206
+ 
224
207
  ```
225
208
 
226
209
  ### Custom Validators
@@ -294,7 +277,7 @@ This feature is meant to allow for simple Typescript types to be able to be back
294
277
 
295
278
  **Code: Simple Custom Type**
296
279
  ```typescript
297
- import { Util } from '@travetto/base';
280
+ import { DataUtil } from '@travetto/base';
298
281
 
299
282
  /**
300
283
  * @concrete .:PointImpl
@@ -311,7 +294,7 @@ export class PointImpl {
311
294
 
312
295
  static bindSchema(input: unknown): [number, number] | typeof INVALID | undefined {
313
296
  if (Array.isArray(input) && input.length === 2) {
314
- return input.map(x => Util.coerceType(x, Number, false)) as [number, number];
297
+ return input.map(x => DataUtil.coerceType(x, Number, false)) as [number, number];
315
298
  } else {
316
299
  return INVALID;
317
300
  }
@@ -341,21 +324,7 @@ All that happens now, is the type is exported, and the class above is able to pr
341
324
 
342
325
  **Terminal: Custom Type Validation**
343
326
  ```bash
344
- $ node @travetto/base/bin/main ./doc/custom-type-output.ts
345
-
346
- Validation Failed {
347
- "message": "Validation errors have occurred",
348
- "category": "data",
349
- "type": "ValidationResultError",
350
- "at": "2022-03-14T04:00:00.837Z",
351
- "errors": [
352
- {
353
- "kind": "type",
354
- "message": "point is not a valid PointImpl",
355
- "path": "point",
356
- "type": "PointImpl"
357
- }
358
- ]
359
- }
327
+ $ trv main support/main.custom-type-output.ts
328
+
329
+ 
360
330
  ```
361
- ;
File without changes
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "displayName": "Schema",
4
- "version": "3.0.0-rc.4",
5
- "description": "Data type registry for runtime validation, reflection and binding. ",
3
+ "version": "3.0.0-rc.7",
4
+ "description": "Data type registry for runtime validation, reflection and binding.",
6
5
  "keywords": [
7
6
  "schema",
8
7
  "ast-transformations",
@@ -18,18 +17,28 @@
18
17
  "name": "Travetto Framework"
19
18
  },
20
19
  "files": [
21
- "index.ts",
20
+ "__index__.ts",
22
21
  "src",
23
22
  "support"
24
23
  ],
25
- "main": "index.ts",
24
+ "main": "__index__.ts",
26
25
  "repository": {
27
26
  "url": "https://github.com/travetto/travetto.git",
28
27
  "directory": "module/schema"
29
28
  },
30
29
  "dependencies": {
31
- "@travetto/registry": "^3.0.0-rc.4",
32
- "@travetto/transformer": "^3.0.0-rc.4"
30
+ "@travetto/registry": "^3.0.0-rc.7"
31
+ },
32
+ "peerDependencies": {
33
+ "@travetto/transformer": "^3.0.0-rc.7"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "@travetto/transformer": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "travetto": {
41
+ "displayName": "Schema"
33
42
  },
34
43
  "publishConfig": {
35
44
  "access": "public"
package/src/bind-util.ts CHANGED
@@ -1,9 +1,15 @@
1
- import { Class, ClassInstance, ConcreteClass, Util } from '@travetto/base';
1
+ import { Class, ConcreteClass, TypedObject, ObjectUtil, DataUtil } from '@travetto/base';
2
2
 
3
3
  import { AllViewⲐ } from './internal/types';
4
4
  import { SchemaRegistry } from './service/registry';
5
5
  import { FieldConfig } from './service/types';
6
6
 
7
+ type BindConfig = {
8
+ view?: string | typeof AllViewⲐ;
9
+ filterField?: (field: FieldConfig) => boolean;
10
+ filterValue?: (value: unknown, field: FieldConfig) => boolean;
11
+ };
12
+
7
13
  /**
8
14
  * Utilities for binding objects to schemas
9
15
  */
@@ -18,7 +24,7 @@ export class BindUtil {
18
24
  if (conf.type?.bindSchema) {
19
25
  val = conf.type.bindSchema(val);
20
26
  } else {
21
- val = Util.coerceType(val, conf.type, false);
27
+ val = DataUtil.coerceType(val, conf.type, false);
22
28
 
23
29
  if (conf.type === Number && conf.precision && typeof val === 'number') {
24
30
  if (conf.precision[1]) { // Supports decimal
@@ -32,16 +38,6 @@ export class BindUtil {
32
38
  return val as T;
33
39
  }
34
40
 
35
- /**
36
- * Register `from` on the Function prototype
37
- */
38
- static register(): void {
39
- const proto = Object.getPrototypeOf(Function);
40
- proto.from = function (data: object | ClassInstance, view?: string): unknown {
41
- return BindUtil.bindSchema(this, data, view);
42
- };
43
- }
44
-
45
41
  /**
46
42
  * Convert dotted paths into a full object
47
43
  *
@@ -53,7 +49,7 @@ export class BindUtil {
53
49
  const out: Record<string, unknown> = {};
54
50
  for (const k of Object.keys(obj)) {
55
51
  const objK = obj[k];
56
- const val = Util.isPlainObject(objK) ? this.expandPaths(objK) : objK;
52
+ const val = ObjectUtil.isPlainObject(objK) ? this.expandPaths(objK) : objK;
57
53
  const parts = k.split('.');
58
54
  const last = parts.pop()!;
59
55
  let sub = out;
@@ -80,8 +76,8 @@ export class BindUtil {
80
76
  }
81
77
 
82
78
  if (last.indexOf('[') < 0) {
83
- if (sub[last] && Util.isPlainObject(val)) {
84
- sub[last] = Util.deepAssign(sub[last], val, 'coerce');
79
+ if (sub[last] && ObjectUtil.isPlainObject(val)) {
80
+ sub[last] = DataUtil.deepAssign(sub[last], val, 'coerce');
85
81
  } else {
86
82
  sub[last] = val;
87
83
  }
@@ -98,8 +94,8 @@ export class BindUtil {
98
94
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
99
95
  key = sub.length as number;
100
96
  }
101
- if (sub[key] && Util.isPlainObject(val) && Util.isPlainObject(sub[key])) {
102
- sub[key] = Util.deepAssign(sub[key], val, 'coerce');
97
+ if (sub[key] && ObjectUtil.isPlainObject(val) && ObjectUtil.isPlainObject(sub[key])) {
98
+ sub[key] = DataUtil.deepAssign(sub[key], val, 'coerce');
103
99
  } else {
104
100
  sub[key] = val;
105
101
  }
@@ -118,13 +114,13 @@ export class BindUtil {
118
114
  const out: Record<string, unknown> = {};
119
115
  for (const [key, value] of Object.entries(data)) {
120
116
  const pre = `${prefix}${key}`;
121
- if (Util.isPlainObject(value)) {
117
+ if (ObjectUtil.isPlainObject(value)) {
122
118
  Object.assign(out, this.flattenPaths(value, `${pre}.`)
123
119
  );
124
120
  } else if (Array.isArray(value)) {
125
121
  for (let i = 0; i < value.length; i++) {
126
122
  const v = value[i];
127
- if (Util.isPlainObject(v)) {
123
+ if (ObjectUtil.isPlainObject(v)) {
128
124
  Object.assign(out, this.flattenPaths(v, `${pre}[${i}].`));
129
125
  } else {
130
126
  out[`${pre}[${i}]`] = v;
@@ -141,12 +137,12 @@ export class BindUtil {
141
137
  * Bind data to the schema for a class, with an optional view
142
138
  * @param cons The schema class to bind against
143
139
  * @param data The provided data to bind
144
- * @param view The optional view to limit the binding against
140
+ * @param cfg The bind configuration
145
141
  */
146
- static bindSchema<T>(cons: Class<T>, data?: undefined, view?: string): undefined;
147
- static bindSchema<T>(cons: Class<T>, data?: null, view?: string): null;
148
- static bindSchema<T>(cons: Class<T>, data?: object | T, view?: string): T;
149
- static bindSchema<T>(cons: Class<T>, data?: object | T, view?: string): T | null | undefined {
142
+ static bindSchema<T>(cons: Class<T>, data?: undefined, cfg?: BindConfig): undefined;
143
+ static bindSchema<T>(cons: Class<T>, data?: null, cfg?: BindConfig): null;
144
+ static bindSchema<T>(cons: Class<T>, data?: object | T, cfg?: BindConfig): T;
145
+ static bindSchema<T>(cons: Class<T>, data?: object | T, cfg?: BindConfig): T | null | undefined {
150
146
  if (data === null || data === undefined) {
151
147
  return data;
152
148
  }
@@ -161,13 +157,13 @@ export class BindUtil {
161
157
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
162
158
  SchemaRegistry.ensureInstanceTypeField(cls, tgt as T & { type?: string });
163
159
 
164
- for (const k of Object.keys(tgt)) { // Do not retain undefined fields
160
+ for (const k of TypedObject.keys(tgt)) { // Do not retain undefined fields
165
161
  if (tgt[k] === undefined) {
166
162
  delete tgt[k];
167
163
  }
168
164
  }
169
165
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
170
- return this.bindSchemaToObject(cls, tgt, data as object, view);
166
+ return this.bindSchemaToObject(cls, tgt, data as object, cfg);
171
167
  }
172
168
  }
173
169
 
@@ -176,17 +172,18 @@ export class BindUtil {
176
172
  * @param cons The schema class
177
173
  * @param obj The target object (instance of cons)
178
174
  * @param data The data to bind
179
- * @param view The desired view
175
+ * @param cfg The bind configuration
180
176
  */
181
- static bindSchemaToObject<T>(cons: Class<T>, obj: T, data?: object, view?: string | typeof AllViewⲐ): T {
182
- view ??= AllViewⲐ;
177
+ static bindSchemaToObject<T>(cons: Class<T>, obj: T, data?: object, cfg: BindConfig = {}): T {
178
+ const view = cfg.view ?? AllViewⲐ; // Does not convey
179
+ delete cfg.view;
183
180
 
184
- if (!!data && !Util.isPrimitive(data)) {
181
+ if (!!data && !ObjectUtil.isPrimitive(data)) {
185
182
  const conf = SchemaRegistry.get(cons);
186
183
 
187
184
  // If no configuration
188
185
  if (!conf) {
189
- for (const k of Object.keys(data)) {
186
+ for (const k of TypedObject.keys(data)) {
190
187
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
191
188
  obj[k] = data[k as keyof typeof data];
192
189
  }
@@ -198,14 +195,16 @@ export class BindUtil {
198
195
  }
199
196
 
200
197
  for (const schemaFieldName of viewConf.fields) {
198
+ const field = viewConf.schema[schemaFieldName];
199
+
201
200
  let inboundField: string | undefined = undefined;
202
- if (viewConf.schema[schemaFieldName].access === 'readonly') {
201
+ if (field.access === 'readonly' || cfg.filterField?.(field) === false) {
203
202
  continue; // Skip trying to write readonly fields
204
203
  }
205
204
  if (schemaFieldName in data) {
206
205
  inboundField = schemaFieldName;
207
- } else if (viewConf.schema[schemaFieldName].aliases) {
208
- for (const aliasedField of (viewConf.schema[schemaFieldName].aliases ?? [])) {
206
+ } else if (field.aliases) {
207
+ for (const aliasedField of (field.aliases ?? [])) {
209
208
  if (aliasedField in data) {
210
209
  inboundField = aliasedField;
211
210
  break;
@@ -220,29 +219,43 @@ export class BindUtil {
220
219
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
221
220
  let v: unknown = data[inboundField as keyof typeof data];
222
221
 
223
- if (v !== undefined && v !== null) {
224
- const config = viewConf.schema[schemaFieldName];
222
+ // Filtering values
223
+ if (cfg.filterValue && !cfg.filterValue(v, field)) {
224
+ continue;
225
+ }
225
226
 
227
+ if (v !== undefined && v !== null) {
226
228
  // Ensure its an array
227
- if (!Array.isArray(v) && config.array) {
228
- v = [v];
229
+ if (!Array.isArray(v) && field.array) {
230
+ if (typeof v === 'string' && v.includes(',')) {
231
+ v = v.split(/\s*,\s*/);
232
+ } else {
233
+ v = [v];
234
+ }
229
235
  }
230
236
 
231
- if (SchemaRegistry.has(config.type)) {
232
- if (config.array && Array.isArray(v)) {
233
- v = v.map(el => this.bindSchema(config.type, el));
237
+ if (SchemaRegistry.has(field.type)) {
238
+ if (field.array && Array.isArray(v)) {
239
+ v = v.map(el => this.bindSchema(field.type, el, cfg));
234
240
  } else {
235
- v = this.bindSchema(config.type, v);
241
+ v = this.bindSchema(field.type, v, cfg);
236
242
  }
237
- } else if (config.array && Array.isArray(v)) {
238
- v = v.map(el => this.#coerceType(config, el));
243
+ } else if (field.array && Array.isArray(v)) {
244
+ v = v.map(el => this.#coerceType(field, el));
239
245
  } else {
240
- v = this.#coerceType(config, v);
246
+ v = this.#coerceType(field, v);
241
247
  }
242
248
  }
243
249
 
244
250
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
245
251
  obj[schemaFieldName as keyof typeof obj] = v as (typeof obj)[keyof typeof obj];
252
+
253
+ if (field.accessor) {
254
+ Object.defineProperty(obj, schemaFieldName, {
255
+ ...SchemaRegistry.getAccessorDescriptor(cons, schemaFieldName),
256
+ enumerable: true
257
+ });
258
+ }
246
259
  }
247
260
  }
248
261
  }
@@ -265,15 +278,15 @@ export class BindUtil {
265
278
  if (field.array) {
266
279
  const valArr = !Array.isArray(val) ? [val] : val;
267
280
  if (complex) {
268
- val = valArr.map(x => this.bindSchema(field.type, x, field.view));
281
+ val = valArr.map(x => this.bindSchema(field.type, x, { view: field.view }));
269
282
  } else {
270
- val = valArr.map(x => Util.coerceType(x, field.type, false));
283
+ val = valArr.map(x => DataUtil.coerceType(x, field.type, false));
271
284
  }
272
285
  } else {
273
286
  if (complex) {
274
- val = this.bindSchema(field.type, val, field.view);
287
+ val = this.bindSchema(field.type, val, { view: field.view });
275
288
  } else {
276
- val = Util.coerceType(val, field.type, false);
289
+ val = DataUtil.coerceType(val, field.type, false);
277
290
  }
278
291
  }
279
292
  return val;
@@ -10,7 +10,7 @@ function isClassInstance(o: Class | ClassInstance, property?: string): o is Clas
10
10
  /**
11
11
  * Describe a model or a field
12
12
  * @param config The describe configuration
13
- * @augments `@trv:schema/Describe`
13
+ * @augments `@travetto/schema:Describe`
14
14
  */
15
15
  export function Describe(config: Partial<DescribableConfig>) {
16
16
  return (target: Class | ClassInstance, property?: string, descOrIdx?: PropertyDescriptor | number): void => {
@@ -33,7 +33,7 @@ const dateNumberProp: (obj: Partial<FieldConfig>) => <T extends Partial<Record<K
33
33
  * Registering a field
34
34
  * @param type The type for the field
35
35
  * @param config The field configuration
36
- * @augments `@trv:schema/Field`
36
+ * @augments `@travetto/schema:Field`
37
37
  */
38
38
  export function Field(type: ClassList, config?: Partial<FieldConfig>) {
39
39
  return (f: ClassInstance, k: string, idx?: number | PropertyDescriptor): void => {
@@ -48,33 +48,39 @@ export function Field(type: ClassList, config?: Partial<FieldConfig>) {
48
48
  /**
49
49
  * Alias for the field
50
50
  * @param aliases List of all aliases for a field
51
- * @augments `@trv:schema/Field`
51
+ * @augments `@travetto/schema:Field`
52
52
  */
53
53
  export function Alias(...aliases: string[]): ReturnType<typeof prop> { return prop({ aliases }); }
54
54
  /**
55
55
  * Mark a field as writeonly
56
56
  * @param active This determines if this field is readonly or not.
57
- * @augments `@trv:schema/Field`
57
+ * @augments `@travetto/schema:Field`
58
58
  */
59
59
  export function Writeonly(active = true): ReturnType<typeof prop> { return prop({ access: 'writeonly' }); }
60
60
  /**
61
61
  * Mark a field as readonly
62
62
  * @param active This determines if this field is readonly or not.
63
- * @augments `@trv:schema/Field`
63
+ * @augments `@travetto/schema:Field`
64
64
  */
65
65
  export function Readonly(active = true): ReturnType<typeof prop> { return prop({ access: 'readonly' }); }
66
+ /**
67
+ * Mark a field as sensitive
68
+ * @param active This determines if this field is sensitive or not.
69
+ * @augments `@travetto/schema:Field`
70
+ */
71
+ export function Secret(active = true): ReturnType<typeof prop> { return prop({ secret: active }); }
66
72
  /**
67
73
  * Mark a field as required
68
74
  * @param active This determines if this field is required or not.
69
75
  * @param message The error message when a the constraint fails.
70
- * @augments `@trv:schema/Field`
76
+ * @augments `@travetto/schema:Field`
71
77
  */
72
78
  export function Required(active = true, message?: string): ReturnType<typeof prop> { return prop({ required: { active, message } }); }
73
79
  /**
74
80
  * Define a field as a set of enumerated values
75
81
  * @param values The list of values allowed for the enumeration
76
82
  * @param message The error message to show when the constraint fails
77
- * @augments `@trv:schema/Field`
83
+ * @augments `@travetto/schema:Field`
78
84
  */
79
85
  export function Enum(values: string[], message?: string): ReturnType<typeof stringNumberProp> {
80
86
  message = message || `{path} is only allowed to be "${values.join('" or "')}"`;
@@ -82,12 +88,12 @@ export function Enum(values: string[], message?: string): ReturnType<typeof stri
82
88
  }
83
89
  /**
84
90
  * Mark the field as indicating it's storing textual data
85
- * @augments `@trv:schema/Field`
91
+ * @augments `@travetto/schema:Field`
86
92
  */
87
93
  export function Text(): ReturnType<typeof stringArrStringProp> { return stringArrStringProp({ specifier: 'text' }); }
88
94
  /**
89
95
  * Mark the field to indicate it's for long form text
90
- * @augments `@trv:schema/Field`
96
+ * @augments `@travetto/schema:Field`
91
97
  */
92
98
  export function LongText(): ReturnType<typeof stringArrStringProp> { return stringArrStringProp({ specifier: 'text-long' }); }
93
99
 
@@ -95,7 +101,7 @@ export function LongText(): ReturnType<typeof stringArrStringProp> { return stri
95
101
  * Require the field to match a specific RegExp
96
102
  * @param re The regular expression to match against
97
103
  * @param message The message to show when the constraint fails
98
- * @augments `@trv:schema/Field`
104
+ * @augments `@travetto/schema:Field`
99
105
  */
100
106
  export function Match(re: RegExp, message?: string): ReturnType<typeof stringArrStringProp> { return stringArrStringProp({ match: { re, message } }); }
101
107
 
@@ -103,7 +109,7 @@ export function Match(re: RegExp, message?: string): ReturnType<typeof stringArr
103
109
  * The minimum length for the string or array
104
110
  * @param n The minimum length
105
111
  * @param message The message to show when the constraint fails
106
- * @augments `@trv:schema/Field`
112
+ * @augments `@travetto/schema:Field`
107
113
  */
108
114
  export function MinLength(n: number, message?: string): ReturnType<typeof stringArrProp> {
109
115
  return stringArrProp({ minlength: { n, message }, ...(n === 0 ? { required: { active: false } } : {}) });
@@ -113,7 +119,7 @@ export function MinLength(n: number, message?: string): ReturnType<typeof string
113
119
  * The maximum length for the string or array
114
120
  * @param n The maximum length
115
121
  * @param message The message to show when the constraint fails
116
- * @augments `@trv:schema/Field`
122
+ * @augments `@travetto/schema:Field`
117
123
  */
118
124
  export function MaxLength(n: number, message?: string): ReturnType<typeof stringArrProp> { return stringArrProp({ maxlength: { n, message } }); }
119
125
 
@@ -121,7 +127,7 @@ export function MaxLength(n: number, message?: string): ReturnType<typeof string
121
127
  * The minimum value
122
128
  * @param n The minimum value
123
129
  * @param message The message to show when the constraint fails
124
- * @augments `@trv:schema/Field`
130
+ * @augments `@travetto/schema:Field`
125
131
  */
126
132
  export function Min<T extends number | Date>(n: T, message?: string): ReturnType<typeof dateNumberProp> {
127
133
  return dateNumberProp({ min: { n, message } });
@@ -131,7 +137,7 @@ export function Min<T extends number | Date>(n: T, message?: string): ReturnType
131
137
  * The maximum value
132
138
  * @param n The maximum value
133
139
  * @param message The message to show when the constraint fails
134
- * @augments `@trv:schema/Field`
140
+ * @augments `@travetto/schema:Field`
135
141
  */
136
142
  export function Max<T extends number | Date>(n: T, message?: string): ReturnType<typeof dateNumberProp> {
137
143
  return dateNumberProp({ max: { n, message } });
@@ -140,21 +146,21 @@ export function Max<T extends number | Date>(n: T, message?: string): ReturnType
140
146
  /**
141
147
  * Mark a field as an email
142
148
  * @param message The message to show when the constraint fails
143
- * @augments `@trv:schema/Field`
149
+ * @augments `@travetto/schema:Field`
144
150
  */
145
151
  export function Email(message?: string): ReturnType<typeof Match> { return Match(CommonRegExp.email, message); }
146
152
 
147
153
  /**
148
154
  * Mark a field as an telephone number
149
155
  * @param message The message to show when the constraint fails
150
- * @augments `@trv:schema/Field`
156
+ * @augments `@travetto/schema:Field`
151
157
  */
152
158
  export function Telephone(message?: string): ReturnType<typeof Match> { return Match(CommonRegExp.telephone, message); }
153
159
 
154
160
  /**
155
161
  * Mark a field as a url
156
162
  * @param message The message to show when the constraint fails
157
- * @augments `@trv:schema/Field`
163
+ * @augments `@travetto/schema:Field`
158
164
  */
159
165
  export function Url(message?: string): ReturnType<typeof Match> { return Match(CommonRegExp.url, message); }
160
166
 
@@ -162,38 +168,38 @@ export function Url(message?: string): ReturnType<typeof Match> { return Match(C
162
168
  * Determine the numeric precision of the value
163
169
  * @param digits The number of digits a number should have
164
170
  * @param decimals The number of decimal digits to support
165
- * @augments `@trv:schema/Field`
171
+ * @augments `@travetto/schema:Field`
166
172
  */
167
173
  export function Precision(digits: number, decimals?: number): ReturnType<typeof numberProp> { return numberProp({ precision: [digits, decimals] }); }
168
174
 
169
175
  /**
170
176
  * Mark a number as an integer
171
- * @augments `@trv:schema/Field`
177
+ * @augments `@travetto/schema:Field`
172
178
  */
173
179
  export function Integer(): ReturnType<typeof numberProp> { return Precision(0); }
174
180
 
175
181
  /**
176
182
  * Mark a number as a float
177
- * @augments `@trv:schema/Field`
183
+ * @augments `@travetto/schema:Field`
178
184
  */
179
185
  export function Float(): ReturnType<typeof numberProp> { return Precision(10, 7); }
180
186
 
181
187
  /**
182
188
  * Mark a number as a long value
183
- * @augments `@trv:schema/Field`
189
+ * @augments `@travetto/schema:Field`
184
190
  */
185
191
  export function Long(): ReturnType<typeof numberProp> { return Precision(19, 0); }
186
192
 
187
193
  /**
188
194
  * Mark a number as a currency
189
- * @augments `@trv:schema/Field`
195
+ * @augments `@travetto/schema:Field`
190
196
  */
191
197
  export function Currency(): ReturnType<typeof numberProp> { return Precision(13, 2); }
192
198
 
193
199
  /**
194
200
  * Mark a field as ignored
195
201
  *
196
- * @augments `@trv:schema/Ignore`
202
+ * @augments `@travetto/schema:Ignore`
197
203
  */
198
204
  export function Ignore(): PropertyDecorator {
199
205
  return (target: Object, property: string | symbol) => { };
@@ -1,16 +1,21 @@
1
1
  import { Class } from '@travetto/base';
2
2
 
3
+ import { BindUtil } from '../bind-util';
3
4
  import { SchemaRegistry } from '../service/registry';
4
5
  import { ViewFieldsConfig } from '../service/types';
6
+ import { DeepPartial } from '../types';
5
7
  import { ValidatorFn } from '../validate/types';
6
8
 
7
9
  /**
8
10
  * Register a class as a Schema
9
11
  *
10
- * @augments `@trv:schema/Schema`
12
+ * @augments `@travetto/schema:Schema`
11
13
  */
12
14
  export function Schema() { // Auto is used during compilation
13
15
  return <T, U extends Class<T>>(target: U): U => {
16
+ target.from ??= function <V>(this: Class<V>, data: DeepPartial<V>, view?: string): V {
17
+ return BindUtil.bindSchema(this, data, { view });
18
+ };
14
19
  SchemaRegistry.getOrCreatePending(target);
15
20
  return target;
16
21
  };
@@ -1 +1 @@
1
- export const AllViewⲐ: unique symbol = Symbol.for('@trv:schema/all');
1
+ export const AllViewⲐ: unique symbol = Symbol.for('@travetto/schema:all');
@@ -6,7 +6,7 @@ import { ChangeEvent } from '@travetto/registry';
6
6
  import { FieldConfig, ClassConfig } from './types';
7
7
  import { AllViewⲐ } from '../internal/types';
8
8
 
9
- const id = (c: Class | string): string => typeof c === 'string' ? c : c.ᚕid;
9
+ const id = (c: Class | string): string => typeof c === 'string' ? c : c.Ⲑid;
10
10
 
11
11
  interface FieldMapping {
12
12
  path: FieldConfig[];
@@ -57,13 +57,6 @@ class $SchemaChangeListener {
57
57
  this.#emitter.on('field', handler);
58
58
  }
59
59
 
60
- /**
61
- * Reset the listener
62
- */
63
- reset(): void {
64
- this.#mapping.clear();
65
- }
66
-
67
60
  /**
68
61
  * Clear dependency mappings for a given class
69
62
  */
@@ -139,7 +132,7 @@ class $SchemaChangeListener {
139
132
  }
140
133
 
141
134
  // Handle class references changing, but keeping same id
142
- const compareTypes = (a: Class, b: Class): boolean => 'id' in a ? a.ᚕid === b.ᚕid : a === b;
135
+ const compareTypes = (a: Class, b: Class): boolean => 'id' in a ? a.Ⲑid === b.Ⲑid : a === b;
143
136
 
144
137
  for (const c of currFields) {
145
138
  if (prevFields.has(c)) {
@@ -1,4 +1,4 @@
1
- import { Class, AppError, Util, ClassInstance, ConcreteClass } from '@travetto/base';
1
+ import { Class, AppError, ObjectUtil, ClassInstance, ConcreteClass } from '@travetto/base';
2
2
  import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
3
3
 
4
4
  import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig, ViewConfig } from './types';
@@ -8,7 +8,7 @@ import { AllViewⲐ } from '../internal/types';
8
8
 
9
9
  function hasType<T>(o: unknown): o is { type: Class<T> | string } {
10
10
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11
- return !!o && !Util.isPrimitive(o) && 'type' in (o as object) && !!(o as Record<string, string>)['type'];
11
+ return !!o && !ObjectUtil.isPrimitive(o) && 'type' in (o as object) && !!(o as Record<string, string>)['type'];
12
12
  }
13
13
 
14
14
  function isWithType<T>(o: T, cfg: ClassConfig | undefined): o is T & { type?: string } {
@@ -20,12 +20,12 @@ function getConstructor<T>(o: T): ConcreteClass<T> {
20
20
  return (o as unknown as ClassInstance<T>).constructor;
21
21
  }
22
22
 
23
-
24
23
  /**
25
24
  * Schema registry for listening to changes
26
25
  */
27
26
  class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
28
27
 
28
+ #accessorDescriptors = new Map<Class, Map<string, PropertyDescriptor>>();
29
29
  #subTypes = new Map<Class, Map<string, Class>>();
30
30
  #typeKeys = new Map<Class, string>();
31
31
  #pendingViews = new Map<Class, Map<string, ViewFieldsConfig<unknown>>>();
@@ -55,6 +55,32 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
55
55
  }
56
56
  }
57
57
 
58
+ /**
59
+ * Retrieve class level metadata
60
+ * @param cls
61
+ * @param prop
62
+ * @param key
63
+ * @returns
64
+ */
65
+ getMetadata<K>(cls: Class, key: symbol): K | undefined {
66
+ const cfg = this.get(cls);
67
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
68
+ return cfg.metadata?.[key] as K;
69
+ }
70
+
71
+ /**
72
+ * Retrieve pending class level metadata, or create if needed
73
+ * @param cls
74
+ * @param prop
75
+ * @param key
76
+ * @returns
77
+ */
78
+ getOrCreatePendingMetadata<K>(cls: Class, key: symbol, value: K): K {
79
+ const cfg = this.getOrCreatePending(cls);
80
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
81
+ return ((cfg.metadata ??= {})[key] ??= value) as K;
82
+ }
83
+
58
84
  /**
59
85
  * Ensure type is set properly
60
86
  */
@@ -64,6 +90,24 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
64
90
  }
65
91
  }
66
92
 
93
+ /**
94
+ * Provides the prototype-derived descriptor for a property
95
+ */
96
+ getAccessorDescriptor(cls: Class, field: string): PropertyDescriptor {
97
+ if (!this.#accessorDescriptors.has(cls)) {
98
+ this.#accessorDescriptors.set(cls, new Map());
99
+ }
100
+ const map = this.#accessorDescriptors.get(cls)!;
101
+ if (!map.has(field)) {
102
+ let proto = cls.prototype;
103
+ while (proto && !Object.hasOwn(proto, field)) {
104
+ proto = proto.prototype;
105
+ }
106
+ map.set(field, Object.getOwnPropertyDescriptor(proto, field)!);
107
+ }
108
+ return map.get(field)!;
109
+ }
110
+
67
111
  /**
68
112
  * Find the subtype for a given instance
69
113
  * @param cls Class for instance
@@ -80,7 +124,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
80
124
  */
81
125
  resolveSubType(cls: Class, type: Class | string): Class {
82
126
  if (this.#subTypes.has(cls)) {
83
- const typeId = type && (typeof type === 'string' ? type : type.ᚕid);
127
+ const typeId = type && (typeof type === 'string' ? type : type.Ⲑid);
84
128
  if (type) {
85
129
  return this.#subTypes.get(cls)!.get(typeId) ?? cls;
86
130
  }
@@ -114,7 +158,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
114
158
  this.#subTypes.set(parent, new Map());
115
159
  }
116
160
  this.#subTypes.get(parent)!.set(type, cls);
117
- this.#subTypes.get(parent)!.set(cls.ᚕid, cls);
161
+ this.#subTypes.get(parent)!.set(cls.Ⲑid, cls);
118
162
  parent = this.getParentClass(parent!)!;
119
163
  parentConfig = this.get(parent);
120
164
  }
@@ -147,6 +191,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
147
191
  class: cls,
148
192
  validators: [],
149
193
  subType: false,
194
+ metadata: {},
150
195
  views: {
151
196
  [AllViewⲐ]: {
152
197
  schema: {},
@@ -301,6 +346,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
301
346
  schema: { ...dest.views[AllViewⲐ].schema, ...src.views?.[AllViewⲐ].schema },
302
347
  fields: [...dest.views[AllViewⲐ].fields, ...src.views?.[AllViewⲐ].fields ?? []]
303
348
  };
349
+ dest.metadata = { ...src.metadata ?? {}, ...dest.metadata ?? {} };
304
350
  dest.subType = src.subType || dest.subType;
305
351
  dest.title = src.title || dest.title;
306
352
  dest.validators = [...src.validators ?? [], ...dest.validators];
@@ -377,6 +423,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
377
423
  this.#subTypes.clear();
378
424
  this.#typeKeys.delete(cls);
379
425
  this.#methodSchemas.delete(cls);
426
+ this.#accessorDescriptors.delete(cls);
380
427
 
381
428
  // Recompute subtype mappings
382
429
  for (const el of this.entries.keys()) {
@@ -1,5 +1,4 @@
1
- import { Class } from '@travetto/base';
2
- import { Primitive } from '@travetto/base/src/internal/global-types';
1
+ import { Primitive, Class } from '@travetto/base';
3
2
 
4
3
  import { AllViewⲐ } from '../internal/types';
5
4
  import { ValidatorFn } from '../validate/types';
@@ -68,6 +67,10 @@ export interface ClassConfig extends DescribableConfig {
68
67
  * Is the class a sub type
69
68
  */
70
69
  subType?: boolean;
70
+ /**
71
+ * Metadata that is related to the schema structure
72
+ */
73
+ metadata?: Record<symbol, unknown>;
71
74
  }
72
75
 
73
76
  /**
@@ -149,6 +152,14 @@ export interface FieldConfig extends DescribableConfig {
149
152
  * Is the field readonly, or write only?, defaults to no restrictions
150
153
  */
151
154
  access?: 'readonly' | 'writeonly';
155
+ /**
156
+ * Is this field secret, defaults to no, can be used to hide field when exporting values
157
+ */
158
+ secret?: boolean;
159
+ /**
160
+ * Is this field a getter or setter
161
+ */
162
+ accessor?: string;
152
163
  }
153
164
 
154
165
  export type ViewFieldsConfig<T> = { with: Extract<(keyof T), string>[] } | { without: Extract<(keyof T), string>[] };
package/src/typings.d.ts CHANGED
@@ -13,7 +13,7 @@ declare global {
13
13
 
14
14
  namespace NodeJS {
15
15
  /**
16
- * @concrete stream:Readable:node
16
+ * @concrete stream:Readable
17
17
  */
18
18
  interface ReadableStream { }
19
19
  }
@@ -21,7 +21,7 @@ declare global {
21
21
 
22
22
  declare module 'stream' {
23
23
  /**
24
- * @concrete stream:Readable:node
24
+ * @concrete stream:Readable
25
25
  */
26
26
  interface Readable { }
27
27
  }
@@ -1,3 +1,4 @@
1
+ import { TypedObject } from '@travetto/base';
1
2
  import { Messages } from './messages';
2
3
 
3
4
  declare global {
@@ -18,7 +19,7 @@ export const CommonRegExp = {
18
19
  };
19
20
 
20
21
  // Rebind regexes
21
- for (const k of Object.keys(CommonRegExp)) {
22
+ for (const k of TypedObject.keys(CommonRegExp)) {
22
23
  CommonRegExp[k].name = `[[:${k}:]]`;
23
24
  Messages.set(CommonRegExp[k].name!, Messages.get(k)!);
24
25
  }
@@ -1,4 +1,4 @@
1
- import { Class, ClassInstance, Util } from '@travetto/base';
1
+ import { Class, ClassInstance, TypedObject, ObjectUtil } from '@travetto/base';
2
2
 
3
3
  import { FieldConfig, SchemaConfig } from '../service/types';
4
4
  import { SchemaRegistry } from '../service/registry';
@@ -38,7 +38,7 @@ export class SchemaValidator {
38
38
  static #validateSchema<T>(schema: SchemaConfig, o: T, relative: string): ValidationError[] {
39
39
  let errors: ValidationError[] = [];
40
40
 
41
- const fields = Object.keys<SchemaConfig>(schema);
41
+ const fields = TypedObject.keys<SchemaConfig>(schema);
42
42
  for (const field of fields) {
43
43
  if (schema[field].access !== 'readonly') { // Do not validate readonly fields
44
44
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -236,7 +236,7 @@ export class SchemaValidator {
236
236
  */
237
237
  static async validate<T>(cls: Class<T>, o: T, view?: string): Promise<T> {
238
238
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
239
- if (!Util.isPlainObject(o) && !(o instanceof cls || cls.ᚕid === (o as ClassInstance<T>).constructor.ᚕid)) {
239
+ if (!ObjectUtil.isPlainObject(o) && !(o instanceof cls || cls.Ⲑid === (o as ClassInstance<T>).constructor.Ⲑid)) {
240
240
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
241
241
  throw new TypeMismatchError(cls.name, (o as ClassInstance).constructor.name);
242
242
  }
@@ -1,5 +1,5 @@
1
- import * as ts from 'typescript';
2
- import { AnyType, DeclarationUtil, DecoratorUtil, DocUtil, ParamDocumentation, TransformerId, TransformerState } from '@travetto/transformer';
1
+ import ts from 'typescript';
2
+ import { type AnyType, DeclarationUtil, DecoratorUtil, DocUtil, ParamDocumentation, TransformerState } from '@travetto/transformer';
3
3
 
4
4
  const SCHEMA_MOD = '@travetto/schema/src/decorator/schema';
5
5
  const FIELD_MOD = '@travetto/schema/src/decorator/field';
@@ -16,7 +16,7 @@ export class SchemaTransformUtil {
16
16
  case 'external': return state.getOrImport(type);
17
17
  case 'tuple': return state.fromLiteral(type.subTypes.map(x => this.toConcreteType(state, x, node, root)!));
18
18
  case 'literal': {
19
- if ((type.ctor === Array || type.ctor === Set) && type.typeArguments?.length) {
19
+ if ((type.ctor === Array) && type.typeArguments?.length) {
20
20
  return state.fromLiteral([this.toConcreteType(state, type.typeArguments[0], node, root)]);
21
21
  } else if (type.ctor) {
22
22
  return state.createIdentifier(type.ctor.name!);
@@ -50,7 +50,7 @@ export class SchemaTransformUtil {
50
50
  )
51
51
  );
52
52
  cls.getText = (): string => '';
53
- state.addStatement(cls, root || node);
53
+ state.addStatements([cls], root || node);
54
54
  }
55
55
  return id;
56
56
  }
@@ -81,7 +81,7 @@ export class SchemaTransformUtil {
81
81
  if (!ts.isGetAccessorDeclaration(node) && !ts.isSetAccessorDeclaration(node)) {
82
82
  // eslint-disable-next-line no-bitwise
83
83
  if ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Readonly) > 0) {
84
- attrs.push(state.factory.createPropertyAssignment('mode', state.fromLiteral('readonly')));
84
+ attrs.push(state.factory.createPropertyAssignment('access', state.fromLiteral('readonly')));
85
85
  } else if (!node.questionToken && !typeExpr.undefinable && !node.initializer) {
86
86
  attrs.push(state.factory.createPropertyAssignment('required', state.fromLiteral({ active: true })));
87
87
  }
@@ -90,11 +90,12 @@ export class SchemaTransformUtil {
90
90
  }
91
91
  } else {
92
92
  const acc = DeclarationUtil.getAccessorPair(node);
93
+ attrs.push(state.factory.createPropertyAssignment('accessor', state.fromLiteral(true)));
93
94
  if (!acc.setter) {
94
95
  attrs.push(state.factory.createPropertyAssignment('access', state.fromLiteral('readonly')));
95
96
  }
96
97
  if (!acc.getter) {
97
- attrs.push(state.factory.createPropertyAssignment('mode', state.fromLiteral('writeonly')));
98
+ attrs.push(state.factory.createPropertyAssignment('access', state.fromLiteral('writeonly')));
98
99
  } else if (!typeExpr.undefinable) {
99
100
  attrs.push(state.factory.createPropertyAssignment('required', state.fromLiteral({ active: true })));
100
101
  }
@@ -129,7 +130,7 @@ export class SchemaTransformUtil {
129
130
 
130
131
  const params: ts.Expression[] = [];
131
132
 
132
- const existing = state.findDecorator({ [TransformerId]: '@trv:schema', name: 'util' }, node, 'Field', FIELD_MOD);
133
+ const existing = state.findDecorator('@travetto/schema', node, 'Field', FIELD_MOD);
133
134
  if (!existing) {
134
135
  const resolved = this.toConcreteType(state, typeExpr, node, config.root);
135
136
  params.push(resolved);
@@ -179,7 +180,7 @@ export class SchemaTransformUtil {
179
180
  const out: Record<string, unknown> = {};
180
181
 
181
182
  while (type?.key === 'literal' && type.typeArguments?.length) {
182
- if (type.ctor === Array || type.ctor === Set) {
183
+ if (type.ctor === Array) {
183
184
  out.array = true;
184
185
  }
185
186
  type = type.typeArguments?.[0] ?? { key: 'literal', ctor: Object }; // We have a promise nested
@@ -1,29 +1,27 @@
1
- import * as ts from 'typescript';
1
+ import ts from 'typescript';
2
2
 
3
3
  import {
4
- TransformerState, OnProperty, OnClass, AfterClass, DecoratorMeta, DocUtil, DeclarationUtil, TransformerId, OnGetter, OnSetter
4
+ TransformerState, OnProperty, OnClass, AfterClass, DecoratorMeta, DocUtil, DeclarationUtil, OnGetter, OnSetter
5
5
  } from '@travetto/transformer';
6
6
 
7
7
  import { SchemaTransformUtil } from './transform-util';
8
8
 
9
- const inSchema = Symbol.for('@trv:schema/schema');
10
- const accessors = Symbol.for('@trv:schema/schema');
9
+ const inSchema = Symbol.for('@travetto/schema:schema');
10
+ const accessors = Symbol.for('@travetto/schema:accessors');
11
11
 
12
12
  interface AutoState {
13
13
  [inSchema]?: boolean;
14
14
  [accessors]?: Set<string>;
15
15
  }
16
16
 
17
- const SCHEMA_MOD = '@travetto/schema/src/decorator/schema';
18
- const COMMON_MOD = '@travetto/schema/src/decorator/common';
17
+ const SCHEMA_IMPORT = '@travetto/schema/src/decorator/schema';
18
+ const COMMON_IMPORT = '@travetto/schema/src/decorator/common';
19
19
 
20
20
  /**
21
21
  * Processes `@Schema` to register class as a valid Schema
22
22
  */
23
23
  export class SchemaTransformer {
24
24
 
25
- static [TransformerId] = '@trv:schema';
26
-
27
25
  /**
28
26
  * Track schema on start
29
27
  */
@@ -43,12 +41,12 @@ export class SchemaTransformer {
43
41
 
44
42
  const comments = DocUtil.describeDocs(node);
45
43
 
46
- if (!state.findDecorator(this, node, 'Schema', SCHEMA_MOD)) {
47
- modifiers.unshift(state.createDecorator(SCHEMA_MOD, 'Schema'));
44
+ if (!state.findDecorator(this, node, 'Schema', SCHEMA_IMPORT)) {
45
+ modifiers.unshift(state.createDecorator(SCHEMA_IMPORT, 'Schema'));
48
46
  }
49
47
 
50
48
  if (comments.description) {
51
- modifiers.push(state.createDecorator(COMMON_MOD, 'Describe', state.fromLiteral({
49
+ modifiers.push(state.createDecorator(COMMON_IMPORT, 'Describe', state.fromLiteral({
52
50
  title: comments.description
53
51
  })));
54
52
  }
@@ -1,11 +0,0 @@
1
- /**
2
- * Registers the bind utilities on class
3
- */
4
- export const init = {
5
- key: '@trv:schema/init',
6
- after: ['@trv:registry/init'], // Should be global
7
- action: async (): Promise<void> => {
8
- const { BindUtil } = await import('../src/bind-util');
9
- BindUtil.register();
10
- }
11
- };