@travetto/schema 6.0.0-rc.1 → 6.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,12 +13,12 @@ npm install @travetto/schema
13
13
  yarn add @travetto/schema
14
14
  ```
15
15
 
16
- 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 leveraged in the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support"), [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework"), [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 provide validation on correctness of input, whether it is a rest request, command line inputs, or a configuration file.
16
+ 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 leveraged in the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support"), [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework"), [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection."), [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 provide validation on correctness of input, whether it is a web request, command line inputs, or a configuration file.
17
17
 
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#L14) decoration.
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#L13) 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:
@@ -64,27 +64,27 @@ User:
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
66
  * [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L25) defines a field that will be serialized.
67
- * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L65) defines a that field should be required
68
- * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L72) defines the allowable values that a field can have
69
- * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L93) 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#L101) enforces min length of a string
71
- * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L111) enforces max length of a string
72
- * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L101) enforces min value for a date or a number
73
- * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L111) enforces max value for a date or a number
74
- * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L138) ensures string field matches basic email regex
75
- * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L145) ensures string field matches basic telephone regex
76
- * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L152) ensures string field matches basic url regex
77
- * [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L191) exclude from auto schema registration
78
- * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L166) ensures number passed in is only a whole number
79
- * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L172) ensures number passed in allows fractional values
80
- * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L184) provides support for standard currency
81
- * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L80) 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#L85) same as text, but expects longer form content
83
- * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L52) 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#L46) 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#L58) 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#L200) 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#L206) allows for promoting a given field as the owner of the sub type discriminator (defaults to `type`).
67
+ * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L63) defines a that field should be required
68
+ * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L70) defines the allowable values that a field can have
69
+ * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L91) 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#L99) enforces min length of a string
71
+ * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L109) enforces max length of a string
72
+ * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99) enforces min value for a date or a number
73
+ * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L109) enforces max value for a date or a number
74
+ * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L136) ensures string field matches basic email regex
75
+ * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L143) ensures string field matches basic telephone regex
76
+ * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L150) ensures string field matches basic url regex
77
+ * [@Ignore](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L189) exclude from auto schema registration
78
+ * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L164) ensures number passed in is only a whole number
79
+ * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L170) ensures number passed in allows fractional values
80
+ * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L182) provides support for standard currency
81
+ * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L78) 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#L83) same as text, but expects longer form content
83
+ * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L50) 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#L45) 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#L56) 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#L198) 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#L204) 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
@@ -93,7 +93,7 @@ Just like the class, all fields can be defined with
93
93
  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#L15) decorator.
94
94
 
95
95
  ### Parameters
96
- Parameters are available in certain scenarios (e.g. [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") 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.
96
+ Parameters are available in certain scenarios (e.g. [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") 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.
97
97
 
98
98
  **Code: Sample Parameter Usage**
99
99
  ```typescript
