@travetto/schema 6.0.1 → 7.0.0-rc.0

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