@travetto/schema 8.0.0-alpha.20 → 8.0.0-alpha.22

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,7 +13,7 @@ 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"), [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative support for creating Web Applications"), [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.
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 support for creating Web Applications"), [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
 
@@ -63,29 +63,29 @@ User:
63
63
  ```
64
64
 
65
65
  ### Fields
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#L24) defines a field that will be serialized.
68
- * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L55) defines a that field should be required
69
- * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L64) defines the allowable values that a field can have
70
- * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L90) 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#L99) enforces min length of a string
72
- * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L110) enforces max length of a string
73
- * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L99) enforces min value for a date or a number
74
- * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L110) enforces max value for a date or a number
75
- * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L140) ensures string field matches basic email regex
76
- * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L148) ensures string field matches basic telephone regex
77
- * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L156) ensures string field matches basic url regex
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#L26) defines a field that will be serialized.
68
+ * [@Required](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L67) defines a that field should be required
69
+ * [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L78) defines the allowable values that a field can have
70
+ * [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L108) 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#L119) enforces min length of a string
72
+ * [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L130) enforces max length of a string
73
+ * [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L119) enforces min value for a date or a number
74
+ * [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L130) enforces max value for a date or a number
75
+ * [@Email](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L162) ensures string field matches basic email regex
76
+ * [@Telephone](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L172) ensures string field matches basic telephone regex
77
+ * [@Url](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L182) ensures string field matches basic url regex
78
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#L172) ensures number passed in is only a whole number
80
- * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L179) ensures number passed in allows fractional values
81
- * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L193) provides support for standard currency
82
- * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L74) 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#L81) same as text, but expects longer form content
84
- * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L40) 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#L33) 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#L48) 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#L201) 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#L208) allows for promoting a given field as the owner of the sub type discriminator.
79
+ * [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L202) ensures number passed in is only a whole number
80
+ * [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L211) ensures number passed in allows fractional values
81
+ * [@Currency](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L229) provides support for standard currency
82
+ * [@Text](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/input.ts#L88) 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#L97) same as text, but expects longer form content
84
+ * [@Readonly](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L44) 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#L35) 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#L54) 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#L239) 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#L248) 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
 
@@ -96,7 +96,7 @@ Just like the class, all fields can be defined with
96
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
- 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.
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.
100
100
 
101
101
  **Code: Sample Parameter Usage**
102
102
  ```typescript
@@ -119,7 +119,7 @@ Binding is a very simple operation, as it takes in a class registered as as [@Sc
119
119
 
120
120
  **Code: Sub Schemas via Address**
121
121
  ```typescript
122
- import { Schema, Integer } from '@travetto/schema';
122
+ import { Integer, Schema } from '@travetto/schema';
123
123
 
124
124
  @Schema()
125
125
  export class Address {
@@ -173,7 +173,7 @@ Validation is very similar to binding, but instead of attempting to assign value
173
173
 
174
174
  **Code: Sub Schemas via Address**
175
175
  ```typescript
176
- import { Schema, Integer } from '@travetto/schema';
176
+ import { Integer, Schema } from '@travetto/schema';
177
177
 
178
178
  @Schema()
179
179
  export class Address {
@@ -198,7 +198,6 @@ import { SchemaValidator } from '@travetto/schema';
198
198
  import { Person } from './person.ts';
199
199
 
200
200
  export async function validate(): Promise<void> {
201
-
202
201
  const person = Person.from({
203
202
  name: 'Test',
204
203
  age: 'abc',
@@ -243,11 +242,11 @@ Validation Failed {
243
242
  ```
244
243
 
245
244
  ### Custom Validators
246
- Within the schema framework, it is possible to add custom validators class level. This allows for more flexibility when dealing with specific situations (e.g. password requirements or ensuring two fields match)
245
+ Within the schema framework, it is possible to add custom validators class level. This allows for more flexibility when dealing with specific situations (e.g. password requirements or ensuring two fields match)
247
246
 
248
247
  **Code: Password Validator**
249
248
  ```typescript
250
- import { Schema, Validator, type ValidationError } from '@travetto/schema';
249
+ import { Schema, type ValidationError, Validator } from '@travetto/schema';
251
250
 
252
251
  const passwordValidator = (user: User): ValidationError | undefined => {
253
252
  const password = user.password;
@@ -270,7 +269,7 @@ class User {
270
269
  }
271
270
  ```
272
271
 
273
- When the validator is executed, it has access to the entire object, and you can check any of the values. The validator expects an object of a specific structure if you are looking to indicate an error has occurred.
272
+ When the validator is executed, it has access to the entire object, and you can check any of the values. The validator expects an object of a specific structure if you are looking to indicate an error has occurred.
274
273
 
275
274
  **Code: Validation Error Structure**
276
275
  ```typescript
@@ -316,7 +315,7 @@ When working with the schema, the basic types are easily understood, but some of
316
315
  To that end, the module supports two concepts:
317
316
 
318
317
  ### Type Adapters
319
- 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.
318
+ 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.
320
319
 
321
320
  **Code: Point Contract**
322
321
  ```typescript
@@ -352,17 +351,17 @@ function bindPoint(input: unknown): [number, number] | typeof InvalidSymbol | un
352
351
  */
353
352
  function validatePoint(input: unknown): 'type' | undefined {
354
353
  const bound = bindPoint(input);
355
- return bound !== InvalidSymbol && bound && !isNaN(bound[0]) && !isNaN(bound[1]) ? undefined : 'type';
354
+ return bound !== InvalidSymbol && bound && !Number.isNaN(bound[0]) && !Number.isNaN(bound[1]) ? undefined : 'type';
356
355
  }
357
356
 
358
357
  /**
359
358
  * Point Contract
360
359
  */
361
- export class PointContract { }
360
+ export class PointContract {}
362
361
 
363
362
  SchemaTypeUtil.setSchemaTypeConfig(PointContract, {
364
363
  validate: validatePoint,
365
- bind: bindPoint,
364
+ bind: bindPoint
366
365
  });
367
366
  Object.defineProperty(PointContract, 'name', { value: 'Point' });
368
367
  ```
@@ -373,7 +372,7 @@ What you can see here is that the `Point` type is now backed by a class that sup
373
372
 
374
373
  **Code: Simple Custom Type Usage**
375
374
  ```typescript
376
- import { Schema, type Point } from '@travetto/schema';
375
+ import { type Point, Schema } from '@travetto/schema';
377
376
 
378
377
  @Schema()
379
378
  export class LocationAware {
@@ -382,7 +381,7 @@ export class LocationAware {
382
381
  }
383
382
  ```
384
383
 
385
- All that happens now, is the type is exported, and the class above is able to properly handle point as an `[x, y]` tuple. All standard binding and validation patterns are supported, and type enforcement will work as expected.
384
+ All that happens now, is the type is exported, and the class above is able to properly handle point as an `[x, y]` tuple. All standard binding and validation patterns are supported, and type enforcement will work as expected.
386
385
 
387
386
  **Terminal: Custom Type Validation**
388
387
  ```bash
@@ -408,8 +407,8 @@ Validation Failed {
408
407
  ```
409
408
 
410
409
  ## Data Utilities
411
- Data utilities for binding values, and type conversion. Currently [DataUtil](https://github.com/travetto/travetto/tree/main/module/schema/src/data.ts#L11) includes:
412
- * `deepAssign(a, b, mode ?)` which allows for deep assignment of `b` onto `a`, the `mode` determines how aggressive the assignment is, and how flexible it is. `mode` can have any of the following values:
410
+ Data utilities for binding values, and type conversion. Currently [DataUtil](https://github.com/travetto/travetto/tree/main/module/schema/src/data.ts#L12) includes:
411
+ * `deepAssign(a, b, mode ?)` which allows for deep assignment of `b` onto `a`, the `mode` determines how aggressive the assignment is, and how flexible it is. `mode` can have any of the following values:
413
412
  * `loose`, which is the default is the most lenient. It will not error out, and overwrites will always happen
414
413
  * `coerce`, will attempt to force values from `b` to fit the types of `a`, and if it can't it will error out
415
414
  * `strict`, will error out if the types do not match
package/__index__.ts CHANGED
@@ -1,19 +1,20 @@
1
- import type { } from './src/global.d.ts';
1
+ import type {} from './src/global.d.ts';
2
+
3
+ export * from './src/bind-util.ts';
4
+ export * from './src/data.ts';
5
+ export * from './src/decorator/common.ts';
2
6
  export * from './src/decorator/field.ts';
3
7
  export * from './src/decorator/input.ts';
4
- export * from './src/decorator/schema.ts';
5
8
  export * from './src/decorator/method.ts';
6
- export * from './src/decorator/common.ts';
9
+ export * from './src/decorator/schema.ts';
10
+ export * from './src/name.ts';
11
+ export * from './src/service/registry-adapter.ts';
12
+ export * from './src/service/registry-index.ts';
7
13
  export * from './src/service/types.ts';
14
+ export * from './src/type-config.ts';
15
+ export * from './src/types.ts';
16
+ export * from './src/validate/error.ts';
8
17
  export * from './src/validate/messages.ts';
9
18
  export * from './src/validate/regex.ts';
10
- export * from './src/validate/validator.ts';
11
- export * from './src/validate/error.ts';
12
19
  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';
17
- export * from './src/type-config.ts';
18
- export * from './src/service/registry-index.ts';
19
- export * from './src/service/registry-adapter.ts';
20
+ export * from './src/validate/validator.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "version": "8.0.0-alpha.20",
3
+ "version": "8.0.0-alpha.22",
4
4
  "type": "module",
5
5
  "description": "Data type registry for runtime validation, reflection and binding.",
6
6
  "keywords": [
@@ -28,10 +28,10 @@
28
28
  "directory": "module/schema"
29
29
  },
30
30
  "dependencies": {
31
- "@travetto/registry": "^8.0.0-alpha.19"
31
+ "@travetto/registry": "^8.0.0-alpha.20"
32
32
  },
33
33
  "peerDependencies": {
34
- "@travetto/transformer": "^8.0.0-alpha.13"
34
+ "@travetto/transformer": "^8.0.0-alpha.14"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "@travetto/transformer": {
package/src/bind-util.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { castTo, type Class, classConstruct, asFull, TypedObject, castKey } from '@travetto/runtime';
1
+ import { asFull, type Class, castKey, castTo, classConstruct, TypedObject } from '@travetto/runtime';
2
2
 
3
3
  import { DataUtil } from './data.ts';
4
- import type { SchemaInputConfig, SchemaParameterConfig, SchemaFieldMap } from './service/types.ts';
5
4
  import { SchemaRegistryIndex } from './service/registry-index.ts';
5
+ import type { SchemaFieldMap, SchemaInputConfig, SchemaParameterConfig } from './service/types.ts';
6
6
  import { SchemaTypeUtil } from './type-config.ts';
7
7
 
8
8
  type BindConfig = {
@@ -19,11 +19,10 @@ function isInstance<T>(value: unknown): value is T {
19
19
  * Utilities for binding objects to schemas
20
20
  */
21
21
  export class BindUtil {
22
-
23
22
  /**
24
23
  * Utility to make a property accessor enumerable at runtime
25
24
  */
26
- static registerAccessor(instance: any, property: string): void {
25
+ static registerAccessor(instance: unknown, property: string): void {
27
26
  Object.defineProperty(instance, property, {
28
27
  ...Object.getOwnPropertyDescriptor(Object.getPrototypeOf(instance), property),
29
28
  enumerable: true
@@ -43,9 +42,11 @@ export class BindUtil {
43
42
  value = DataUtil.coerceType(value, config.type, false);
44
43
 
45
44
  if (config.type === Number && config.precision && typeof value === 'number') {
46
- if (config.precision[1]) { // Supports decimal
45
+ if (config.precision[1]) {
46
+ // Supports decimal
47
47
  value = +value.toFixed(config.precision[1]);
48
- } else { // 0 digits
48
+ } else {
49
+ // 0 digits
49
50
  value = Math.trunc(value);
50
51
  }
51
52
  }
@@ -72,8 +73,9 @@ export class BindUtil {
72
73
  const part = parts.shift()!;
73
74
  const partArrayIndex = part.indexOf('[') > 0;
74
75
  const name = part.split(/[^A-Za-z_0-9]/)[0];
76
+ // biome-ignore lint/complexity/noUselessEscapeInRegex: Consistent escaping for open and close
75
77
  const idx = partArrayIndex ? part.split(/[\[\]]/)[1] : '';
76
- const key = partArrayIndex ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined)) : undefined;
78
+ const key = partArrayIndex ? (/^\d+$/.test(idx) ? parseInt(idx, 10) : idx.trim() || undefined) : undefined;
77
79
 
78
80
  if (!(name in sub)) {
79
81
  sub[name] = typeof key === 'number' ? [] : {};
@@ -96,10 +98,11 @@ export class BindUtil {
96
98
  }
97
99
  } else {
98
100
  const name = last.split(/[^A-Za-z_0-9]/)[0];
101
+ // biome-ignore lint/complexity/noUselessEscapeInRegex: Consistent escaping for open and close
99
102
  const idx = last.split(/[\[\]]/)[1];
100
103
 
101
- let key = (/^\d+$/.test(idx) ? parseInt(idx, 10) : (idx.trim() || undefined));
102
- sub[name] ??= (typeof key === 'string') ? {} : [];
104
+ let key = /^\d+$/.test(idx) ? parseInt(idx, 10) : idx.trim() || undefined;
105
+ sub[name] ??= typeof key === 'string' ? {} : [];
103
106
 
104
107
  const arrSub: Record<string, unknown> & { length: number } = castTo(sub[name]);
105
108
  if (key === undefined) {
@@ -125,8 +128,7 @@ export class BindUtil {
125
128
  for (const [key, value] of Object.entries(data)) {
126
129
  const pre = `${prefix}${key}`;
127
130
  if (DataUtil.isPlainObject(value)) {
128
- Object.assign(out, this.flattenPaths(value, `${pre}.`)
129
- );
131
+ Object.assign(out, this.flattenPaths(value, `${pre}.`));
130
132
  } else if (Array.isArray(value)) {
131
133
  for (let i = 0; i < value.length; i++) {
132
134
  const element = value[i];
@@ -162,9 +164,10 @@ export class BindUtil {
162
164
  const resolvedCls = SchemaRegistryIndex.resolveInstanceType<T>(cls, asFull<T>(data));
163
165
  const instance = classConstruct<T & { type?: string }>(resolvedCls);
164
166
 
165
- for (const key of TypedObject.keys(instance)) { // Do not retain undefined fields
167
+ for (const key of TypedObject.keys(instance)) {
168
+ // Do not retain undefined fields
166
169
  const descriptor = Object.getOwnPropertyDescriptor(instance, key);
167
- if (descriptor?.get) {
170
+ if (descriptor?.writable === false || descriptor?.get) {
168
171
  continue;
169
172
  }
170
173
  if (instance[key] === undefined) {
@@ -189,7 +192,7 @@ export class BindUtil {
189
192
  const view = config.view; // Does not convey
190
193
  delete config.view;
191
194
 
192
- if (!!data && isInstance<T>(data)) {
195
+ if (data && isInstance<T>(data)) {
193
196
  const adapter = SchemaRegistryIndex.get(cls);
194
197
  const schemaConfig = adapter.get();
195
198
 
@@ -208,14 +211,14 @@ export class BindUtil {
208
211
  }
209
212
 
210
213
  for (const [schemaFieldName, field] of Object.entries(schema)) {
211
- let inboundField: string | undefined = undefined;
214
+ let inboundField: string | undefined;
212
215
  if (field.access === 'readonly' || config.filterInput?.(field) === false) {
213
216
  continue; // Skip trying to write readonly fields
214
217
  }
215
218
  if (schemaFieldName in data) {
216
219
  inboundField = schemaFieldName;
217
220
  } else if (field.aliases) {
218
- for (const aliasedField of (field.aliases ?? [])) {
221
+ for (const aliasedField of field.aliases ?? []) {
219
222
  if (aliasedField in data) {
220
223
  inboundField = aliasedField;
221
224
  break;
@@ -280,7 +283,8 @@ export class BindUtil {
280
283
  return value;
281
284
  }
282
285
  const complex = SchemaRegistryIndex.has(config.type);
283
- const bindConfig: BindConfig | undefined = (complex && 'view' in config && typeof config.view === 'string') ? { view: config.view } : undefined;
286
+ const bindConfig: BindConfig | undefined =
287
+ complex && 'view' in config && typeof config.view === 'string' ? { view: config.view } : undefined;
284
288
  if (config.array) {
285
289
  const subValue = !Array.isArray(value) ? [value] : value;
286
290
  if (complex) {
@@ -324,4 +328,4 @@ export class BindUtil {
324
328
  const paramConfigs = SchemaRegistryIndex.get(cls).getMethod(method).parameters;
325
329
  return this.coerceParameters(paramConfigs, params, applyDefaults);
326
330
  }
327
- }
331
+ }
package/src/data.ts CHANGED
@@ -1,38 +1,47 @@
1
- import { isNumberObject, isBooleanObject, isStringObject } from 'node:util/types';
1
+ import { isBooleanObject, isNumberObject, isStringObject } from 'node:util/types';
2
+
3
+ import { asConstructable, asFull, type Class, castTo, TypedObject } from '@travetto/runtime';
2
4
 
3
- import { asConstructable, castTo, type Class, asFull, TypedObject } from '@travetto/runtime';
4
5
  import { UnknownType } from './types.ts';
5
6
 
6
- const REGEX_PATTERN = /[\/](.*)[\/](i|g|m|s)?/;
7
+ const REGEX_PATTERN = /[/](.*)[/](i|g|m|s)?/;
7
8
 
8
9
  /**
9
10
  * Utilities for data conversion and binding
10
11
  */
11
12
  export class DataUtil {
12
-
13
13
  /**
14
14
  * Is a value a plain JS object, created using {}
15
15
  * @param value Object to check
16
16
  */
17
17
  static isPlainObject(value: unknown): value is Record<string, unknown> {
18
- return typeof value === 'object' // separate from primitives
19
- && value !== undefined
20
- && value !== null // is obvious
21
- && value.constructor === Object // separate instances (Array, DOM, ...)
22
- && Object.prototype.toString.call(value) === '[object Object]'; // separate build-in like Math
18
+ return (
19
+ typeof value === 'object' && // separate from primitives
20
+ value !== undefined &&
21
+ value !== null && // is obvious
22
+ value.constructor === Object && // separate instances (Array, DOM, ...)
23
+ Object.prototype.toString.call(value) === '[object Object]'
24
+ ); // separate build-in like Math
23
25
  }
24
26
 
25
27
  /**
26
28
  * Is a value of primitive type
27
29
  * @param value Value to check
28
30
  */
29
- static isPrimitive(value: unknown): value is (string | boolean | number | RegExp) {
31
+ static isPrimitive(value: unknown): value is string | boolean | number | RegExp {
30
32
  switch (typeof value) {
31
- case 'string': case 'boolean': case 'number': case 'bigint': return true;
32
- case 'object': return !!value && (
33
- value instanceof RegExp || value instanceof Date || isStringObject(value) || isNumberObject(value) || isBooleanObject(value)
34
- );
35
- default: return false;
33
+ case 'string':
34
+ case 'boolean':
35
+ case 'number':
36
+ case 'bigint':
37
+ return true;
38
+ case 'object':
39
+ return (
40
+ !!value &&
41
+ (value instanceof RegExp || value instanceof Date || isStringObject(value) || isNumberObject(value) || isBooleanObject(value))
42
+ );
43
+ default:
44
+ return false;
36
45
  }
37
46
  }
38
47
 
@@ -53,7 +62,8 @@ export class DataUtil {
53
62
 
54
63
  let value: unknown;
55
64
 
56
- if (isEmptyA || isEmptyB) { // If no `a`, `b` always wins
65
+ if (isEmptyA || isEmptyB) {
66
+ // If no `a`, `b` always wins
57
67
  if (mode === 'replace' || b === null || !isEmptyB) {
58
68
  value = isEmptyB ? b : this.shallowClone(b);
59
69
  } else if (!isEmptyA) {
@@ -65,7 +75,8 @@ export class DataUtil {
65
75
  if (isArrA !== isArrB || isSimpA !== isSimpB) {
66
76
  throw new Error(`Cannot merge differing types ${a} and ${b}`);
67
77
  }
68
- if (Array.isArray(b)) { // Arrays
78
+ if (Array.isArray(b)) {
79
+ // Arrays
69
80
  value = a; // Write onto A
70
81
  if (mode === 'replace') {
71
82
  value = b;
@@ -76,18 +87,23 @@ export class DataUtil {
76
87
  valueArray[i] = this.#deepAssignRaw(valueArray[i], bArray[i], mode);
77
88
  }
78
89
  }
79
- } else if (isSimpB) { // Scalars
90
+ } else if (isSimpB) {
91
+ // Scalars
80
92
  const match = typeof a === typeof b;
81
93
  value = b;
82
94
 
83
- if (!match) { // If types do not match
84
- if (mode === 'strict') { // Bail on strict
95
+ if (!match) {
96
+ // If types do not match
97
+ if (mode === 'strict') {
98
+ // Bail on strict
85
99
  throw new Error(`Cannot merge ${a} [${typeof a}] with ${b} [${typeof b}]`);
86
- } else if (mode === 'coerce') { // Force on coerce
100
+ } else if (mode === 'coerce') {
101
+ // Force on coerce
87
102
  value = this.coerceType(b, asConstructable(a).constructor, false);
88
103
  }
89
104
  }
90
- } else { // Object merge
105
+ } else {
106
+ // Object merge
91
107
  value = a;
92
108
  const bObject: Record<string, unknown> = castTo(b);
93
109
  const valueObject: Record<string, unknown> = castTo(value);
@@ -148,15 +164,16 @@ export class DataUtil {
148
164
  if (typeof input === 'object' && 'toDate' in input && typeof input.toDate === 'function') {
149
165
  value = castTo(input.toDate());
150
166
  } else {
151
- value = input instanceof Date ?
152
- input :
153
- typeof input === 'number' ?
154
- new Date(input) :
155
- typeof input === 'bigint' ?
156
- new Date(Number(input)) :
157
- (typeof input === 'string' && /^[-]?\d+$/.test(input)) ?
158
- new Date(parseInt(input, 10)) :
159
- new Date(input.toString());
167
+ value =
168
+ input instanceof Date
169
+ ? input
170
+ : typeof input === 'number'
171
+ ? new Date(input)
172
+ : typeof input === 'bigint'
173
+ ? new Date(Number(input))
174
+ : typeof input === 'string' && /^[-]?\d+$/.test(input)
175
+ ? new Date(parseInt(input, 10))
176
+ : new Date(input.toString());
160
177
  }
161
178
  if (strict && value && Number.isNaN(value.getTime())) {
162
179
  throw new Error(`Invalid date value: ${input}`);
@@ -178,8 +195,7 @@ export class DataUtil {
178
195
  return input;
179
196
  }
180
197
  try {
181
- return BigInt((typeof input === 'boolean' || typeof input === 'number') ?
182
- input : `${input}`.replace(/n$/i, ''));
198
+ return BigInt(typeof input === 'boolean' || typeof input === 'number' ? input : `${input}`.replace(/n$/i, ''));
183
199
  } catch {
184
200
  if (strict) {
185
201
  throw new Error(`Invalid numeric value: ${input}`);
@@ -222,7 +238,8 @@ export class DataUtil {
222
238
  }
223
239
  }
224
240
  case undefined:
225
- case String: return `${input}`;
241
+ case String:
242
+ return `${input}`;
226
243
  }
227
244
  if (!strict || this.isPlainObject(input)) {
228
245
  return input;
@@ -236,7 +253,7 @@ export class DataUtil {
236
253
  * @param value Object to clone
237
254
  */
238
255
  static shallowClone<T>(value: T): T {
239
- return castTo(Array.isArray(value) ? value.slice(0) : (this.isSimpleValue(value) ? value : { ...castTo<object>(value) }));
256
+ return castTo(Array.isArray(value) ? value.slice(0) : this.isSimpleValue(value) ? value : { ...castTo<object>(value) });
240
257
  }
241
258
 
242
259
  /**
@@ -245,7 +262,7 @@ export class DataUtil {
245
262
  * @param b The source
246
263
  * @param mode How the assignment should be handled
247
264
  */
248
- static deepAssign<T, U>(a: T, b: U, mode: | 'replace' | 'loose' | 'strict' | 'coerce' = 'loose'): T & U {
265
+ static deepAssign<T, U>(a: T, b: U, mode: 'replace' | 'loose' | 'strict' | 'coerce' = 'loose'): T & U {
249
266
  if (!a || this.isSimpleValue(a)) {
250
267
  throw new Error(`Cannot merge onto a simple value, ${a}`);
251
268
  }
@@ -278,4 +295,4 @@ export class DataUtil {
278
295
  return input;
279
296
  }
280
297
  }
281
- }
298
+ }
@@ -1,7 +1,7 @@
1
1
  import { type Class, type ClassInstance, getClass } from '@travetto/runtime';
2
2
 
3
- import type { SchemaCoreConfig } from '../service/types.ts';
4
3
  import { SchemaRegistryIndex } from '../service/registry-index.ts';
4
+ import type { SchemaCoreConfig } from '../service/types.ts';
5
5
 
6
6
  /**
7
7
  * Describe a model or a field
@@ -31,7 +31,7 @@ export function Describe(config: Partial<Omit<SchemaCoreConfig, 'metadata'>>) {
31
31
  * @augments `@travetto/schema:Input`
32
32
  * @kind decorator
33
33
  */
34
- export const IsPrivate = (): (instanceOrCls: Class | ClassInstance, property?: string) => void => Describe({ private: true });
34
+ export const IsPrivate = (): ((instanceOrCls: Class | ClassInstance, property?: string) => void) => Describe({ private: true });
35
35
 
36
36
  /**
37
37
  * Mark a field/method as ignored
@@ -39,5 +39,5 @@ export const IsPrivate = (): (instanceOrCls: Class | ClassInstance, property?: s
39
39
  * @kind decorator
40
40
  */
41
41
  export function Ignore(): PropertyDecorator {
42
- return () => { };
42
+ return () => {};
43
43
  }
@@ -1,11 +1,13 @@
1
1
  import { type Any, type ClassInstance, getClass } from '@travetto/runtime';
2
2
 
3
- import type { SchemaFieldConfig } from '../service/types.ts';
4
3
  import { SchemaRegistryIndex } from '../service/registry-index.ts';
4
+ import type { SchemaFieldConfig } from '../service/types.ts';
5
5
 
6
- type PropType<V> = (<T extends Partial<Record<K, V | Function>>, K extends string>(
7
- instance: T, property: K, idx?: TypedPropertyDescriptor<Any> | number
8
- ) => void);
6
+ type PropType<V> = <T extends Partial<Record<K, V | Function>>, K extends string>(
7
+ instance: T,
8
+ property: K,
9
+ idx?: TypedPropertyDescriptor<Any> | number
10
+ ) => void;
9
11
 
10
12
  function field<V>(...configs: Partial<SchemaFieldConfig>[]): PropType<V> {
11
13
  return (instance: ClassInstance, property: string): void => {
@@ -30,14 +32,18 @@ export function Field(type?: Pick<SchemaFieldConfig, 'type' | 'array'>, ...confi
30
32
  * @augments `@travetto/schema:Input`
31
33
  * @kind decorator
32
34
  */
33
- export function Writeonly(): PropType<unknown> { return field({ access: 'writeonly' }); }
35
+ export function Writeonly(): PropType<unknown> {
36
+ return field({ access: 'writeonly' });
37
+ }
34
38
 
35
39
  /**
36
40
  * Mark a field as readonly
37
41
  * @augments `@travetto/schema:Input`
38
42
  * @kind decorator
39
43
  */
40
- export function Readonly(): PropType<unknown> { return field({ access: 'readonly' }); }
44
+ export function Readonly(): PropType<unknown> {
45
+ return field({ access: 'readonly' });
46
+ }
41
47
 
42
48
  /**
43
49
  * Mark a field as sensitive
@@ -45,4 +51,6 @@ export function Readonly(): PropType<unknown> { return field({ access: 'readonly
45
51
  * @augments `@travetto/schema:Input`
46
52
  * @kind decorator
47
53
  */
48
- export function Secret(active = true): PropType<unknown> { return field({ secret: active }); }
54
+ export function Secret(active = true): PropType<unknown> {
55
+ return field({ secret: active });
56
+ }