@@ -112,7 +112,7 @@ export class ParamUsage {
112
112
  At runtime, once a schema is registered, a programmer can utilize this structure to perform specific operations. Specifically binding and validation.
113
113
 
114
114
  ### Binding
115
- 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:
115
+ 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#L13) and a JS object that will be the source of the binding. Given the schema:
116
116
 
117
117
  **Code: Sub Schemas via Address**
118
118
  ```typescript
@@ -136,7 +136,7 @@ A binding operation could look like:
136
136
 
137
137
  **Code: Binding from JSON to Schema**
138
138
  ```typescript
139
- import { Person } from './person';
139
+ import { Person } from './person.ts';
140
140
 
141
141
  export function Test(): Person {
142
142
  return Person.from({
@@ -192,7 +192,7 @@ But now with an invalid json object
192
192
  ```typescript
193
193
  import { SchemaValidator } from '@travetto/schema';
194
194
 
195
- import { Person } from './person';
195
+ import { Person } from './person.ts';
196
196
 
197
197
  export async function validate(): Promise<void> {
198
198
 
@@ -310,32 +310,49 @@ To that end, the module supports two concepts:
310
310
  ### Type Adapters
311
311
  This feature is meant to allow for simple Typescript types to be able to be backed by a proper class. This is because all of the typescript type information disappears at runtime, and so only concrete types (like classes) remain. An example of this, can be found with how the [Data Model Querying](https://github.com/travetto/travetto/tree/main/module/model-query#readme "Datastore abstraction for advanced query support.") module handles geo data.
312
312
 
313
- **Code: Simple Custom Type**
313
+ **Code: Point Contract**
314
314
  ```typescript
315
- import { DataUtil } from '@travetto/schema';
316
-
317
315
  /**
318
- * @concrete #PointImpl
316
+ * Geometric Point as [number,number] with validation and binding support
317
+ *
318
+ * @concrete ./internal/types.ts#PointImpl
319
319
  */
320
320
  export type Point = [number, number];
321
+ ```
322
+
323
+ **Code: Point Implementation**
324
+ ```typescript
325
+ import { DataUtil } from '../data.ts';
321
326
 
322
- const INVALID = Symbol.for('invalid-point');
327
+ const InvalidSymbol = Symbol();
323
328
 
329
+ /**
330
+ * Point Implementation
331
+ */
324
332
  export class PointImpl {
333
+
334
+ /**
335
+ * Validate we have an actual point
336
+ */
325
337
  static validateSchema(input: unknown): 'type' | undefined {
326
- const ret = this.bindSchema(input);
327
- return ret !== INVALID && ret && !isNaN(ret[0]) && !isNaN(ret[1]) ? undefined : 'type';
338
+ const bound = this.bindSchema(input);
339
+ return bound !== InvalidSymbol && bound && !isNaN(bound[0]) && !isNaN(bound[1]) ? undefined : 'type';
328
340
  }
329
341
 
330
- static bindSchema(input: unknown): [number, number] | typeof INVALID | undefined {
342
+ /**
343
+ * Convert to tuple of two numbers
344
+ */
345
+ static bindSchema(input: unknown): [number, number] | typeof InvalidSymbol | undefined {
331
346
  if (Array.isArray(input) && input.length === 2) {
332
347
  const [a, b] = input.map(x => DataUtil.coerceType(x, Number, false));
333
348
  return [a, b];
334
349
  } else {
335
- return INVALID;
350
+ return InvalidSymbol;
336
351
  }
337
352
  }
338
353
  }
354
+
355
+ Object.defineProperty(PointImpl, 'name', { value: 'Point' });
339
356
  ```
340
357
 
341
358
  What you can see here is that the `Point` type is now backed by a class that supports:
@@ -344,8 +361,7 @@ What you can see here is that the `Point` type is now backed by a class that sup
344
361
 
345
362
  **Code: Simple Custom Type Usage**
346
363
  ```typescript
347
- import { Schema } from '@travetto/schema';
348
- import { Point } from './custom-type';
364
+ import { Schema, Point } from '@travetto/schema';
349
365
 
350
366
  @Schema()
351
367
  export class LocationAware {
@@ -369,8 +385,8 @@ Validation Failed {
369
385
  "errors": [
370
386
  {
371
387
  "kind": "type",
372
- "type": "PointImpl",
373
- "message": "point is not a valid PointImpl",
388
+ "type": "Point",
389
+ "message": "point is not a valid Point",
374
390
  "path": "point"
375
391
  }
376
392
  ]
package/__index__.ts CHANGED
@@ -1,15 +1,16 @@
1
- import type { } from './src/global';
2
- export * from './src/decorator/field';
3
- export * from './src/decorator/schema';
4
- export * from './src/decorator/common';
5
- export * from './src/service/changes';
6
- export * from './src/service/registry';
7
- export * from './src/service/types';
8
- export * from './src/validate/messages';
9
- export * from './src/validate/regexp';
10
- export * from './src/validate/validator';
11
- export * from './src/validate/error';
12
- export * from './src/validate/types';
13
- export * from './src/bind-util';
14
- export * from './src/data';
15
- export * from './src/name';
1
+ import type { } from './src/global.d.ts';
2
+ export * from './src/decorator/field.ts';
3
+ export * from './src/decorator/schema.ts';
4
+ export * from './src/decorator/common.ts';
5
+ export * from './src/service/changes.ts';
6
+ export * from './src/service/registry.ts';
7
+ export * from './src/service/types.ts';
8
+ export * from './src/validate/messages.ts';
9
+ export * from './src/validate/regexp.ts';
10
+ export * from './src/validate/validator.ts';
11
+ export * from './src/validate/error.ts';
12
+ export * from './src/validate/types.ts';
13
+ export * from './src/bind-util.ts';
14
+ export * from './src/data.ts';
15
+ export * from './src/name.ts';
16
+ export * from './src/types.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "version": "6.0.0-rc.1",
3
+ "version": "6.0.0-rc.2",
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": "^6.0.0-rc.1"
30
+ "@travetto/registry": "^6.0.0-rc.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^6.0.0-rc.1"
33
+ "@travetto/transformer": "^6.0.0-rc.3"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
package/src/bind-util.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import { castTo, Class, classConstruct, asFull, TypedObject, castKey } from '@travetto/runtime';
2
2
 
3
- import { DataUtil } from './data';
4
- import { AllViewSymbol } from './internal/types';
5
- import { SchemaRegistry } from './service/registry';
6
- import { FieldConfig } from './service/types';
3
+ import { DataUtil } from './data.ts';
4
+ import { SchemaRegistry } from './service/registry.ts';
5
+ import { FieldConfig } from './service/types.ts';
7
6
 
8
7
  type BindConfig = {
9
- view?: string | typeof AllViewSymbol;
8
+ view?: string;
10
9
  filterField?: (field: FieldConfig) => boolean;
11
10
  filterValue?: (value: unknown, field: FieldConfig) => boolean;
12
11
  };
@@ -169,7 +168,7 @@ export class BindUtil {
169
168
  * @param cfg The bind configuration
170
169
  */
171
170
  static bindSchemaToObject<T>(cons: Class<T>, obj: T, data?: object, cfg: BindConfig = {}): T {
172
- const view = cfg.view ?? AllViewSymbol; // Does not convey
171
+ const view = cfg.view; // Does not convey
173
172
  delete cfg.view;
174
173
 
175
174
  if (!!data && isInstance<T>(data)) {
@@ -181,10 +180,12 @@ export class BindUtil {
181
180
  obj[k] = data[k];
182
181
  }
183
182
  } else {
184
-
185
- const viewConf = conf.views[view];
186
- if (!viewConf) {
187
- throw new Error(`View not found: ${view.toString()}`);
183
+ let viewConf = conf.totalView;
184
+ if (view) {
185
+ viewConf = conf.views[view];
186
+ if (!viewConf) {
187
+ throw new Error(`View not found: ${view.toString()}`);
188
+ }
188
189
  }
189
190
 
190
191
  for (const schemaFieldName of viewConf.fields) {
package/src/data.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isNumberObject as isNum, isBooleanObject as isBool, isStringObject as isStr } from 'node:util/types';
2
2
 
3
3
  import { asConstructable, castTo, Class, asFull, TypedObject } from '@travetto/runtime';
4
- import { UnknownType } from './internal/types';
4
+ import { UnknownType } from './types.ts';
5
5
 
6
6
  const REGEX_PAT = /[\/](.*)[\/](i|g|m|s)?/;
7
7
 
@@ -49,26 +49,26 @@ export class DataUtil {
49
49
  const isSimpA = !isEmptyA && this.isSimpleValue(a);
50
50
  const isSimpB = !isEmptyB && this.isSimpleValue(b);
51
51
 
52
- let ret: unknown;
52
+ let value: unknown;
53
53
 
54
54
  if (isEmptyA || isEmptyB) { // If no `a`, `b` always wins
55
55
  if (mode === 'replace' || b === null || !isEmptyB) {
56
- ret = isEmptyB ? b : this.shallowClone(b);
56
+ value = isEmptyB ? b : this.shallowClone(b);
57
57
  } else if (!isEmptyA) {
58
- ret = this.shallowClone(a);
58
+ value = this.shallowClone(a);
59
59
  } else {
60
- ret = undefined;
60
+ value = undefined;
61
61
  }
62
62
  } else {
63
63
  if (isArrA !== isArrB || isSimpA !== isSimpB) {
64
64
  throw new Error(`Cannot merge differing types ${a} and ${b}`);
65
65
  }
66
66
  if (Array.isArray(b)) { // Arrays
67
- ret = a; // Write onto A
67
+ value = a; // Write onto A
68
68
  if (mode === 'replace') {
69
- ret = b;
69
+ value = b;
70
70
  } else {
71
- const retArr: unknown[] = castTo(ret);
71
+ const retArr: unknown[] = castTo(value);
72
72
  const bArr = b;
73
73
  for (let i = 0; i < bArr.length; i++) {
74
74
  retArr[i] = this.#deepAssignRaw(retArr[i], bArr[i], mode);
@@ -76,19 +76,19 @@ export class DataUtil {
76
76
  }
77
77
  } else if (isSimpB) { // Scalars
78
78
  const match = typeof a === typeof b;
79
- ret = b;
79
+ value = b;
80
80
 
81
81
  if (!match) { // If types do not match
82
82
  if (mode === 'strict') { // Bail on strict
83
83
  throw new Error(`Cannot merge ${a} [${typeof a}] with ${b} [${typeof b}]`);
84
84
  } else if (mode === 'coerce') { // Force on coerce
85
- ret = this.coerceType(b, asConstructable(a).constructor, false);
85
+ value = this.coerceType(b, asConstructable(a).constructor, false);
86
86
  }
87
87
  }
88
88
  } else { // Object merge
89
- ret = a;
89
+ value = a;
90
90
  const bObj: Record<string, unknown> = castTo(b);
91
- const retObj: Record<string, unknown> = castTo(ret);
91
+ const retObj: Record<string, unknown> = castTo(value);
92
92
 
93
93
  for (const key of Object.keys(bObj)) {
94
94
  if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
@@ -98,7 +98,7 @@ export class DataUtil {
98
98
  }
99
99
  }
100
100
  }
101
- return ret;
101
+ return value;
102
102
  }
103
103
 
104
104
  /**
@@ -142,11 +142,11 @@ export class DataUtil {
142
142
 
143
143
  switch (type) {
144
144
  case Date: {
145
- let res: Date | undefined;
145
+ let value: Date | undefined;
146
146
  if (typeof input === 'object' && 'toDate' in input && typeof input.toDate === 'function') {
147
- res = castTo(input.toDate());
147
+ value = castTo(input.toDate());
148
148
  } else {
149
- res = input instanceof Date ?
149
+ value = input instanceof Date ?
150
150
  input :
151
151
  typeof input === 'number' ?
152
152
  new Date(input) :
@@ -154,17 +154,17 @@ export class DataUtil {
154
154
  new Date(parseInt(input, 10)) :
155
155
  new Date(input.toString());
156
156
  }
157
- if (strict && res && Number.isNaN(res.getTime())) {
157
+ if (strict && value && Number.isNaN(value.getTime())) {
158
158
  throw new Error(`Invalid date value: ${input}`);
159
159
  }
160
- return res;
160
+ return value;
161
161
  }
162
162
  case Number: {
163
- const res = `${input}`.includes('.') ? parseFloat(`${input}`) : parseInt(`${input}`, 10);
164
- if (strict && Number.isNaN(res)) {
163
+ const value = `${input}`.includes('.') ? parseFloat(`${input}`) : parseInt(`${input}`, 10);
164
+ if (strict && Number.isNaN(value)) {
165
165
  throw new Error(`Invalid numeric value: ${input}`);
166
166
  }
167
- return res;
167
+ return value;
168
168
  }
169
169
  case BigInt: {
170
170
  if (typeof input === 'bigint') {
@@ -252,7 +252,9 @@ export class DataUtil {
252
252
  * @returns
253
253
  */
254
254
  static filterByKeys<T>(obj: T, exclude: (string | RegExp)[]): T {
255
- if (obj !== null && obj !== undefined && typeof obj === 'object') {
255
+ if (Array.isArray(obj)) {
256
+ return castTo(obj.map(x => this.filterByKeys(x, exclude)));
257
+ } else if (obj !== null && obj !== undefined && typeof obj === 'object') {
256
258
  const out: Partial<T> = {};
257
259
  for (const key of TypedObject.keys(obj)) {
258
260
  if (!exclude.some(r => typeof key === 'string' && (typeof r === 'string' ? r === key : r.test(key)))) {
@@ -1,7 +1,7 @@
1
1
  import { Class, ClassInstance } from '@travetto/runtime';
2
2
 
3
- import { DescribableConfig } from '../service/types';
4
- import { SchemaRegistry } from '../service/registry';
3
+ import { DescribableConfig } from '../service/types.ts';
4
+ import { SchemaRegistry } from '../service/registry.ts';
5
5
 
6
6
  function isClassInstance(o: Class | ClassInstance, property?: string): o is ClassInstance {
7
7
  return !!property;
@@ -1,8 +1,8 @@
1
1
  import { Any, ClassInstance } from '@travetto/runtime';
2
2
 
3
- import { SchemaRegistry } from '../service/registry';
4
- import { CommonRegExp } from '../validate/regexp';
5
- import { ClassList, FieldConfig } from '../service/types';
3
+ import { SchemaRegistry } from '../service/registry.ts';
4
+ import { CommonRegExp } from '../validate/regexp.ts';
5
+ import { ClassList, FieldConfig } from '../service/types.ts';
6
6
 
7
7
  type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(t: T, k: K, idx?: TypedPropertyDescriptor<Any> | number) => void);
8
8
 
@@ -40,16 +40,14 @@ export function Field(type: ClassList, ...config: Partial<FieldConfig>[]) {
40
40
  export function Alias(...aliases: string[]): PropType<unknown> { return prop({ aliases }); }
41
41
  /**
42
42
  * Mark a field as writeonly
43
- * @param active This determines if this field is readonly or not.
44
43
  * @augments `@travetto/schema:Field`
45
44
  */
46
- export function Writeonly(active = true): PropType<unknown> { return prop({ access: 'writeonly' }); }
45
+ export function Writeonly(): PropType<unknown> { return prop({ access: 'writeonly' }); }
47
46
  /**
48
47
  * Mark a field as readonly
49
- * @param active This determines if this field is readonly or not.
50
48
  * @augments `@travetto/schema:Field`
51
49
  */
52
- export function Readonly(active = true): PropType<unknown> { return prop({ access: 'readonly' }); }
50
+ export function Readonly(): PropType<unknown> { return prop({ access: 'readonly' }); }
53
51
  /**
54
52
  * Mark a field as sensitive
55
53
  * @param active This determines if this field is sensitive or not.
@@ -189,7 +187,7 @@ export function Currency(): PropType<number> { return Precision(13, 2); }
189
187
  * @augments `@travetto/schema:Ignore`
190
188
  */
191
189
  export function Ignore(): PropertyDecorator {
192
- return (target: Object, property: string | symbol) => { };
190
+ return () => { };
193
191
  }
194
192
 
195
193
  /**
@@ -1,14 +1,13 @@
1
1
  import { Any, castTo, Class, ClassInstance, DeepPartial } from '@travetto/runtime';
2
2
 
3
- import { BindUtil } from '../bind-util';
4
- import { SchemaRegistry } from '../service/registry';
5
- import { ClassConfig, ViewFieldsConfig } from '../service/types';
6
- import { MethodValidatorFn, ValidatorFn } from '../validate/types';
3
+ import { BindUtil } from '../bind-util.ts';
4
+ import { SchemaRegistry } from '../service/registry.ts';
5
+ import { ClassConfig, ViewFieldsConfig } from '../service/types.ts';
6
+ import { MethodValidatorFn, ValidatorFn } from '../validate/types.ts';
7
7
 
8
8
  /**
9
9
  * Register a class as a Schema
10
10
  *
11
- *
12
11
  * @augments `@travetto/schema:Schema`
13
12
  */
14
13
  export function Schema(cfg?: Partial<Pick<ClassConfig, 'subTypeName' | 'subTypeField' | 'baseType'>>) { // Auto is used during compilation
@@ -27,7 +26,7 @@ export function Schema(cfg?: Partial<Pick<ClassConfig, 'subTypeName' | 'subTypeF
27
26
  * @param fn The validator function
28
27
  */
29
28
  export const Validator = <T>(fn: ValidatorFn<T, string>) =>
30
- (target: Class<T>, k?: string): void => {
29
+ (target: Class<T>, _k?: string): void => {
31
30
  SchemaRegistry.getOrCreatePending(target).validators!.push(castTo(fn));
32
31
  };
33
32
 
@@ -37,7 +36,7 @@ export const Validator = <T>(fn: ValidatorFn<T, string>) =>
37
36
  * @param fn The validator function
38
37
  */
39
38
  export function MethodValidator<T extends (...args: Any[]) => Any>(fn: MethodValidatorFn<Parameters<T>>) {
40
- return (target: ClassInstance, k: string, prop: TypedPropertyDescriptor<T>): void => {
39
+ return (target: ClassInstance, k: string, _prop: TypedPropertyDescriptor<T>): void => {
41
40
  SchemaRegistry.registerPendingMethod(target.constructor, k).validators!.push(castTo(fn));
42
41
  };
43
42
  }
@@ -1,2 +1,31 @@
1
- export const AllViewSymbol: unique symbol = Symbol.for('@travetto/schema:all');
2
- export class UnknownType { }
1
+ import { DataUtil } from '../data.ts';
2
+
3
+ const InvalidSymbol = Symbol();
4
+
5
+ /**
6
+ * Point Implementation
7
+ */
8
+ export class PointImpl {
9
+
10
+ /**
11
+ * Validate we have an actual point
12
+ */
13
+ static validateSchema(input: unknown): 'type' | undefined {
14
+ const bound = this.bindSchema(input);
15
+ return bound !== InvalidSymbol && bound && !isNaN(bound[0]) && !isNaN(bound[1]) ? undefined : 'type';
16
+ }
17
+
18
+ /**
19
+ * Convert to tuple of two numbers
20
+ */
21
+ static bindSchema(input: unknown): [number, number] | typeof InvalidSymbol | undefined {
22
+ if (Array.isArray(input) && input.length === 2) {
23
+ const [a, b] = input.map(x => DataUtil.coerceType(x, Number, false));
24
+ return [a, b];
25
+ } else {
26
+ return InvalidSymbol;
27
+ }
28
+ }
29
+ }
30
+
31
+ Object.defineProperty(PointImpl, 'name', { value: 'Point' });
package/src/name.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ClassConfig } from './service/types';
1
+ import { ClassConfig } from './service/types.ts';
2
2
 
3
3
  const ID_RE = /(\d{1,100})Δ$/;
4
4
 
@@ -3,8 +3,7 @@ import { EventEmitter } from 'node:events';
3
3
  import { Class } from '@travetto/runtime';
4
4
  import { ChangeEvent } from '@travetto/registry';
5
5
 
6
- import { FieldConfig, ClassConfig } from './types';
7
- import { AllViewSymbol } from '../internal/types';
6
+ import { FieldConfig, ClassConfig } from './types.ts';
8
7
 
9
8
  const id = (c: Class | string): string => typeof c === 'string' ? c : c.Ⲑid;
10
9
 
@@ -111,8 +110,8 @@ class $SchemaChangeListener {
111
110
  */
112
111
  emitFieldChanges({ prev, curr }: ChangeEvent<ClassConfig>): void {
113
112
 
114
- const prevView = prev?.views[AllViewSymbol] || { fields: [], schema: {} };
115
- const currView = curr!.views[AllViewSymbol];
113
+ const prevView = prev?.totalView || { fields: [], schema: {} };
114
+ const currView = curr!.totalView;
116
115
 
117
116
  const prevFields = new Set(prevView.fields);
118
117
  const currFields = new Set(currView.fields);
@@ -1,10 +1,9 @@
1
1
  import { Class, AppError, describeFunction, castTo, classConstruct, asFull, castKey } from '@travetto/runtime';
2
2
  import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
3
3
 
4
- import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig, ViewConfig, SchemaMethodConfig } from './types';
5
- import { SchemaChangeListener } from './changes';
6
- import { AllViewSymbol } from '../internal/types';
7
- import { MethodValidatorFn } from '../validate/types';
4
+ import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig, ViewConfig, SchemaMethodConfig } from './types.ts';
5
+ import { SchemaChangeListener } from './changes.ts';
6
+ import { MethodValidatorFn } from '../validate/types.ts';
8
7
 
9
8
  const classToSubTypeName = (cls: Class): string => cls.name
10
9
  .replace(/([A-Z])([A-Z][a-z])/g, (all, l, r) => `${l}_${r.toLowerCase()}`)
@@ -73,7 +72,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
73
72
  ensureInstanceTypeField<T>(cls: Class, o: T): void {
74
73
  const schema = this.get(cls);
75
74
  const typeField = castKey<T>(schema.subTypeField);
76
- if (schema.subTypeName && typeField in schema.views[AllViewSymbol].schema && !o[typeField]) { // Do we have a type field defined
75
+ if (schema.subTypeName && typeField in schema.totalView.schema && !o[typeField]) { // Do we have a type field defined
77
76
  o[typeField] = castTo(schema.subTypeName); // Assign if missing
78
77
  }
79
78
  }
@@ -110,11 +109,11 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
110
109
 
111
110
  if (clsSchema.subTypeName || baseSchema.baseType) { // We have a sub type
112
111
  const type = castTo<string>(o[castKey<T>(baseSchema.subTypeField)]) ?? clsSchema.subTypeName ?? baseSchema.subTypeName;
113
- const ret = this.#subTypes.get(base)!.get(type)!;
114
- if (ret && !(classConstruct(ret) instanceof cls)) {
115
- throw new AppError(`Resolved class ${ret.name} is not assignable to ${cls.name}`);
112
+ const subType = this.#subTypes.get(base)!.get(type)!;
113
+ if (subType && !(classConstruct(subType) instanceof cls)) {
114
+ throw new AppError(`Resolved class ${subType.name} is not assignable to ${cls.name}`);
116
115
  }
117
- return ret;
116
+ return subType;
118
117
  } else {
119
118
  return cls;
120
119
  }
@@ -169,7 +168,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
169
168
  SchemaChangeListener.trackSchemaDependency(curr, cls, path, this.get(cls));
170
169
 
171
170
  // Read children
172
- const view = config.views[AllViewSymbol];
171
+ const view = config.totalView;
173
172
  for (const k of view.fields) {
174
173
  if (this.has(view.schema[k].type) && view.schema[k].type !== cls) {
175
174
  this.trackSchemaDependencies(cls, view.schema[k].type, [...path, view.schema[k]]);
@@ -185,12 +184,11 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
185
184
  baseType: describeFunction(cls)?.abstract,
186
185
  metadata: {},
187
186
  methods: {},
188
- views: {
189
- [AllViewSymbol]: {
190
- schema: {},
191
- fields: []
192
- }
193
- }
187
+ totalView: {
188
+ schema: {},
189
+ fields: [],
190
+ },
191
+ views: {}
194
192
  };
195
193
  }
196
194
 
@@ -199,16 +197,17 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
199
197
  * @param cls The class to retrieve the schema for
200
198
  * @param view The view name
201
199
  */
202
- getViewSchema<T>(cls: Class<T>, view?: string | typeof AllViewSymbol): ViewConfig {
203
- view = view ?? AllViewSymbol;
204
-
200
+ getViewSchema<T>(cls: Class<T>, view?: string): ViewConfig {
205
201
  const schema = this.get(cls)!;
206
202
  if (!schema) {
207
203
  throw new Error(`Unknown schema class ${cls.name}`);
208
204
  }
209
- const res = schema.views[view];
210
- if (!res) {
211
- throw new Error(`Unknown view ${view.toString()} for ${cls.name}`);
205
+ let res = schema.totalView;
206
+ if (view) {
207
+ res = schema.views[view];
208
+ if (!res) {
209
+ throw new Error(`Unknown view ${view.toString()} for ${cls.name}`);
210
+ }
212
211
  }
213
212
  return res;
214
213
  }
@@ -219,7 +218,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
219
218
  * @param method
220
219
  */
221
220
  getMethodSchema<T>(cls: Class<T>, method: string): FieldConfig[] {
222
- return (this.get(cls)?.methods?.[method] ?? {}).fields?.filter(x => !!x).sort((a, b) => a.index! - b.index!) ?? [];
221
+ return (this.get(cls)?.methods?.[method] ?? {}).fields?.filter(x => !!x).toSorted((a, b) => a.index! - b.index!) ?? [];
223
222
  }
224
223
 
225
224
  /**
@@ -275,7 +274,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
275
274
  config.specifiers = [...params[idx]?.specifiers ?? [], ...config.specifiers];
276
275
  }
277
276
  if (config.enum?.values) {
278
- config.enum.values = config.enum.values.slice().sort();
277
+ config.enum.values = config.enum.values.toSorted();
279
278
  }
280
279
 
281
280
  params[idx] = {
@@ -296,24 +295,28 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
296
295
  * @param config The config to register
297
296
  */
298
297
  registerPendingFieldFacet(target: Class, prop: string, config: Partial<FieldConfig>): Class {
299
- const allViewConf = this.getOrCreatePending(target).views![AllViewSymbol];
298
+ if (prop === '__proto__' || prop === 'constructor' || prop === 'prototype') {
299
+ throw new AppError('Invalid property name');
300
+ }
301
+
302
+ const totalViewConf = this.getOrCreatePending(target).totalView!;
300
303
 
301
- if (!allViewConf.schema[prop]) {
302
- allViewConf.fields.push(prop);
304
+ if (!totalViewConf.schema[prop]) {
305
+ totalViewConf.fields.push(prop);
303
306
  // Partial config while building
304
- allViewConf.schema[prop] = asFull<FieldConfig>({});
307
+ totalViewConf.schema[prop] = asFull<FieldConfig>({});
305
308
  }
306
309
  if (config.aliases) {
307
- config.aliases = [...allViewConf.schema[prop].aliases ?? [], ...config.aliases];
310
+ config.aliases = [...totalViewConf.schema[prop].aliases ?? [], ...config.aliases];
308
311
  }
309
312
  if (config.specifiers) {
310
- config.specifiers = [...allViewConf.schema[prop].specifiers ?? [], ...config.specifiers];
313
+ config.specifiers = [...totalViewConf.schema[prop].specifiers ?? [], ...config.specifiers];
311
314
  }
312
315
  if (config.enum?.values) {
313
- config.enum.values = config.enum.values.slice().sort();
316
+ config.enum.values = config.enum.values.toSorted();
314
317
  }
315
318
 
316
- Object.assign(allViewConf.schema[prop], config);
319
+ Object.assign(totalViewConf.schema[prop], config);
317
320
 
318
321
  return target;
319
322
  }
@@ -359,9 +362,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
359
362
  * @param src Source config
360
363
  */
361
364
  mergeConfigs(dest: ClassConfig, src: Partial<ClassConfig>, inherited = false): ClassConfig {
362
- dest.views[AllViewSymbol] = {
363
- schema: { ...dest.views[AllViewSymbol].schema, ...src.views?.[AllViewSymbol].schema },
364
- fields: [...dest.views[AllViewSymbol].fields, ...src.views?.[AllViewSymbol].fields ?? []]
365
+ dest.totalView = {
366
+ schema: { ...dest.totalView.schema, ...src.totalView?.schema },
367
+ fields: [...dest.totalView.fields, ...src.totalView?.fields ?? []]
365
368
  };
366
369
  if (!inherited) {
367
370
  dest.baseType = src.baseType ?? dest.baseType;
@@ -381,20 +384,20 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
381
384
  * @param conf The class config
382
385
  */
383
386
  finalizeViews<T>(target: Class<T>, conf: ClassConfig): ClassConfig {
384
- const allViewConf = conf.views![AllViewSymbol];
387
+ const totalViewConf = conf.totalView;
385
388
  const pending = this.#pendingViews.get(target) ?? new Map<string, ViewFieldsConfig<string>>();
386
389
  this.#pendingViews.delete(target);
387
390
 
388
391
  for (const [view, fields] of pending.entries()) {
389
392
  const withoutSet = 'without' in fields ? new Set<string>(fields.without) : undefined;
390
393
  const fieldList = withoutSet ?
391
- allViewConf.fields.filter(x => !withoutSet.has(x)) :
394
+ totalViewConf.fields.filter(x => !withoutSet.has(x)) :
392
395
  ('with' in fields ? fields.with : []);
393
396
 
394
397
  conf.views![view] = {
395
398
  fields: fieldList,
396
399
  schema: fieldList.reduce<SchemaConfig>((acc, v) => {
397
- acc[v] = allViewConf.schema[v];
400
+ acc[v] = totalViewConf.schema[v];
398
401
  return acc;
399
402
  }, {})
400
403
  };
@@ -427,9 +430,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
427
430
  // Write views out
428
431
  config = this.finalizeViews(cls, config);
429
432
 
430
- if (config.subTypeName && config.subTypeField in config.views[AllViewSymbol].schema) {
431
- const field = config.views[AllViewSymbol].schema[config.subTypeField];
432
- config.views[AllViewSymbol].schema[config.subTypeField] = {
433
+ if (config.subTypeName && config.subTypeField in config.totalView.schema) {
434
+ const field = config.totalView.schema[config.subTypeField];
435
+ config.totalView.schema[config.subTypeField] = {
433
436
  ...field,
434
437
  enum: {
435
438
  values: [config.subTypeName],
@@ -1,7 +1,6 @@
1
1
  import { Any, Class, Primitive } from '@travetto/runtime';
2
2
 
3
- import { AllViewSymbol } from '../internal/types';
4
- import { MethodValidatorFn, ValidatorFn } from '../validate/types';
3
+ import { MethodValidatorFn, ValidatorFn } from '../validate/types.ts';
5
4
 
6
5
  export type ClassList = Class | [Class];
7
6
 
@@ -69,7 +68,11 @@ export interface ClassConfig extends DescribableConfig {
69
68
  /**
70
69
  * List of all views
71
70
  */
72
- views: Record<string, ViewConfig> & { [AllViewSymbol]: ViewConfig };
71
+ views: Record<string, ViewConfig>;
72
+ /**
73
+ * Composite of all views
74
+ */
75
+ totalView: ViewConfig;
73
76
  /**
74
77
  * Global validators
75
78
  */
package/src/types.ts ADDED
@@ -0,0 +1,8 @@
1
+ export class UnknownType { }
2
+
3
+ /**
4
+ * Geometric Point as [number,number] with validation and binding support
5
+ *
6
+ * @concrete ./internal/types.ts#PointImpl
7
+ */
8
+ export type Point = [number, number];
@@ -1,5 +1,5 @@
1
1
  import { Class, AppError } from '@travetto/runtime';
2
- import { ValidationError } from './types';
2
+ import { ValidationError } from './types.ts';
3
3
 
4
4
  /**
5
5
  * Validation results error.
@@ -1,5 +1,5 @@
1
1
  import { TypedObject } from '@travetto/runtime';
2
- import { Messages } from './messages';
2
+ import { Messages } from './messages.ts';
3
3
 
4
4
  /**
5
5
  * List of common regular expressions for fields
@@ -3,6 +3,7 @@ export type ValidationKind = ValidationKindCore | string;
3
3
 
4
4
  /**
5
5
  * Individual Validation Error
6
+ * @concrete
6
7
  */
7
8
  export interface ValidationError {
8
9
  /**
@@ -1,12 +1,12 @@
1
1
  import { castKey, castTo, Class, ClassInstance, TypedObject } from '@travetto/runtime';
2
2
 
3
- import { FieldConfig, SchemaConfig } from '../service/types';
4
- import { SchemaRegistry } from '../service/registry';
5
- import { ValidationError, ValidationKindCore, ValidationResult } from './types';
6
- import { Messages } from './messages';
7
- import { isValidationError, TypeMismatchError, ValidationResultError } from './error';
8
- import { DataUtil } from '../data';
9
- import { CommonRegExpToName } from './regexp';
3
+ import { FieldConfig, SchemaConfig } from '../service/types.ts';
4
+ import { SchemaRegistry } from '../service/registry.ts';
5
+ import { ValidationError, ValidationKindCore, ValidationResult } from './types.ts';
6
+ import { Messages } from './messages.ts';
7
+ import { isValidationError, TypeMismatchError, ValidationResultError } from './error.ts';
8
+ import { DataUtil } from '../data.ts';
9
+ import { CommonRegExpToName } from './regexp.ts';
10
10
 
11
11
  /**
12
12
  * Get the schema config for Class/Schema config, including support for polymorphism
@@ -184,22 +184,22 @@ export class SchemaValidator {
184
184
  */
185
185
  static #prepareErrors(path: string, results: ValidationResult[]): ValidationError[] {
186
186
  const out: ValidationError[] = [];
187
- for (const res of results) {
187
+ for (const result of results) {
188
188
  const err: ValidationError = {
189
- ...res,
190
- kind: res.kind,
191
- value: res.value,
189
+ ...result,
190
+ kind: result.kind,
191
+ value: result.value,
192
192
  message: '',
193
- re: CommonRegExpToName.get(res.re!) ?? res.re?.source ?? '',
193
+ re: CommonRegExpToName.get(result.re!) ?? result.re?.source ?? '',
194
194
  path,
195
- type: (typeof res.type === 'function' ? res.type.name : res.type)
195
+ type: (typeof result.type === 'function' ? result.type.name : result.type)
196
196
  };
197
197
 
198
198
  if (!err.re) {
199
199
  delete err.re;
200
200
  }
201
201
 
202
- const msg = res.message ?? (
202
+ const msg = result.message ?? (
203
203
  Messages.get(err.re ?? '') ??
204
204
  Messages.get(err.kind) ??
205
205
  Messages.get('default')!
@@ -4,13 +4,13 @@ import {
4
4
  DecoratorUtil, DocUtil, ParamDocumentation, TransformerState, transformCast,
5
5
  } from '@travetto/transformer';
6
6
 
7
- const SCHEMA_MOD = '@travetto/schema/src/decorator/schema';
8
- const FIELD_MOD = '@travetto/schema/src/decorator/field';
9
- const COMMON_MOD = '@travetto/schema/src/decorator/common';
10
- const TYPES_FILE = '@travetto/schema/src/internal/types';
11
-
12
7
  export class SchemaTransformUtil {
13
8
 
9
+ static SCHEMA_IMPORT = '@travetto/schema/src/decorator/schema.ts';
10
+ static FIELD_IMPORT = '@travetto/schema/src/decorator/field.ts';
11
+ static COMMON_IMPORT = '@travetto/schema/src/decorator/common.ts';
12
+ static TYPES_IMPORT = '@travetto/schema/src/types.ts';
13
+
14
14
  /**
15
15
  * Produce concrete type given transformer type
16
16
  */
@@ -29,7 +29,7 @@ export class SchemaTransformUtil {
29
29
  break;
30
30
  }
31
31
  case 'unknown': {
32
- const imp = state.importFile(TYPES_FILE);
32
+ const imp = state.importFile(this.TYPES_IMPORT);
33
33
  return state.createAccess(imp.ident, 'UnknownType');
34
34
  }
35
35
  case 'shape': {
@@ -40,8 +40,8 @@ export class SchemaTransformUtil {
40
40
  if (!existing) {
41
41
  const cls = state.factory.createClassDeclaration(
42
42
  [
43
- state.createDecorator(SCHEMA_MOD, 'Schema'),
44
- state.createDecorator(COMMON_MOD, 'Describe',
43
+ state.createDecorator(this.SCHEMA_IMPORT, 'Schema'),
44
+ state.createDecorator(this.COMMON_IMPORT, 'Describe',
45
45
  state.fromLiteral({
46
46
  title: type.name,
47
47
  description: type.comment
@@ -163,7 +163,7 @@ export class SchemaTransformUtil {
163
163
 
164
164
  const params: ts.Expression[] = [];
165
165
 
166
- const existing = state.findDecorator('@travetto/schema', node, 'Field', FIELD_MOD);
166
+ const existing = state.findDecorator('@travetto/schema', node, 'Field', this.FIELD_IMPORT);
167
167
  if (!existing) {
168
168
  const resolved = this.toConcreteType(state, typeExpr, node, config.root);
169
169
  params.push(resolved);
@@ -183,31 +183,31 @@ export class SchemaTransformUtil {
183
183
 
184
184
  const newModifiers = [
185
185
  ...(node.modifiers ?? []).filter(x => x !== existing),
186
- state.createDecorator(FIELD_MOD, 'Field', ...params)
186
+ state.createDecorator(this.FIELD_IMPORT, 'Field', ...params)
187
187
  ];
188
188
 
189
- let ret: unknown;
189
+ let result: unknown;
190
190
  if (ts.isPropertyDeclaration(node)) {
191
191
  const comments = DocUtil.describeDocs(node);
192
192
  if (comments.description) {
193
- newModifiers.push(state.createDecorator(COMMON_MOD, 'Describe', state.fromLiteral({
193
+ newModifiers.push(state.createDecorator(this.COMMON_IMPORT, 'Describe', state.fromLiteral({
194
194
  description: comments.description
195
195
  })));
196
196
  }
197
197
 
198
- ret = state.factory.updatePropertyDeclaration(node,
198
+ result = state.factory.updatePropertyDeclaration(node,
199
199
  newModifiers, node.name, node.questionToken, node.type, node.initializer);
200
200
  } else if (ts.isParameter(node)) {
201
- ret = state.factory.updateParameterDeclaration(node,
201
+ result = state.factory.updateParameterDeclaration(node,
202
202
  newModifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer);
203
203
  } else if (ts.isGetAccessorDeclaration(node)) {
204
- ret = state.factory.updateGetAccessorDeclaration(node,
204
+ result = state.factory.updateGetAccessorDeclaration(node,
205
205
  newModifiers, node.name, node.parameters, node.type, node.body);
206
206
  } else {
207
- ret = state.factory.updateSetAccessorDeclaration(node,
207
+ result = state.factory.updateSetAccessorDeclaration(node,
208
208
  newModifiers, node.name, node.parameters, node.body);
209
209
  }
210
- return transformCast(ret);
210
+ return transformCast(result);
211
211
  }
212
212
 
213
213
  /**
@@ -1,22 +1,19 @@
1
1
  import ts from 'typescript';
2
2
 
3
3
  import {
4
- TransformerState, OnProperty, OnClass, AfterClass, DecoratorMeta, DocUtil, DeclarationUtil, OnGetter, OnSetter
4
+ TransformerState, OnProperty, OnClass, AfterClass, DocUtil, DeclarationUtil, OnGetter, OnSetter
5
5
  } from '@travetto/transformer';
6
6
 
7
- import { SchemaTransformUtil } from './transformer/util';
7
+ import { SchemaTransformUtil } from './transformer/util.ts';
8
8
 
9
- const inSchema = Symbol.for('@travetto/schema:schema');
10
- const accessors = Symbol.for('@travetto/schema:accessors');
9
+ const InSchemaSymbol = Symbol();
10
+ const AccessorsSymbol = Symbol();
11
11
 
12
12
  interface AutoState {
13
- [inSchema]?: boolean;
14
- [accessors]?: Set<string>;
13
+ [InSchemaSymbol]?: boolean;
14
+ [AccessorsSymbol]?: Set<string>;
15
15
  }
16
16
 
17
- const SCHEMA_IMPORT = '@travetto/schema/src/decorator/schema';
18
- const COMMON_IMPORT = '@travetto/schema/src/decorator/common';
19
-
20
17
  /**
21
18
  * Processes `@Schema` to register class as a valid Schema
22
19
  */
@@ -26,9 +23,9 @@ export class SchemaTransformer {
26
23
  * Track schema on start
27
24
  */
28
25
  @OnClass('Schema')
29
- static startSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration, dec?: DecoratorMeta): ts.ClassDeclaration {
30
- state[inSchema] = true;
31
- state[accessors] = new Set();
26
+ static startSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
27
+ state[InSchemaSymbol] = true;
28
+ state[AccessorsSymbol] = new Set();
32
29
  return node;
33
30
  }
34
31
 
@@ -41,18 +38,18 @@ export class SchemaTransformer {
41
38
 
42
39
  const comments = DocUtil.describeDocs(node);
43
40
 
44
- if (!state.findDecorator(this, node, 'Schema', SCHEMA_IMPORT)) {
45
- modifiers.unshift(state.createDecorator(SCHEMA_IMPORT, 'Schema'));
41
+ if (!state.findDecorator(this, node, 'Schema', SchemaTransformUtil.SCHEMA_IMPORT)) {
42
+ modifiers.unshift(state.createDecorator(SchemaTransformUtil.SCHEMA_IMPORT, 'Schema'));
46
43
  }
47
44
 
48
45
  if (comments.description) {
49
- modifiers.push(state.createDecorator(COMMON_IMPORT, 'Describe', state.fromLiteral({
46
+ modifiers.push(state.createDecorator(SchemaTransformUtil.COMMON_IMPORT, 'Describe', state.fromLiteral({
50
47
  title: comments.description
51
48
  })));
52
49
  }
53
50
 
54
- delete state[inSchema];
55
- delete state[accessors];
51
+ delete state[InSchemaSymbol];
52
+ delete state[AccessorsSymbol];
56
53
 
57
54
  return state.factory.updateClassDeclaration(
58
55
  node,
@@ -70,7 +67,7 @@ export class SchemaTransformer {
70
67
  @OnProperty()
71
68
  static processSchemaField(state: TransformerState & AutoState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
72
69
  const ignore = state.findDecorator(this, node, 'Ignore');
73
- return state[inSchema] && !ignore && DeclarationUtil.isPublic(node) ?
70
+ return state[InSchemaSymbol] && !ignore && DeclarationUtil.isPublic(node) ?
74
71
  SchemaTransformUtil.computeField(state, node) : node;
75
72
  }
76
73
 
@@ -80,8 +77,8 @@ export class SchemaTransformer {
80
77
  @OnGetter()
81
78
  static processSchemaGetter(state: TransformerState & AutoState, node: ts.GetAccessorDeclaration): ts.GetAccessorDeclaration {
82
79
  const ignore = state.findDecorator(this, node, 'Ignore');
83
- if (state[inSchema] && !ignore && DeclarationUtil.isPublic(node) && !state[accessors]?.has(node.name.getText())) {
84
- state[accessors]?.add(node.name.getText());
80
+ if (state[InSchemaSymbol] && !ignore && DeclarationUtil.isPublic(node) && !state[AccessorsSymbol]?.has(node.name.getText())) {
81
+ state[AccessorsSymbol]?.add(node.name.getText());
85
82
  return SchemaTransformUtil.computeField(state, node);
86
83
  }
87
84
  return node;
@@ -93,8 +90,8 @@ export class SchemaTransformer {
93
90
  @OnSetter()
94
91
  static processSchemaSetter(state: TransformerState & AutoState, node: ts.SetAccessorDeclaration): ts.SetAccessorDeclaration {
95
92
  const ignore = state.findDecorator(this, node, 'Ignore');
96
- if (state[inSchema] && !ignore && DeclarationUtil.isPublic(node) && !state[accessors]?.has(node.name.getText())) {
97
- state[accessors]?.add(node.name.getText());
93
+ if (state[InSchemaSymbol] && !ignore && DeclarationUtil.isPublic(node) && !state[AccessorsSymbol]?.has(node.name.getText())) {
94
+ state[AccessorsSymbol]?.add(node.name.getText());
98
95
  return SchemaTransformUtil.computeField(state, node);
99
96
  }
100
97
  return node;