@travetto/schema 2.1.3 → 2.2.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.
@@ -1,5 +1,5 @@
1
1
  // @file-if faker
2
- import type * as fakerType from 'faker';
2
+ import * as fakerType from 'faker';
3
3
 
4
4
  import { Class } from '@travetto/base';
5
5
 
@@ -9,13 +9,16 @@ import { SchemaRegistry } from '../service/registry';
9
9
  import { BindUtil } from '../bind-util';
10
10
 
11
11
  // Load faker on demand, as its very heavy in loading
12
- let faker = new Proxy({}, {
13
- get: (t, p, r) => (faker = require('faker'))[p]
14
- }) as typeof fakerType;
12
+ let faker: typeof fakerType = new Proxy(
13
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
14
+ {} as typeof fakerType,
15
+ {
16
+ get: (t, prop, r) => (faker = require('faker'))[prop]
17
+ });
15
18
 
16
19
  const DAY_IN_MS = 24 * 60 * 60 * 1000;
17
20
 
18
- const between = (fromDays: number, toDays: number) =>
21
+ const between = (fromDays: number, toDays: number): Date =>
19
22
  faker.date.between(
20
23
  new Date(Date.now() + fromDays * DAY_IN_MS),
21
24
  new Date(Date.now() + toDays * DAY_IN_MS)
@@ -27,53 +30,56 @@ const between = (fromDays: number, toDays: number) =>
27
30
  export class SchemaFakerUtil {
28
31
 
29
32
  static #stringToReType: [RegExp, () => string][] = [
30
- [CommonRegExp.email, () => faker.internet.email()],
31
- [CommonRegExp.url, () => faker.internet.url()],
32
- [CommonRegExp.telephone, () => faker.phone.phoneNumber()],
33
- [CommonRegExp.postalCode, () => faker.address.zipCode()]
33
+ [CommonRegExp.email, (): string => faker.internet.email()],
34
+ [CommonRegExp.url, (): string => faker.internet.url()],
35
+ [CommonRegExp.telephone, (): string => faker.phone.phoneNumber()],
36
+ [CommonRegExp.postalCode, (): string => faker.address.zipCode()]
34
37
  ];
35
38
 
36
39
  /**
37
40
  * Mapping of field types to faker utils
38
41
  */
39
- static #namesToType = {
40
- string: [
41
- [/^(image|img).*url$/, () => faker.image.imageUrl()],
42
- [/^url$/, () => faker.internet.url()],
43
- [/^email(addr(ress)?)?$/, () => faker.internet.email()],
44
- [/^(tele)?phone(num|number)?$/, () => faker.phone.phoneNumber()],
45
- [/^((postal|zip)code)|zip$/, () => faker.address.zipCode()],
46
- [/f(irst)?name/, () => faker.name.firstName()],
47
- [/l(ast)?name/, () => faker.name.lastName()],
48
- [/^ip(add(ress)?)?$/, () => faker.internet.ip()],
49
- [/^ip(add(ress)?)?(v?)6$/, () => faker.internet.ipv6()],
50
- [/^username$/, () => faker.internet.userName()],
51
- [/^domain(name)?$/, () => faker.internet.domainName()],
52
- [/^file(path|name)?$/, () => faker.system.filePath()],
53
- [/^street(1)?$/, () => faker.address.streetAddress()],
54
- [/^street2$/, () => faker.address.secondaryAddress()],
55
- [/^county$/, () => faker.address.county()],
56
- [/^country$/, () => faker.address.country()],
57
- [/^state$/, () => faker.address.state()],
58
- [/^lon(gitude)?$/, () => faker.address.longitude()],
59
- [/^lat(itude)?$/, () => faker.address.latitude()],
60
- [/(profile).*(image|img)/, () => faker.image.avatar()],
61
- [/(image|img)/, () => faker.image.image()],
62
- [/^company(name)?$/, () => faker.company.companyName()],
63
- [/(desc|description)$/, () => faker.lorem.sentences(10)]
64
- ] as [RegExp, () => string][],
65
- date: [
66
- [/dob|birth/, () => faker.date.past(60)],
67
- [/creat(e|ion)/, () => between(-200, -100)],
68
- [/(update|modif(y|ied))/, () => between(-100, -50)]
69
- ] as [RegExp, () => Date][],
70
- };
42
+ static #namesToType: {
43
+ string: [RegExp, () => string][];
44
+ date: [RegExp, () => Date][];
45
+ } = {
46
+ string: [
47
+ [/^(image|img).*url$/, (): string => faker.image.imageUrl()],
48
+ [/^url$/, (): string => faker.internet.url()],
49
+ [/^email(addr(ress)?)?$/, (): string => faker.internet.email()],
50
+ [/^(tele)?phone(num|number)?$/, (): string => faker.phone.phoneNumber()],
51
+ [/^((postal|zip)code)|zip$/, (): string => faker.address.zipCode()],
52
+ [/f(irst)?name/, (): string => faker.name.firstName()],
53
+ [/l(ast)?name/, (): string => faker.name.lastName()],
54
+ [/^ip(add(ress)?)?$/, (): string => faker.internet.ip()],
55
+ [/^ip(add(ress)?)?(v?)6$/, (): string => faker.internet.ipv6()],
56
+ [/^username$/, (): string => faker.internet.userName()],
57
+ [/^domain(name)?$/, (): string => faker.internet.domainName()],
58
+ [/^file(path|name)?$/, (): string => faker.system.filePath()],
59
+ [/^street(1)?$/, (): string => faker.address.streetAddress()],
60
+ [/^street2$/, (): string => faker.address.secondaryAddress()],
61
+ [/^county$/, (): string => faker.address.county()],
62
+ [/^country$/, (): string => faker.address.country()],
63
+ [/^state$/, (): string => faker.address.state()],
64
+ [/^lon(gitude)?$/, (): string => faker.address.longitude()],
65
+ [/^lat(itude)?$/, (): string => faker.address.latitude()],
66
+ [/(profile).*(image|img)/, (): string => faker.image.avatar()],
67
+ [/(image|img)/, (): string => faker.image.image()],
68
+ [/^company(name)?$/, (): string => faker.company.companyName()],
69
+ [/(desc|description)$/, (): string => faker.lorem.sentences(10)]
70
+ ],
71
+ date: [
72
+ [/dob|birth/, (): Date => faker.date.past(60)],
73
+ [/creat(e|ion)/, (): Date => between(-200, -100)],
74
+ [/(update|modif(y|ied))/, (): Date => between(-100, -50)]
75
+ ],
76
+ };
71
77
 
