@travetto/schema 7.0.0-rc.2 → 7.0.0-rc.4

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
@@ -293,11 +293,11 @@ export interface ValidationError {
293
293
  /**
294
294
  * Regular expression to match
295
295
  */
296
- re?: string;
296
+ regex?: string;
297
297
  /**
298
298
  * Number to compare against
299
299
  */
300
- n?: number | Date;
300
+ limit?: number | Date;
301
301
  /**
302
302
  * The type of the field
303
303
  */
@@ -322,7 +322,7 @@ This feature is meant to allow for simple Typescript types to be able to be back
322
322
  /**
323
323
  * Geometric Point as [number,number] with validation and binding support
324
324
  *
325
- * @concrete ./internal/types.ts#PointImplementation
325
+ * @concrete ./internal/types.ts#PointContract
326
326
  */
327
327
  export type Point = [number, number];
328
328
  ```
@@ -334,9 +334,9 @@ import { DataUtil } from '../data.ts';
334
334
  const InvalidSymbol = Symbol();
335
335
 
336
336
  /**
337
- * Point Implementation
337
+ * Point Contract
338
338
  */
339
- export class PointImplementation {
339
+ export class PointContract {
340
340
 
341
341
  /**
342
342
  * Validate we have an actual point
@@ -359,7 +359,7 @@ export class PointImplementation {
359
359
  }
360
360
  }
361
361
 
362
- Object.defineProperty(PointImplementation, 'name', { value: 'Point' });
362
+ Object.defineProperty(PointContract, 'name', { value: 'Point' });
363
363
  ```
364
364
 
365
365
  What you can see here is that the `Point` type is now backed by a class that supports:
package/__index__.ts CHANGED
@@ -4,10 +4,9 @@ export * from './src/decorator/input.ts';
4
4
  export * from './src/decorator/schema.ts';
5
5
  export * from './src/decorator/method.ts';
6
6
  export * from './src/decorator/common.ts';
7
- export * from './src/service/changes.ts';
8
7
  export * from './src/service/types.ts';
9
8
  export * from './src/validate/messages.ts';
10
- export * from './src/validate/regexp.ts';
9
+ export * from './src/validate/regex.ts';
11
10
  export * from './src/validate/validator.ts';
12
11
  export * from './src/validate/error.ts';
13
12
  export * from './src/validate/types.ts';
@@ -15,4 +14,5 @@ export * from './src/bind-util.ts';
15
14
  export * from './src/data.ts';
16
15
  export * from './src/name.ts';
17
16
  export * from './src/types.ts';
18
- export * from './src/service/registry-index.ts';
17
+ export * from './src/service/registry-index.ts';
18
+ export * from './src/service/registry-adapter.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/schema",
3
- "version": "7.0.0-rc.2",
3
+ "version": "7.0.0-rc.4",
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": "^7.0.0-rc.2"
30
+ "@travetto/registry": "^7.0.0-rc.4"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^7.0.0-rc.2"
33
+ "@travetto/transformer": "^7.0.0-rc.3"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
@@ -1,6 +1,6 @@
1
1
  import { Any, Class, ClassInstance, getClass } from '@travetto/runtime';
2
2
 
3
- import { CommonRegExp } from '../validate/regexp.ts';
3
+ import { CommonRegex } from '../validate/regex.ts';
4
4
  import { CONSTRUCTOR_PROPERTY, SchemaInputConfig } from '../service/types.ts';
5
5
  import { SchemaRegistryIndex } from '../service/registry-index.ts';
6
6
 
@@ -76,53 +76,53 @@ export function LongText(): PropType<string | string[]> { return input({ specifi
76
76
 
77
77
  /**
78
78
  * Require the input to match a specific RegExp
79
- * @param re The regular expression to match against
79
+ * @param regex The regular expression to match against
80
80
  * @param message The message to show when the constraint fails
81
81
  * @augments `@travetto/schema:Input`
82
82
  * @kind decorator
83
83
  */
84
- export function Match(re: RegExp, message?: string): PropType<string | string[]> { return input({ match: { re, message } }); }
84
+ export function Match(regex: RegExp, message?: string): PropType<string | string[]> { return input({ match: { regex, message } }); }
85
85
 
86
86
  /**
87
87
  * The minimum length for the string or array
88
- * @param n The minimum length
88
+ * @param limit The minimum length
89
89
  * @param message The message to show when the constraint fails
90
90
  * @augments `@travetto/schema:Input`
91
91
  * @kind decorator
92
92
  */
93
- export function MinLength(n: number, message?: string): PropType<string | unknown[]> {
94
- return input({ minlength: { n, message }, ...(n === 0 ? { required: { active: false } } : {}) });
93
+ export function MinLength(limit: number, message?: string): PropType<string | unknown[]> {
94
+ return input({ minlength: { limit, message }, ...(limit === 0 ? { required: { active: false } } : {}) });
95
95
  }
96
96
 
97
97
  /**
98
98
  * The maximum length for the string or array
99
- * @param n The maximum length
99
+ * @param limit The maximum length
100
100
  * @param message The message to show when the constraint fails
101
101
  * @augments `@travetto/schema:Input`
102
102
  * @kind decorator
103
103
  */
104
- export function MaxLength(n: number, message?: string): PropType<string | unknown[]> { return input({ maxlength: { n, message } }); }
104
+ export function MaxLength(limit: number, message?: string): PropType<string | unknown[]> { return input({ maxlength: { limit, message } }); }
105
105
 
106
106
  /**
107
107
  * The minimum value
108
- * @param n The minimum value
108
+ * @param limit The minimum value
109
109
  * @param message The message to show when the constraint fails
110
110
  * @augments `@travetto/schema:Input`
111
111
  * @kind decorator
112
112
  */
113
- export function Min<T extends number | Date>(n: T, message?: string): PropType<Date | number> {
114
- return input({ min: { n, message } });
113
+ export function Min<T extends number | Date>(limit: T, message?: string): PropType<Date | number> {
114
+ return input({ min: { limit, message } });
115
115
  }
116
116
 
117
117
  /**
118
118
  * The maximum value
119
- * @param n The maximum value
119
+ * @param limit The maximum value
120
120
  * @param message The message to show when the constraint fails
121
121
  * @augments `@travetto/schema:Input`
122
122
  * @kind decorator
123
123
  */
124
- export function Max<T extends number | Date>(n: T, message?: string): PropType<Date | number> {
125
- return input({ max: { n, message } });
124
+ export function Max<T extends number | Date>(limit: T, message?: string): PropType<Date | number> {
125
+ return input({ max: { limit, message } });
126
126
  }
127
127
 
128
128
  /**
@@ -131,7 +131,7 @@ export function Max<T extends number | Date>(n: T, message?: string): PropType<D
131
131
  * @augments `@travetto/schema:Input`
132
132
  * @kind decorator
133
133
  */
134
- export function Email(message?: string): PropType<string | string[]> { return Match(CommonRegExp.email, message); }
134
+ export function Email(message?: string): PropType<string | string[]> { return Match(CommonRegex.email, message); }
135
135
 
136
136
  /**
137
137
  * Mark an input as an telephone number
@@ -139,7 +139,7 @@ export function Email(message?: string): PropType<string | string[]> { return Ma
139
139
  * @augments `@travetto/schema:Input`
140
140
  * @kind decorator
141
141
  */
142
- export function Telephone(message?: string): PropType<string | string[]> { return Match(CommonRegExp.telephone, message); }
142
+ export function Telephone(message?: string): PropType<string | string[]> { return Match(CommonRegex.telephone, message); }
143
143
 
144
144
  /**
145
145
  * Mark an input as a url
@@ -147,7 +147,7 @@ export function Telephone(message?: string): PropType<string | string[]> { retur
147
147
  * @augments `@travetto/schema:Input`
148
148
  * @kind decorator
149
149
  */
150
- export function Url(message?: string): PropType<string | string[]> { return Match(CommonRegExp.url, message); }
150
+ export function Url(message?: string): PropType<string | string[]> { return Match(CommonRegex.url, message); }
151
151
 
152
152
  /**
153
153
  * Determine the numeric precision of the value
@@ -3,9 +3,9 @@ import { DataUtil } from '../data.ts';
3
3
  const InvalidSymbol = Symbol();
4
4
 
5
5
  /**
6
- * Point Implementation
6
+ * Point Contract
7
7
  */
8
- export class PointImplementation {
8
+ export class PointContract {
9
9
 
10
10
  /**
11
11
  * Validate we have an actual point
@@ -28,4 +28,4 @@ export class PointImplementation {
28
28
  }
29
29
  }
30
30
 
31
- Object.defineProperty(PointImplementation, 'name', { value: 'Point' });
31
+ Object.defineProperty(PointContract, 'name', { value: 'Point' });
@@ -7,6 +7,8 @@ import {
7
7
  CONSTRUCTOR_PROPERTY
8
8
  } from './types';
9
9
 
10
+ export type SchemaDiscriminatedInfo = Required<Pick<SchemaClassConfig, 'discriminatedType' | 'discriminatedField' | 'discriminatedBase'>>;
11
+
10
12
  const classToDiscriminatedType = (cls: Class): string => cls.name
11
13
  .replace(/([A-Z])([A-Z][a-z])/g, (all, left, right) => `${left}_${right.toLowerCase()}`)
12
14
  .replace(/([a-z]|\b)([A-Z])/g, (all, left, right) => left ? `${left}_${right.toLowerCase()}` : right.toLowerCase())
@@ -69,6 +71,7 @@ function getConstructorConfig<T extends SchemaClassConfig>(base: Partial<T>, par
69
71
  const parentCons = parent?.methods?.[CONSTRUCTOR_PROPERTY];
70
72
  const baseCons = base.methods?.[CONSTRUCTOR_PROPERTY];
71
73
  return {
74
+ class: base.class!,
72
75
  parameters: [],
73
76
  validators: [],
74
77
  ...parentCons,
@@ -168,7 +171,7 @@ export class SchemaRegistryAdapter implements RegistryAdapter<SchemaClassConfig>
168
171
 
169
172
  registerField(field: string, ...data: Partial<SchemaFieldConfig>[]): SchemaFieldConfig {
170
173
  const classConfig = this.register({});
171
- const config = classConfig.fields[field] ??= { name: field, owner: this.#cls, type: null! };
174
+ const config = classConfig.fields[field] ??= { name: field, class: this.#cls, type: null! };
172
175
  const combined = combineInputs(config, data);
173
176
  return combined;
174
177
  }
@@ -197,7 +200,7 @@ export class SchemaRegistryAdapter implements RegistryAdapter<SchemaClassConfig>
197
200
 
198
201
  registerMethod(method: string, ...data: Partial<SchemaMethodConfig>[]): SchemaMethodConfig {
199
202
  const classConfig = this.register();
200
- const config = classConfig.methods[method] ??= { parameters: [], validators: [] };
203
+ const config = classConfig.methods[method] ??= { class: this.#cls, parameters: [], validators: [] };
201
204
  return combineMethods(config, data);
202
205
  }
203
206
 
@@ -213,7 +216,7 @@ export class SchemaRegistryAdapter implements RegistryAdapter<SchemaClassConfig>
213
216
 
214
217
  registerParameter(method: string, idx: number, ...data: Partial<SchemaParameterConfig>[]): SchemaParameterConfig {
215
218
  const params = this.registerMethod(method, {}).parameters;
216
- const config = params[idx] ??= { method, index: idx, owner: this.#cls, array: false, type: null! };
219
+ const config = params[idx] ??= { method, index: idx, class: this.#cls, array: false, type: null! };
217
220
  return combineInputs(config, data);
218
221
  }
219
222
 
@@ -305,8 +308,8 @@ export class SchemaRegistryAdapter implements RegistryAdapter<SchemaClassConfig>
305
308
  }
306
309
 
307
310
  /**
308
- * Provides the prototype-derived descriptor for a property
309
- */
311
+ * Provides the prototype-derived descriptor for a property
312
+ */
310
313
  getAccessorDescriptor(field: string): PropertyDescriptor {
311
314
  if (!this.#accessorDescriptors.has(field)) {
312
315
  let proto = this.#cls.prototype;
@@ -330,7 +333,7 @@ export class SchemaRegistryAdapter implements RegistryAdapter<SchemaClassConfig>
330
333
  return value;
331
334
  }
332
335
 
333
- getDiscriminatedConfig(): Required<Pick<SchemaClassConfig, 'discriminatedType' | 'discriminatedField' | 'discriminatedBase'>> | undefined {
336
+ getDiscriminatedConfig(): SchemaDiscriminatedInfo | undefined {
334
337
  const { discriminatedField, discriminatedType, discriminatedBase } = this.#config;
335
338
  if (discriminatedType && discriminatedField) {
336
339
  return { discriminatedType, discriminatedField, discriminatedBase: !!discriminatedBase };
@@ -1,9 +1,8 @@
1
- import { ChangeEvent, RegistrationMethods, RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
2
- import { AppError, castKey, castTo, Class, classConstruct, getParentClass, Util } from '@travetto/runtime';
1
+ import { RegistrationMethods, RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
2
+ import { AppError, castKey, castTo, Class, classConstruct, getParentClass } from '@travetto/runtime';
3
3
 
4
4
  import { SchemaFieldConfig, SchemaClassConfig } from './types.ts';
5
- import { SchemaRegistryAdapter } from './registry-adapter.ts';
6
- import { SchemaChangeListener } from './changes.ts';
5
+ import { SchemaDiscriminatedInfo, SchemaRegistryAdapter } from './registry-adapter.ts';
7
6
 
8
7
  /**
9
8
  * Schema registry index for managing schema configurations across classes
@@ -20,7 +19,7 @@ export class SchemaRegistryIndex implements RegistryIndex {
20
19
  return this.#instance.store.get(cls).get();
21
20
  }
22
21
 
23
- static getDiscriminatedConfig<T>(cls: Class<T>): Required<Pick<SchemaClassConfig, 'discriminatedType' | 'discriminatedField' | 'discriminatedBase'>> | undefined {
22
+ static getDiscriminatedConfig<T>(cls: Class<T>): SchemaDiscriminatedInfo | undefined {
24
23
  return this.#instance.store.get(cls).getDiscriminatedConfig();
25
24
  }
26
25
 
@@ -28,10 +27,6 @@ export class SchemaRegistryIndex implements RegistryIndex {
28
27
  return this.#instance.store.has(cls);
29
28
  }
30
29
 
31
- static getClassById(classId: string): Class {
32
- return this.#instance.store.getClassById(classId);
33
- }
34
-
35
30
  static getDiscriminatedTypes(cls: Class): string[] | undefined {
36
31
  return this.#instance.getDiscriminatedTypes(cls);
37
32
  }
@@ -68,6 +63,8 @@ export class SchemaRegistryIndex implements RegistryIndex {
68
63
  #baseSchema = new Map<Class, Class>();
69
64
  #byDiscriminatedTypes = new Map<Class, Map<string, Class>>();
70
65
 
66
+ /** @private */ constructor(source: unknown) { Registry.validateConstructor(source); }
67
+
71
68
  /**
72
69
  * Register discriminated types for a class
73
70
  */
@@ -84,40 +81,7 @@ export class SchemaRegistryIndex implements RegistryIndex {
84
81
  this.#byDiscriminatedTypes.get(base)!.set(config.discriminatedType, cls);
85
82
  }
86
83
 
87
- #onChanged(event: ChangeEvent<Class> & { type: 'changed' }): void {
88
- Util.queueMacroTask().then(() => {
89
- SchemaChangeListener.emitFieldChanges({
90
- type: 'changed',
91
- current: this.getClassConfig(event.current),
92
- previous: this.getClassConfig(event.previous)
93
- });
94
- });
95
- }
96
-
97
- #onRemoving(event: ChangeEvent<Class> & { type: 'removing' }): void {
98
- SchemaChangeListener.clearSchemaDependency(event.previous);
99
- }
100
-
101
- #onAdded(event: ChangeEvent<Class> & { type: 'added' }): void {
102
- Util.queueMacroTask().then(() => {
103
- SchemaChangeListener.emitFieldChanges({
104
- type: 'added',
105
- current: this.getClassConfig(event.current)
106
- });
107
- });
108
- }
109
-
110
- process(events: ChangeEvent<Class>[]): void {
111
- for (const event of events) {
112
- if (event.type === 'changed') {
113
- this.#onChanged(event);
114
- } else if (event.type === 'removing') {
115
- this.#onRemoving(event);
116
- } else if (event.type === 'added') {
117
- this.#onAdded(event);
118
- }
119
- }
120
-
84
+ beforeChangeSetComplete(): void {
121
85
  // Rebuild indices after every "process" batch
122
86
  this.#byDiscriminatedTypes.clear();
123
87
  for (const cls of this.store.getClasses()) {
@@ -174,25 +138,6 @@ export class SchemaRegistryIndex implements RegistryIndex {
174
138
  }
175
139
  }
176
140
 
177
- /**
178
- * Track changes to schemas, and track the dependent changes
179
- * @param cls The root class of the hierarchy
180
- * @param current The new class
181
- * @param path The path within the object hierarchy
182
- */
183
- trackSchemaDependencies(cls: Class, current: Class = cls, path: SchemaFieldConfig[] = []): void {
184
- const config = this.getClassConfig(current);
185
-
186
- SchemaChangeListener.trackSchemaDependency(current, cls, path, this.getClassConfig(cls));
187
-
188
- // Read children
189
- for (const field of Object.values(config.fields)) {
190
- if (SchemaRegistryIndex.has(field.type) && field.type !== cls) {
191
- this.trackSchemaDependencies(cls, field.type, [...path, field]);
192
- }
193
- }
194
- }
195
-
196
141
  /**
197
142
  * Visit fields recursively
198
143
  */
@@ -36,6 +36,10 @@ export type SchemaBasicType = {
36
36
  * Basic schema configuration
37
37
  */
38
38
  export interface SchemaCoreConfig {
39
+ /**
40
+ * Schema class
41
+ */
42
+ class: Class;
39
43
  /**
40
44
  * Description
41
45
  */
@@ -90,10 +94,6 @@ export interface SchemaFieldMap {
90
94
  * Class configuration
91
95
  */
92
96
  export interface SchemaClassConfig extends SchemaCoreConfig {
93
- /**
94
- * Target class
95
- */
96
- class: Class;
97
97
  /**
98
98
  * List of all views
99
99
  */
@@ -144,10 +144,6 @@ export interface SchemaInputConfig extends SchemaCoreConfig, SchemaBasicType {
144
144
  * Key name for validation when dealing with complex types
145
145
  */
146
146
  view?: string;
147
- /**
148
- * Owner class
149
- */
150
- owner: Class;
151
147
  /**
152
148
  * List of aliases
153
149
  */
@@ -167,23 +163,23 @@ export interface SchemaInputConfig extends SchemaCoreConfig, SchemaBasicType {
167
163
  /**
168
164
  * Does the field expect a match
169
165
  */
170
- match?: { re: RegExp, message?: string, template?: TemplateLiteral };
166
+ match?: { regex: RegExp, message?: string, template?: TemplateLiteral };
171
167
  /**
172
168
  * Minimum value configuration
173
169
  */
174
- min?: { n: number | Date, message?: string };
170
+ min?: { limit: number | Date, message?: string };
175
171
  /**
176
172
  * Maximum value configuration
177
173
  */
178
- max?: { n: number | Date, message?: string };
174
+ max?: { limit: number | Date, message?: string };
179
175
  /**
180
176
  * Minimum length configuration
181
177
  */
182
- minlength?: { n: number, message?: string };
178
+ minlength?: { limit: number, message?: string };
183
179
  /**
184
180
  * Maximum length configuration
185
181
  */
186
- maxlength?: { n: number, message?: string };
182
+ maxlength?: { limit: number, message?: string };
187
183
  /**
188
184
  * Enumerated values
189
185
  */
package/src/types.ts CHANGED
@@ -3,6 +3,6 @@ export class UnknownType { }
3
3
  /**
4
4
  * Geometric Point as [number,number] with validation and binding support
5
5
  *
6
- * @concrete ./internal/types.ts#PointImplementation
6
+ * @concrete ./internal/types.ts#PointContract
7
7
  */
8
8
  export type Point = [number, number];
@@ -5,14 +5,14 @@ export const Messages = new Map<string, string>(Object.entries({
5
5
  default: '{path} is not valid',
6
6
  type: '{path} is not a valid {type}',
7
7
  required: '{path} is required',
8
- minlength: '{path} is not long enough ({n})',
9
- maxlength: '{path} is too long ({n})',
10
- match: '{path} should match {re}',
11
- min: '{path} is less than ({n})',
12
- max: '{path} is greater than ({n})',
13
- telephone: '{path} is not a valid phone number',
14
- url: '{path} is not a valid url',
15
- simpleName: '{path} is not a proper name',
16
- postalCode: '{path} is not a valid postal code',
17
- email: '{path} is not a valid email address'
8
+ minlength: '{path} is not long enough ({limit})',
9
+ maxlength: '{path} is too long ({limit})',
10
+ match: '{path} should match {regex}',
11
+ min: '{path} is less than ({limit})',
12
+ max: '{path} is greater than ({limit})',
13
+ '[[:telephone:]]': '{path} is not a valid phone number',
14
+ '[[:url:]]': '{path} is not a valid url',
15
+ '[[:simpleName:]]': '{path} is not a proper name',
16
+ '[[:postalCode:]]': '{path} is not a valid postal code',
17
+ '[[:email:]]': '{path} is not a valid email address'
18
18
  }));
@@ -0,0 +1,22 @@
1
+ import { TypedObject } from '@travetto/runtime';
2
+
3
+ /**
4
+ * List of common regular expressions for fields
5
+ */
6
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
7
+ export const [CommonRegex, CommonRegexToName] = (() => {
8
+ const regexToName = new Map<RegExp, string>();
9
+ const regexes = {
10
+ email: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
11
+ telephone: /^(\+?\d{1,3}(\s*-?\s*|\s+))?((\(\d{3}\))|\d{3})(\s*|-|[.])(\d{3})(\s*|-|[.])(\d{4})(\s+(x|ext[.]?)\s*\d+)?$/,
12
+ url: /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/,
13
+ simpleName: /^([a-zA-Z\u0080-\u024F]{0,100}(?:. |-| |')){0,10}[a-zA-Z\u0080-\u024F]+$/,
14
+ postalCode: /^\d{5}(?:[-\s]\d{4})?$/
15
+ };
16
+ // Rebind regexes
17
+ for (const key of TypedObject.keys(regexes)) {
18
+ const name = `[[:${key}:]]`;
19
+ regexToName.set(regexes[key], name);
20
+ }
21
+ return [regexes, regexToName];
22
+ })();
@@ -25,11 +25,11 @@ export interface ValidationError {
25
25
  /**
26
26
  * Regular expression to match
27
27
  */
28
- re?: string;
28
+ regex?: string;
29
29
  /**
30
30
  * Number to compare against
31
31
  */
32
- n?: number | Date;
32
+ limit?: number | Date;
33
33
  /**
34
34
  * The type of the field
35
35
  */
@@ -63,11 +63,11 @@ export interface ValidationResult {
63
63
  /**
64
64
  * Potential regular expression for the result
65
65
  */
66
- re?: RegExp;
66
+ regex?: RegExp;
67
67
  /**
68
68
  * Number to compare against
69
69
  */
70
- n?: number | Date;
70
+ limit?: number | Date;
71
71
  }
72
72
 
73
73
  type OrPromise<T> = T | Promise<T>;
@@ -5,7 +5,7 @@ import { ValidationError, ValidationKindCore, ValidationResult } from './types.t
5
5
  import { Messages } from './messages.ts';
6
6
  import { isValidationError, TypeMismatchError, ValidationResultError } from './error.ts';
7
7
  import { DataUtil } from '../data.ts';
8
- import { CommonRegExpToName } from './regexp.ts';
8
+ import { CommonRegexToName } from './regex.ts';
9
9
  import { SchemaRegistryIndex } from '../service/registry-index.ts';
10
10
 
11
11
  /**
@@ -111,7 +111,7 @@ export class SchemaValidator {
111
111
  (input.type === Date ? Date.parse(value) : parseInt(value, 10)) :
112
112
  (value instanceof Date ? value.getTime() : value);
113
113
 
114
- const boundary = (typeof config.n === 'number' ? config.n : config.n.getTime());
114
+ const boundary = (typeof config.limit === 'number' ? config.limit : config.limit.getTime());
115
115
  return key === 'min' ? parsed < boundary : parsed > boundary;
116
116
  }
117
117
 
@@ -144,15 +144,15 @@ export class SchemaValidator {
144
144
  }
145
145
  }
146
146
 
147
- if (input.match && !input.match.re.test(`${value}`)) {
147
+ if (input.match && !input.match.regex.test(`${value}`)) {
148
148
  criteria.push(['match', input.match]);
149
149
  }
150
150
 
151
- if (input.minlength && `${value}`.length < input.minlength.n) {
151
+ if (input.minlength && `${value}`.length < input.minlength.limit) {
152
152
  criteria.push(['minlength', input.minlength]);
153
153
  }
154
154
 
155
- if (input.maxlength && `${value}`.length > input.maxlength.n) {
155
+ if (input.maxlength && `${value}`.length > input.maxlength.limit) {
156
156
  criteria.push(['maxlength', input.maxlength]);
157
157
  }
158
158
 
@@ -189,17 +189,17 @@ export class SchemaValidator {
189
189
  kind: result.kind,
190
190
  value: result.value,
191
191
  message: '',
192
- re: CommonRegExpToName.get(result.re!) ?? result.re?.source ?? '',
192
+ regex: CommonRegexToName.get(result.regex!) ?? result.regex?.source ?? '',
193
193
  path,
194
194
  type: (typeof result.type === 'function' ? result.type.name : result.type)
195
195
  };
196
196
 
197
- if (!error.re) {
198
- delete error.re;
197
+ if (!error.regex) {
198
+ delete error.regex;
199
199
  }
200
200
 
201
201
  const msg = result.message ?? (
202
- Messages.get(error.re ?? '') ??
202
+ Messages.get(error.regex ?? '') ??
203
203
  Messages.get(error.kind) ??
204
204
  Messages.get('default')!
205
205
  );
@@ -171,11 +171,11 @@ class ${uniqueId} extends ${type.mappedClassName} {
171
171
  };
172
172
  }
173
173
  } else if (primaryExpr.key === 'template' && primaryExpr.template) {
174
- const re = LiteralUtil.templateLiteralToRegex(primaryExpr.template);
174
+ const regex = LiteralUtil.templateLiteralToRegex(primaryExpr.template);
175
175
  attrs.match = {
176
- re: new RegExp(re),
176
+ regex: new RegExp(regex),
177
177
  template: primaryExpr.template,
178
- message: `{path} must match "${re}"`
178
+ message: `{path} must match "${regex}"`
179
179
  };
180
180
  }
181
181
 
@@ -1,152 +0,0 @@
1
- import { EventEmitter } from 'node:events';
2
-
3
- import { Class } from '@travetto/runtime';
4
- import { ChangeEvent } from '@travetto/registry';
5
-
6
- import { SchemaFieldConfig, SchemaClassConfig } from './types.ts';
7
-
8
- interface FieldMapping {
9
- path: SchemaFieldConfig[];
10
- config: SchemaClassConfig;
11
- }
12
-
13
- export interface FieldChangeEvent {
14
- cls: Class;
15
- changes: ChangeEvent<SchemaFieldConfig>[];
16
- }
17
-
18
- interface SubSchemaChange {
19
- path: SchemaFieldConfig[];
20
- fields: ChangeEvent<SchemaFieldConfig>[];
21
- }
22
-
23
- export interface SchemaChange {
24
- config: SchemaClassConfig;
25
- subs: SubSchemaChange[];
26
- }
27
-
28
- export interface SchemaChangeEvent {
29
- cls: Class;
30
- change: SchemaChange;
31
- }
32
-
33
- /**
34
- * Schema change listener. Handles all changes that occur via the SchemaRegistryIndex
35
- */
36
- class $SchemaChangeListener {
37
-
38
- #emitter = new EventEmitter();
39
- #mapping = new Map<string, Map<string, FieldMapping>>();
40
-
41
- /**
42
- * On schema change, emit the change event for the whole schema
43
- * @param handler The function to call on schema change
44
- */
45
- onSchemaChange(handler: (event: SchemaChangeEvent) => void): void {
46
- this.#emitter.on('schema', handler);
47
- }
48
-
49
- /**
50
- * On schema field change, emit the change event for the whole schema
51
- * @param handler The function to call on schema field change
52
- */
53
- onFieldChange(handler: (event: FieldChangeEvent) => void): void {
54
- this.#emitter.on('field', handler);
55
- }
56
-
57
- /**
58
- * Clear dependency mappings for a given class
59
- */
60
- clearSchemaDependency(cls: Class): void {
61
- this.#mapping.delete(cls.Ⲑid);
62
- }
63
-
64
- /**
65
- * Track a specific class for dependencies
66
- * @param cls The target class
67
- * @param parent The parent class
68
- * @param path The path within the object hierarchy to arrive at the class
69
- * @param config The configuration or the class
70
- */
71
- trackSchemaDependency(cls: Class, parent: Class, path: SchemaFieldConfig[], config: SchemaClassConfig): void {
72
- const idValue = cls.Ⲑid;
73
- if (!this.#mapping.has(idValue)) {
74
- this.#mapping.set(idValue, new Map());
75
- }
76
- this.#mapping.get(idValue)!.set(parent.Ⲑid, { path, config });
77
- }
78
-
79
- /**
80
- * Emit changes to the schema
81
- * @param cls The class of the event
82
- * @param changes The changes to send
83
- */
84
- emitSchemaChanges({ cls, changes }: FieldChangeEvent): void {
85
- const updates = new Map<string, SchemaChange>();
86
- const clsId = cls.Ⲑid;
87
-
88
- if (this.#mapping.has(clsId)) {
89
- const dependencies = this.#mapping.get(clsId)!;
90
- for (const dependencyClsId of dependencies.keys()) {
91
- if (!updates.has(dependencyClsId)) {
92
- updates.set(dependencyClsId, { config: dependencies.get(dependencyClsId)!.config, subs: [] });
93
- }
94
- const childDependency = dependencies.get(dependencyClsId)!;
95
- updates.get(dependencyClsId)!.subs.push({ path: [...childDependency.path], fields: changes });
96
- }
97
- }
98
-
99
- for (const key of updates.keys()) {
100
- this.#emitter.emit('schema', { cls: updates.get(key)!.config.class, change: updates.get(key)! });
101
- }
102
- }
103
-
104
- /**
105
- * Emit field level changes in the schema
106
- * @param previous The previous class config
107
- * @param current The current class config
108
- */
109
- emitFieldChanges(event: ChangeEvent<SchemaClassConfig>): void {
110
- const previous = 'previous' in event ? event.previous : undefined;
111
- const current = 'current' in event ? event.current : undefined;
112
-
113
- const previousFields = new Set(Object.keys(previous?.fields ?? {}));
114
- const currentFields = new Set(Object.keys(current?.fields ?? {}));
115
-
116
- const changes: ChangeEvent<SchemaFieldConfig>[] = [];
117
-
118
- for (const field of currentFields) {
119
- if (!previousFields.has(field) && current) {
120
- changes.push({ current: current.fields[field], type: 'added' });
121
- }
122
- }
123
-
124
- for (const field of previousFields) {
125
- if (!currentFields.has(field) && previous) {
126
- changes.push({ previous: previous.fields[field], type: 'removing' });
127
- }
128
- }
129
-
130
- // Handle class references changing, but keeping same id
131
- const compareTypes = (a: Class, b: Class): boolean => a.Ⲑid ? a.Ⲑid === b.Ⲑid : a === b;
132
-
133
- for (const field of currentFields) {
134
- if (previousFields.has(field) && previous && current) {
135
- const prevSchema = previous.fields[field];
136
- const currSchema = current.fields[field];
137
- if (
138
- JSON.stringify(prevSchema) !== JSON.stringify(currSchema) ||
139
- !compareTypes(prevSchema.type, currSchema.type)
140
- ) {
141
- changes.push({ previous: previous.fields[field], current: current.fields[field], type: 'changed' });
142
- }
143
- }
144
- }
145
-
146
- // Send field changes
147
- this.#emitter.emit('field', { cls: current!.class, changes });
148
- this.emitSchemaChanges({ cls: current!.class, changes });
149
- }
150
- }
151
-
152
- export const SchemaChangeListener = new $SchemaChangeListener();
@@ -1,22 +0,0 @@
1
- import { TypedObject } from '@travetto/runtime';
2
- import { Messages } from './messages.ts';
3
-
4
- /**
5
- * List of common regular expressions for fields
6
- */
7
- export const CommonRegExp = {
8
- email: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
9
- telephone: /^(\+?\d{1,3}(\s*-?\s*|\s+))?((\(\d{3}\))|\d{3})(\s*|-|[.])(\d{3})(\s*|-|[.])(\d{4})(\s+(x|ext[.]?)\s*\d+)?$/,
10
- url: /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/,
11
- simpleName: /^([a-zA-Z\u0080-\u024F]{0,100}(?:. |-| |')){0,10}[a-zA-Z\u0080-\u024F]+$/,
12
- postalCode: /^\d{5}(?:[-\s]\d{4})?$/
13
- };
14
-
15
- export const CommonRegExpToName = new Map<RegExp, string>();
16
-
17
- // Rebind regexes
18
- for (const key of TypedObject.keys(CommonRegExp)) {
19
- const name = `[[:${key}:]]`;
20
- CommonRegExpToName.set(CommonRegExp[key], name);
21
- Messages.set(name, Messages.get(key)!);
22
- }