72
78
  /**
73
79
  * Get an array of values
74
80
  * @param cfg Field configuration
75
81
  */
76
- static getArrayValue(cfg: FieldConfig) {
82
+ static getArrayValue(cfg: FieldConfig): unknown[] {
77
83
  const min = cfg.minlength ? cfg.minlength.n : 0;
78
84
  const max = cfg.maxlength ? cfg.maxlength.n : 10;
79
85
  const size = faker.datatype.number({ min, max });
@@ -88,9 +94,9 @@ export class SchemaFakerUtil {
88
94
  * Get a new number value
89
95
  * @param cfg Number config
90
96
  */
91
- static getNumberValue(cfg: FieldConfig) {
92
- let min = cfg.min ? cfg.min.n as number : undefined;
93
- let max = cfg.max ? cfg.max.n as number : undefined;
97
+ static getNumberValue(cfg: FieldConfig): number {
98
+ let min = cfg.min && typeof cfg.min.n === 'number' ? cfg.min.n : undefined;
99
+ let max = cfg.max && typeof cfg.max.n === 'number' ? cfg.max.n : undefined;
94
100
  let precision = cfg.precision;
95
101
 
96
102
  const name = cfg.name.toUpperCase();
@@ -123,10 +129,10 @@ export class SchemaFakerUtil {
123
129
  * Get a date value
124
130
  * @param cfg Field config
125
131
  */
126
- static getDateValue(cfg: FieldConfig) {
132
+ static getDateValue(cfg: FieldConfig): Date {
127
133
  const name = cfg.name.toUpperCase();
128
- const min = cfg.min ? cfg.min.n as Date : undefined;
129
- const max = cfg.max ? cfg.max.n as Date : undefined;
134
+ const min = cfg.min && typeof cfg.min.n !== 'number' ? cfg.min.n : undefined;
135
+ const max = cfg.max && typeof cfg.max.n !== 'number' ? cfg.max.n : undefined;
130
136
 
131
137
  if (min !== undefined || max !== undefined) {
132
138
  return faker.date.between(min || new Date(Date.now() - (50 * DAY_IN_MS)), max || new Date());
@@ -144,7 +150,7 @@ export class SchemaFakerUtil {
144
150
  * Get a string value
145
151
  * @param cfg Field config
146
152
  */
147
- static getStringValue(cfg: FieldConfig) {
153
+ static getStringValue(cfg: FieldConfig): string {
148
154
  const name = cfg.name.toLowerCase();
149
155
 
150
156
  if (cfg.match) {
@@ -197,7 +203,7 @@ export class SchemaFakerUtil {
197
203
  * @param cls The class to get an instance of
198
204
  * @param view The view to define specifically
199
205
  */
200
- static generate<T>(cls: Class<T>, view?: string) {
206
+ static generate<T>(cls: Class<T>, view?: string): T {
201
207
  const cfg = SchemaRegistry.getViewSchema(cls, view);
202
208
  const out: Record<string, unknown> = {};
203
209
 
@@ -6,7 +6,7 @@ import { ChangeEvent } from '@travetto/registry';
6
6
  import { FieldConfig, ClassConfig } from './types';
7
7
  import { AllViewⲐ } from '../internal/types';
8
8
 
9
- const id = (c: Class | string) => typeof c === 'string' ? c : c.ᚕid;
9
+ const id = (c: Class | string): string => typeof c === 'string' ? c : c.ᚕid;
10
10
 
11
11
  interface FieldMapping {
12
12
  path: FieldConfig[];
@@ -45,7 +45,7 @@ class $SchemaChangeListener {
45
45
  * On schema change, emit the change event for the whole schema
46
46
  * @param cb The function to call on schema change
47
47
  */
48
- onSchemaChange(handler: (e: SchemaChangeEvent) => void) {
48
+ onSchemaChange(handler: (e: SchemaChangeEvent) => void): void {
49
49
  this.#emitter.on('schema', handler);
50
50
  }
51
51
 
@@ -53,21 +53,21 @@ class $SchemaChangeListener {
53
53
  * On schema field change, emit the change event for the whole schema
54
54
  * @param cb The function to call on schema field change
55
55
  */
56
- onFieldChange(handler: (e: FieldChangeEvent) => void) {
56
+ onFieldChange(handler: (e: FieldChangeEvent) => void): void {
57
57
  this.#emitter.on('field', handler);
58
58
  }
59
59
 
60
60
  /**
61
61
  * Reset the listener
62
62
  */
63
- reset() {
63
+ reset(): void {
64
64
  this.#mapping.clear();
65
65
  }
66
66
 
67
67
  /**
68
68
  * Clear dependency mappings for a given class
69
69
  */
70
- clearSchemaDependency(cls: Class) {
70
+ clearSchemaDependency(cls: Class): void {
71
71
  this.#mapping.delete(id(cls));
72
72
  }
73
73
 
@@ -78,7 +78,7 @@ class $SchemaChangeListener {
78
78
  * @param path The path within the object hierarchy to arrive at the class
79
79
  * @param config The configuration or the class
80
80
  */
81
- trackSchemaDependency(src: Class, parent: Class, path: FieldConfig[], config: ClassConfig) {
81
+ trackSchemaDependency(src: Class, parent: Class, path: FieldConfig[], config: ClassConfig): void {
82
82
  const idValue = id(src);
83
83
  if (!this.#mapping.has(idValue)) {
84
84
  this.#mapping.set(idValue, new Map());
@@ -91,7 +91,7 @@ class $SchemaChangeListener {
91
91
  * @param cls The class of the event
92
92
  * @param changes The changes to send
93
93
  */
94
- emitSchemaChanges({ cls, changes }: FieldChangeEvent) {
94
+ emitSchemaChanges({ cls, changes }: FieldChangeEvent): void {
95
95
  const updates = new Map<string, SchemaChange>();
96
96
  const clsId = id(cls);
97
97
 
@@ -116,7 +116,7 @@ class $SchemaChangeListener {
116
116
  * @param prev The previous class config
117
117
  * @param curr The current class config
118
118
  */
119
- emitFieldChanges({ prev, curr }: ChangeEvent<ClassConfig>) {
119
+ emitFieldChanges({ prev, curr }: ChangeEvent<ClassConfig>): void {
120
120
 
121
121
  const prevView = prev?.views[AllViewⲐ] || { fields: [], schema: {} };
122
122
  const currView = curr!.views[AllViewⲐ];
@@ -139,7 +139,7 @@ class $SchemaChangeListener {
139
139
  }
140
140
 
141
141
  // Handle class references changing, but keeping same id
142
- const compareTypes = (a: Class, b: Class) => 'ᚕid' in a ? a.ᚕid === b.ᚕid : a === b;
142
+ const compareTypes = (a: Class, b: Class): boolean => 'ᚕid' in a ? a.ᚕid === b.ᚕid : a === b;
143
143
 
144
144
  for (const c of currFields) {
145
145
  if (prevFields.has(c)) {
@@ -1,12 +1,13 @@
1
- import { Class, AppError, Util } from '@travetto/base';
1
+ import { Class, AppError, Util, ClassInstance } from '@travetto/base';
2
2
  import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
3
3
 
4
- import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig } from './types';
4
+ import { ClassList, FieldConfig, ClassConfig, SchemaConfig, ViewFieldsConfig, ViewConfig } from './types';
5
5
  import { SchemaChangeListener } from './changes';
6
6
  import { BindUtil } from '../bind-util';
7
7
  import { AllViewⲐ } from '../internal/types';
8
8
 
9
9
  function hasType<T>(o: unknown): o is { type: Class<T> | string } {
10
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
11
  return !!o && !Util.isPrimitive(o) && 'type' in (o as object) && !!(o as Record<string, string>)['type'];
11
12
  }
12
13
 
@@ -24,31 +25,31 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
24
25
  super(RootRegistry);
25
26
  }
26
27
 
27
- #computeSubTypeName(cls: Class) {
28
+ #computeSubTypeName(cls: Class): string {
28
29
  if (!this.#typeKeys.has(cls)) {
29
30
  this.#typeKeys.set(cls, cls.name
30
31
  .replace(/([A-Z])([A-Z][a-z])/g, (all, l, r) => `${l}_${r.toLowerCase()}`)
31
32
  .replace(/([a-z]|\b)([A-Z])/g, (all, l, r) => l ? `${l}_${r.toLowerCase()}` : r.toLowerCase())
32
33
  .toLowerCase());
33
34
  }
34
- return this.#typeKeys.get(cls);
35
+ return this.#typeKeys.get(cls)!;
35
36
  }
36
37
 
37
38
  /**
38
39
  * Get subtype name for a class
39
40
  * @param cls Base class
40
41
  */
41
- getSubTypeName(cls: Class) {
42
+ getSubTypeName(cls: Class): string | undefined {
42
43
  if (this.get(cls).subType) {
43
44
  return this.#computeSubTypeName(cls);
44
45
  }
45
46
  }
46
47
 
47
48
  /**
48
- * Ensure type is set properl
49
+ * Ensure type is set properly
49
50
  */
50
- ensureInstanceTypeField<T>(cls: Class, o: T) {
51
- const withType = (o as { type?: string });
51
+ ensureInstanceTypeField<T>(cls: Class, o: T): void {
52
+ const withType: { type?: string } = o;
52
53
  if (this.get(cls)?.subType && 'type' in this.get(cls).views[AllViewⲐ].schema && !withType.type) { // Do we have a type field defined
53
54
  withType.type = this.#computeSubTypeName(cls); // Assign if missing
54
55
  }
@@ -59,8 +60,9 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
59
60
  * @param cls Class for instance
60
61
  * @param o Actual instance
61
62
  */
62
- resolveSubTypeForInstance<T>(cls: Class<T>, o: T) {
63
- return this.resolveSubType(cls, hasType<T>(o) ? o.type : (o as unknown as { constructor: Class<T> }).constructor);
63
+ resolveSubTypeForInstance<T>(cls: Class<T>, o: T): Class {
64
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
+ return this.resolveSubType(cls, hasType<T>(o) ? o.type : (o as unknown as ClassInstance<T>).constructor);
64
66
  }
65
67
 
66
68
  /**
@@ -88,7 +90,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
88
90
  * @param cls The class to register against
89
91
  * @param type The subtype name
90
92
  */
91
- registerSubTypes(cls: Class, type?: string) {
93
+ registerSubTypes(cls: Class, type?: string): string {
92
94
  // Mark as subtype
93
95
  (this.get(cls) ?? this.getOrCreatePending(cls)).subType = true;
94
96
 
@@ -118,7 +120,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
118
120
  * @param curr The new class
119
121
  * @param path The path within the object hierarchy
120
122
  */
121
- trackSchemaDependencies(cls: Class, curr: Class = cls, path: FieldConfig[] = []) {
123
+ trackSchemaDependencies(cls: Class, curr: Class = cls, path: FieldConfig[] = []): void {
122
124
  const config = this.get(curr);
123
125
 
124
126
  SchemaChangeListener.trackSchemaDependency(curr, cls, path, this.get(cls));
@@ -132,7 +134,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
132
134
  }
133
135
  }
134
136
 
135
- createPending(cls: Class) {
137
+ createPending(cls: Class): ClassConfig {
136
138
  return {
137
139
  class: cls,
138
140
  validators: [],
@@ -149,16 +151,16 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
149
151
  /**
150
152
  * Get schema for a given view
151
153
  * @param cls The class to retrieve the schema for
152
- * @param view Thee view name
154
+ * @param view The view name
153
155
  */
154
- getViewSchema<T>(cls: Class<T>, view?: string | typeof AllViewⲐ) {
156
+ getViewSchema<T>(cls: Class<T>, view?: string | typeof AllViewⲐ): ViewConfig {
155
157
  view = view ?? AllViewⲐ;
156
158
 
157
- const schm = this.get(cls)!;
158
- if (!schm) {
159
+ const schema = this.get(cls)!;
160
+ if (!schema) {
159
161
  throw new Error(`Unknown schema class ${cls.name}`);
160
162
  }
161
- const res = schm.views[view];
163
+ const res = schema.views[view];
162
164
  if (!res) {
163
165
  throw new Error(`Unknown view ${view.toString()} for ${cls.name}`);
164
166
  }
@@ -196,8 +198,8 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
196
198
  * @param params
197
199
  * @returns
198
200
  */
199
- coereceMethodParams<T>(cls: Class<T>, method: string, params: unknown[], applyDefaults = false): unknown[] {
200
- return BindUtil.coereceFields(this.getMethodSchema(cls, method), params, applyDefaults);
201
+ coerceMethodParams<T>(cls: Class<T>, method: string, params: unknown[], applyDefaults = false): unknown[] {
202
+ return BindUtil.coerceFields(this.getMethodSchema(cls, method), params, applyDefaults);
201
203
  }
202
204
 
203
205
  /**
@@ -206,11 +208,13 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
206
208
  * @param view View name
207
209
  * @param fields Fields to register
208
210
  */
209
- registerPendingView<T>(target: Class<T>, view: string, fields: ViewFieldsConfig<T>) {
211
+ registerPendingView<T>(target: Class<T>, view: string, fields: ViewFieldsConfig<T>): void {
210
212
  if (!this.#pendingViews.has(target)) {
211
213
  this.#pendingViews.set(target, new Map());
212
214
  }
213
- this.#pendingViews.get(target)!.set(view, fields as ViewFieldsConfig<unknown>);
215
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
216
+ const generalConfig = fields as unknown as ViewFieldsConfig<unknown>;
217
+ this.#pendingViews.get(target)!.set(view, generalConfig);
214
218
  }
215
219
 
216
220
  /**
@@ -220,7 +224,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
220
224
  * @param idx The param index
221
225
  * @param config The config to register
222
226
  */
223
- registerPendingParamFacet(target: Class, prop: string, idx: number, config: Partial<FieldConfig>) {
227
+ registerPendingParamFacet(target: Class, prop: string, idx: number, config: Partial<FieldConfig>): Class {
224
228
  config.index = idx;
225
229
  return this.registerPendingFieldFacet(target, `${prop}.${idx}`, config);
226
230
  }
@@ -231,11 +235,13 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
231
235
  * @param prop The property name
232
236
  * @param config The config to register
233
237
  */
234
- registerPendingFieldFacet(target: Class, prop: string, config: Partial<FieldConfig>) {
238
+ registerPendingFieldFacet(target: Class, prop: string, config: Partial<FieldConfig>): Class {
235
239
  const allViewConf = this.getOrCreatePending(target).views![AllViewⲐ];
236
240
 
237
241
  if (!allViewConf.schema[prop]) {
238
242
  allViewConf.fields.push(prop);
243
+ // Partial config while building
244
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
239
245
  allViewConf.schema[prop] = {} as FieldConfig;
240
246
  }
241
247
 
@@ -252,7 +258,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
252
258
  * @param type List of types
253
259
  * @param conf Extra config
254
260
  */
255
- registerPendingParamConfig(target: Class, method: string, idx: number, type: ClassList, conf?: Partial<FieldConfig>) {
261
+ registerPendingParamConfig(target: Class, method: string, idx: number, type: ClassList, conf?: Partial<FieldConfig>): Class {
256
262
  conf ??= {};
257
263
  conf.index = idx;
258
264
  return this.registerPendingFieldConfig(target, `${method}.${idx}`, type, conf);
@@ -265,7 +271,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
265
271
  * @param type List of types
266
272
  * @param conf Extra config
267
273
  */
268
- registerPendingFieldConfig(target: Class, prop: string, type: ClassList, conf?: Partial<FieldConfig>) {
274
+ registerPendingFieldConfig(target: Class, prop: string, type: ClassList, conf?: Partial<FieldConfig>): Class {
269
275
  const fieldConf: FieldConfig = {
270
276
  owner: target,
271
277
  name: prop,
@@ -282,14 +288,14 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
282
288
  * @param dest Target config
283
289
  * @param src Source config
284
290
  */
285
- mergeConfigs(dest: ClassConfig, src: ClassConfig) {
291
+ mergeConfigs(dest: ClassConfig, src: Partial<ClassConfig>): ClassConfig {
286
292
  dest.views[AllViewⲐ] = {
287
- schema: { ...dest.views[AllViewⲐ].schema, ...src.views[AllViewⲐ].schema },
288
- fields: [...dest.views[AllViewⲐ].fields, ...src.views[AllViewⲐ].fields]
293
+ schema: { ...dest.views[AllViewⲐ].schema, ...src.views?.[AllViewⲐ].schema },
294
+ fields: [...dest.views[AllViewⲐ].fields, ...src.views?.[AllViewⲐ].fields ?? []]
289
295
  };
290
296
  dest.subType = src.subType || dest.subType;
291
297
  dest.title = src.title || dest.title;
292
- dest.validators = [...src.validators, ...dest.validators];
298
+ dest.validators = [...src.validators ?? [], ...dest.validators];
293
299
  return dest;
294
300
  }
295
301
 
@@ -298,32 +304,32 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
298
304
  * @param target The target class
299
305
  * @param conf The class config
300
306
  */
301
- finalizeViews<T>(target: Class<T>, conf: ClassConfig) {
307
+ finalizeViews<T>(target: Class<T>, conf: ClassConfig): ClassConfig {
302
308
  const allViewConf = conf.views![AllViewⲐ];
303
- const pending = this.#pendingViews.get(target) ?? new Map<string, ViewFieldsConfig<unknown>>();
309
+ const pending = this.#pendingViews.get(target) ?? new Map<string, ViewFieldsConfig<string>>();
304
310
  this.#pendingViews.delete(target);
305
311
 
306
312
  for (const [view, fields] of pending.entries()) {
307
- const withoutSet = 'without' in fields ? new Set(fields.without as string[]) : undefined;
313
+ const withoutSet = 'without' in fields ? new Set<string>(fields.without) : undefined;
308
314
  const fieldList = withoutSet ?
309
315
  allViewConf.fields.filter(x => !withoutSet.has(x)) :
310
- ('with' in fields ? fields.with as string[] : []);
316
+ ('with' in fields ? fields.with : []);
311
317
 
312
318
  conf.views![view] = {
313
- fields: fieldList as string[],
314
- schema: fieldList.reduce((acc, v) => {
319
+ fields: fieldList,
320
+ schema: fieldList.reduce<SchemaConfig>((acc, v) => {
315
321
  acc[v] = allViewConf.schema[v];
316
322
  return acc;
317
- }, {} as SchemaConfig)
323
+ }, {})
318
324
  };
319
325
  }
320
326
 
321
327
  return conf;
322
328
  }
323
329
 
324
- onInstallFinalize(cls: Class) {
330
+ onInstallFinalize(cls: Class): ClassConfig {
325
331
 
326
- let config: ClassConfig = this.createPending(cls) as ClassConfig;
332
+ let config: ClassConfig = this.createPending(cls);
327
333
 
328
334
  // Merge parent
329
335
  const parent = this.getParentClass(cls);
@@ -339,7 +345,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
339
345
  // Merge pending, back on top, to allow child to have higher precedence
340
346
  const pending = this.getOrCreatePending(cls);
341
347
  if (pending) {
342
- config = this.mergeConfigs(config, pending as ClassConfig);
348
+ config = this.mergeConfigs(config, pending);
343
349
  }
344
350
 
345
351
  // Write views out
@@ -348,7 +354,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
348
354
  return config;
349
355
  }
350
356
 
351
- override onInstall(cls: Class, e: ChangeEvent<Class>) {
357
+ override onInstall(cls: Class, e: ChangeEvent<Class>): void {
352
358
  super.onInstall(cls, e);
353
359
 
354
360
  if (this.has(cls)) { // Track dependencies of schemas
@@ -356,7 +362,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
356
362
  }
357
363
  }
358
364
 
359
- override onUninstall<T>(cls: Class<T>, e: ChangeEvent<Class>) {
365
+ override onUninstall<T>(cls: Class<T>, e: ChangeEvent<Class>): void {
360
366
  super.onUninstall(cls, e);
361
367
  if (e.type === 'removing' && this.hasExpired(cls)) {
362
368
  // Recompute subtypes
@@ -374,7 +380,7 @@ class $SchemaRegistry extends MetadataRegistry<ClassConfig, FieldConfig> {
374
380
  }
375
381
  }
376
382
 
377
- override emit(ev: ChangeEvent<Class>) {
383
+ override emit(ev: ChangeEvent<Class>): void {
378
384
  super.emit(ev);
379
385
  if (ev.type === 'changed') {
380
386
  SchemaChangeListener.emitFieldChanges({
@@ -112,7 +112,7 @@ export interface FieldConfig extends DescribableConfig {
112
112
  /**
113
113
  * The numeric precision
114
114
  */
115
- precision?: [number, number] | [number, undefined];
115
+ precision?: [number, number | undefined];
116
116
  /**
117
117
  * Is the field required
118
118
  */
@@ -151,4 +151,4 @@ export interface FieldConfig extends DescribableConfig {
151
151
  access?: 'readonly' | 'writeonly';
152
152
  }
153
153
 
154
- export type ViewFieldsConfig<T> = { with: (keyof T)[] } | { without: (keyof T)[] };
154
+ export type ViewFieldsConfig<T> = { with: Extract<(keyof T), string>[] } | { without: Extract<(keyof T), string>[] };
package/src/typings.d.ts CHANGED
@@ -17,4 +17,11 @@ declare global {
17
17
  */
18
18
  interface ReadableStream { }
19
19
  }
20
- }
20
+ }
21
+
22
+ declare module 'stream' {
23
+ /**
24
+ * @concrete stream:Readable:node
25
+ */
26
+ interface Readable { }
27
+ }
@@ -20,4 +20,8 @@ export class TypeMismatchError extends AppError {
20
20
  constructor(cls: Class | string, type: string) {
21
21
  super(`Expected ${typeof cls === 'string' ? cls : cls.name} but found ${type}`, 'data');
22
22
  }
23
+ }
24
+
25
+ export function isValidationError(err: unknown): err is ValidationError {
26
+ return !!err && err instanceof Error && 'path' in err;
23
27
  }
@@ -18,6 +18,7 @@ export const CommonRegExp = {
18
18
  };
19
19
 
20
20
  // Rebind regexes
21
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
21
22
  for (const k of Object.keys(CommonRegExp) as (keyof typeof CommonRegExp)[]) {
22
23
  CommonRegExp[k].name = `[[:${k}:]]`;
23
24
  Messages.set(CommonRegExp[k].name!, Messages.get(k)!);
@@ -1,4 +1,5 @@
1
- export type ValidationKind = 'required' | 'match' | 'min' | 'max' | 'minlength' | 'maxlength' | 'enum' | 'type' | string;
1
+ export type ValidationKindCore = 'required' | 'match' | 'min' | 'max' | 'minlength' | 'maxlength' | 'enum' | 'type';
2
+ export type ValidationKind = ValidationKindCore | string;
2
3
 
3
4
  /**
4
5
  * Individual Validation Error