@jarenjs/validate 0.8.4 → 0.34.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.
Files changed (53) hide show
  1. package/ARCHITECTURE.md +1131 -0
  2. package/LICENSE +21 -0
  3. package/README.md +796 -2
  4. package/dist/types/array.d.ts +2 -0
  5. package/dist/types/bigint.d.ts +1 -0
  6. package/dist/types/combine.d.ts +1 -0
  7. package/dist/types/condition.d.ts +1 -0
  8. package/dist/types/content.d.ts +3 -0
  9. package/dist/types/data.d.ts +7 -0
  10. package/dist/types/dollar-data.d.ts +11 -0
  11. package/dist/types/dynamic-ref.d.ts +44 -0
  12. package/dist/types/enum.d.ts +1 -0
  13. package/dist/types/format.d.ts +21 -0
  14. package/dist/types/index.d.ts +972 -0
  15. package/dist/types/messages.d.ts +142 -0
  16. package/dist/types/normalize.d.ts +107 -0
  17. package/dist/types/number.d.ts +1 -0
  18. package/dist/types/object.d.ts +3 -0
  19. package/dist/types/query-keyword.d.ts +19 -0
  20. package/dist/types/query.d.ts +29 -0
  21. package/dist/types/schema.d.ts +1 -0
  22. package/dist/types/string.d.ts +1 -0
  23. package/dist/types/tools.d.ts +109 -0
  24. package/dist/types/traverse.d.ts +32 -0
  25. package/dist/types/unevaluated.d.ts +12 -0
  26. package/docs/ERROR-MESSAGES.md +251 -0
  27. package/package.json +37 -7
  28. package/src/array.js +610 -0
  29. package/src/bigint.js +108 -0
  30. package/src/combine.js +276 -0
  31. package/src/condition.js +129 -0
  32. package/src/content.js +83 -0
  33. package/src/data.js +101 -0
  34. package/src/dollar-data.js +212 -0
  35. package/src/dynamic-ref.js +121 -0
  36. package/src/enum.js +147 -0
  37. package/src/format.js +108 -0
  38. package/src/index.js +1896 -0
  39. package/src/messages.js +497 -0
  40. package/src/normalize.js +585 -0
  41. package/src/number.js +169 -0
  42. package/src/object.js +848 -0
  43. package/src/query-keyword.js +99 -0
  44. package/src/query.js +85 -0
  45. package/src/schema.js +690 -0
  46. package/src/string.js +164 -0
  47. package/src/tools.js +397 -0
  48. package/src/traverse.js +442 -0
  49. package/src/unevaluated.js +173 -0
  50. package/dist/index.js +0 -1998
  51. package/dist/index.js.map +0 -7
  52. package/dist/index.min.js +0 -2
  53. package/dist/index.min.js.map +0 -7
package/src/string.js ADDED
@@ -0,0 +1,164 @@
1
+ //@ts-check
2
+
3
+ import {
4
+ isStringType,
5
+ } from '@jarenjs/core';
6
+
7
+ import {
8
+ getIntishType,
9
+ } from '@jarenjs/core/number';
10
+
11
+ import {
12
+ createRegExp,
13
+ getStringLength,
14
+ } from '@jarenjs/core/string';
15
+
16
+ import {
17
+ trueThat,
18
+ } from '@jarenjs/core/function';
19
+
20
+ function compileMinLength(schemaObj, jsonSchema) {
21
+ const min = getIntishType(jsonSchema.minLength) || 0;
22
+ if (min < 1) return undefined;
23
+
24
+ const addError = schemaObj.createErrorHandler(min, 'minLength');
25
+
26
+ return function validateIsMinLength(len = 0, dataPath) {
27
+ return len >= min
28
+ || addError(len, dataPath);
29
+ };
30
+ }
31
+
32
+ function compileMaxLength(schemaObj, jsonSchema) {
33
+ const max = getIntishType(jsonSchema.maxLength) || -1;
34
+ if (max < 0) return undefined;
35
+
36
+ const addError = schemaObj.createErrorHandler(max, 'maxLength');
37
+
38
+ return function validateIsMaxLength(len = 0, dataPath) {
39
+ return len <= max
40
+ || addError(len, dataPath);
41
+ };
42
+ }
43
+
44
+ function compilePattern(schemaObj, jsonSchema) {
45
+ // Skip if pattern is a $data reference object (has $data property)
46
+ if (jsonSchema.pattern && typeof jsonSchema.pattern === 'object' && jsonSchema.pattern.$data) return undefined;
47
+
48
+ const pattern = createRegExp(jsonSchema.pattern);
49
+ if (pattern == null) return undefined;
50
+
51
+ const addError = schemaObj.createErrorHandler(pattern, 'pattern');
52
+
53
+ return function validateIsMatch(str = '', dataPath) {
54
+ return pattern.test(str)
55
+ || addError(str, dataPath);
56
+ };
57
+ }
58
+
59
+ function compileStringIntern(schemaObj, jsonSchema) {
60
+ const minLength = compileMinLength(schemaObj, jsonSchema);
61
+ const maxLength = compileMaxLength(schemaObj, jsonSchema);
62
+ const pattern = compilePattern(schemaObj, jsonSchema);
63
+
64
+ if ((minLength || maxLength || pattern) == null) return undefined;
65
+
66
+ const isMinLength = minLength || trueThat;
67
+ const isMaxLength = maxLength || trueThat;
68
+ const isMatch = pattern || trueThat;
69
+ const useGrapheme = schemaObj.options.useGrapheme;
70
+
71
+ if (schemaObj.options.skipErrors) {
72
+ return function validateStringIntern(data, dataPath) {
73
+ const len = getStringLength(data, useGrapheme);
74
+ return isMinLength(len, dataPath)
75
+ && isMaxLength(len, dataPath)
76
+ && isMatch(data, dataPath);
77
+ };
78
+ }
79
+
80
+ // Length and pattern are independent assertions over the same string:
81
+ // `len` is computed before any of them and `isMatch` reads the raw data,
82
+ // so a failed `minLength` says nothing about whether `pattern` holds.
83
+ // Short-circuiting them is a boolean-mode optimization; when errors are
84
+ // recorded it would hide half the reasons the value is wrong.
85
+ return function validateStringInternAll(data, dataPath) {
86
+ const len = getStringLength(data, useGrapheme);
87
+ let valid = isMinLength(len, dataPath);
88
+ valid = isMaxLength(len, dataPath) && valid;
89
+ return isMatch(data, dataPath) && valid;
90
+ };
91
+ }
92
+
93
+ export function compileStringBasic(schemaObj, jsonSchema) {
94
+ const intern = compileStringIntern(schemaObj, jsonSchema);
95
+ if (intern == null) return undefined;
96
+
97
+ // Fast path for simple maxLength-only schemas (most common case)
98
+ // This inlines the validation to reduce function call overhead
99
+ const max = getIntishType(jsonSchema.maxLength) ?? -1;
100
+ const min = getIntishType(jsonSchema.minLength) || 0;
101
+ const hasPattern = jsonSchema.pattern != null;
102
+ const useGrapheme = schemaObj.options.useGrapheme;
103
+
104
+ if (!hasPattern) {
105
+ if (!useGrapheme) {
106
+ // Simple maxLength-only without grapheme counting
107
+ if (max >= 0 && min < 1) {
108
+ const addError = schemaObj.createErrorHandler(max, 'maxLength');
109
+ return function validateStringMaxLength(data, dataPath) {
110
+ if (!isStringType(data)) return true;
111
+ return data.length <= max || addError(data.length, dataPath);
112
+ };
113
+ }
114
+
115
+ // Simple minLength-only without grapheme counting
116
+ if (min > 0 && max < 0) {
117
+ const addError = schemaObj.createErrorHandler(min, 'minLength');
118
+ return function validateStringMinLength(data, dataPath) {
119
+ if (!isStringType(data)) return true;
120
+ return data.length >= min || addError(data.length, dataPath);
121
+ };
122
+ }
123
+ } else {
124
+ // Fast path WITH grapheme counting - inline the ASCII fast-path logic
125
+ // This avoids the function call overhead of getStringLength for ASCII strings
126
+ if (max >= 0 && min < 1) {
127
+ const addError = schemaObj.createErrorHandler(max, 'maxLength');
128
+ return function validateStringMaxLengthGrapheme(data, dataPath) {
129
+ if (!isStringType(data)) return true;
130
+ const len = getStringLength(data, true);
131
+ return len <= max || addError(len, dataPath);
132
+ };
133
+ }
134
+
135
+ if (min > 0 && max < 0) {
136
+ const addError = schemaObj.createErrorHandler(min, 'minLength');
137
+ return function validateStringMinLengthGrapheme(data, dataPath) {
138
+ if (!isStringType(data)) return true;
139
+ const len = getStringLength(data, true);
140
+ return len >= min || addError(len, dataPath);
141
+ };
142
+ }
143
+ }
144
+ }
145
+
146
+ // Fast path for pattern-only schemas (common in ecmascript-regex tests)
147
+ // This eliminates the intermediate function call overhead
148
+ if (hasPattern && max < 0 && min < 1) {
149
+ const pattern = createRegExp(jsonSchema.pattern);
150
+ if (pattern != null) {
151
+ const addError = schemaObj.createErrorHandler(pattern, 'pattern');
152
+ return function validateStringPatternOnly(data, dataPath) {
153
+ if (!isStringType(data)) return true;
154
+ return pattern.test(data) || addError(data, dataPath);
155
+ };
156
+ }
157
+ }
158
+
159
+ // Generic case
160
+ return function validateStringBasic(data, dataPath) {
161
+ return !isStringType(data)
162
+ || intern(data, dataPath);
163
+ };
164
+ }
package/src/tools.js ADDED
@@ -0,0 +1,397 @@
1
+ //@ts-check
2
+
3
+ import {
4
+ isBigIntType,
5
+ isNullValue,
6
+ isBooleanType,
7
+ isIntegerType,
8
+ isNumberType,
9
+ isStringType,
10
+ isObjectClass,
11
+ isObjectType,
12
+ isMapClass,
13
+ isArrayClass,
14
+ isSetClass,
15
+ } from '@jarenjs/core';
16
+
17
+ import {
18
+ isRegExpType,
19
+ isStringWhiteSpace,
20
+ } from '@jarenjs/core/string';
21
+
22
+ import {
23
+ isArrayish,
24
+ } from '@jarenjs/core/array';
25
+
26
+ import {
27
+ isJsonObject,
28
+ } from '@jarenjs/core/object';
29
+
30
+ import {
31
+ JSONPOINTER_NOTHING,
32
+ } from '@jarenjs/json';
33
+
34
+ //#region Object
35
+ export function isBoolOrObjectClass(obj) {
36
+ return isBooleanType(obj)
37
+ || isObjectClass(obj);
38
+ }
39
+
40
+ /**
41
+ * getBoolOrObjectClass
42
+ * extract the data of first parameter if data is boolean or object type otherwise return default
43
+ * @param {any} obj any data data has to be tested on boolean or object type
44
+ * @param {boolean | object | undefined} def default return type if not boolean or object type
45
+ * @returns {boolean | undefined} return value when boolean or object otherwise def
46
+ */
47
+ export function getBoolOrObjectClass(obj, def = undefined) {
48
+ return isBoolOrObjectClass(obj) ? obj : def;
49
+ }
50
+
51
+ export function getArrayClassMinItems(obj, len = 1, def = undefined) {
52
+ return (isArrayClass(obj) && obj.length >= len && obj) || def;
53
+ }
54
+ //#endregion
55
+
56
+ //#region Schema Helpers
57
+ export function isOfSchemaType(schema, type) {
58
+ const stype = schema.type;
59
+ if (stype == null) return false;
60
+ if (stype === type) return true;
61
+ if (stype.constructor === Array) {
62
+ return stype.includes(type);
63
+ }
64
+ if (stype.constructor === Set) {
65
+ return stype.has(type);
66
+ }
67
+ return false;
68
+ }
69
+
70
+ export function hasSchemaRef(schema) {
71
+ return isObjectClass(schema)
72
+ && isStringType(schema.$ref)
73
+ && !isStringWhiteSpace(schema.$ref);
74
+ }
75
+
76
+ export function hasSchemaRecursiveRef(schema) {
77
+ return isObjectClass(schema)
78
+ && isStringType(schema.$recursiveRef)
79
+ && !isStringWhiteSpace(schema.$recursiveRef);
80
+ }
81
+
82
+ export function hasSchemaDynamicRef(schema) {
83
+ return isObjectClass(schema)
84
+ && isStringType(schema.$dynamicRef)
85
+ && !isStringWhiteSpace(schema.$dynamicRef);
86
+ }
87
+
88
+ /**
89
+ * Whether a sibling keyword of unevaluatedProperties already evaluates every
90
+ * property of the instance. additionalProperties (boolean or schema) applies
91
+ * to each property not matched by properties/patternProperties, so once it
92
+ * has passed no property is left unevaluated.
93
+ * @param {object} schema - The schema holding the unevaluatedProperties keyword
94
+ * @returns {boolean} True when the unevaluatedProperties check can never match
95
+ */
96
+ export function hasUnevaluatedPropertiesCoverage(schema) {
97
+ return isBoolOrObjectClass(schema.additionalProperties);
98
+ }
99
+
100
+ /**
101
+ * Whether a sibling keyword of unevaluatedItems already evaluates every item
102
+ * of the instance: a uniform items schema (boolean or object) covers all
103
+ * items beyond any prefixItems, and a tuple-form items with additionalItems
104
+ * covers the items beyond the tuple.
105
+ * @param {object} schema - The schema holding the unevaluatedItems keyword
106
+ * @returns {boolean} True when the unevaluatedItems check can never match
107
+ */
108
+ export function hasUnevaluatedItemsCoverage(schema) {
109
+ const items = schema.items;
110
+ if (isBoolOrObjectClass(items)) return true;
111
+ return isArrayClass(items) && isBoolOrObjectClass(schema.additionalItems);
112
+ }
113
+
114
+ export function createIsSchemaTypeHandler(type, isStrict = false) {
115
+ switch (type) {
116
+ case 'null': return isNullValue;
117
+ case 'boolean': return isBooleanType;
118
+ case 'integer': return isIntegerType;
119
+ case 'bigint': return isBigIntType;
120
+ case 'number': return isNumberType;
121
+ case 'string': return isStringType;
122
+ case 'object': return isStrict
123
+ ? isObjectClass
124
+ : isObjectType;
125
+ case 'array': return isStrict
126
+ ? isArrayish
127
+ : isArrayClass;
128
+ case 'set': return isSetClass;
129
+ case 'map': return isMapClass;
130
+ case 'tuple': return isArrayClass;
131
+ case 'regex': return isRegExpType;
132
+ default: break;
133
+ }
134
+
135
+ if (type === null)
136
+ return isNullValue;
137
+
138
+ if (typeof type === 'function')
139
+ throw new Error('This is interesting!');
140
+
141
+ return undefined;
142
+ }
143
+
144
+ //#endregion
145
+
146
+ //#region Data references
147
+
148
+ /**
149
+ * The fallback resolver of the data-reference keywords (`data`, `$data`):
150
+ * a ref that fails the strict compile keeps the lax keyword semantics, so
151
+ * it resolves as not-found and the keyword asserts nothing.
152
+ * @returns {any} the JSON Pointer not-found sentinel
153
+ */
154
+ export const resolveNothing = () => JSONPOINTER_NOTHING;
155
+
156
+ const isDefined = (data) => data !== undefined;
157
+ const isJsonString = (data) => typeof data === 'string';
158
+ const isAnyValue = () => true;
159
+
160
+ /**
161
+ * Build the keyword validators the two data-reference keywords share
162
+ * verbatim. The `data` keyword (json-everything, absolute + relative
163
+ * pointers via `compileDataRef`) and the Ajv-style `$data` keyword
164
+ * (relative pointers only) differ ONLY in which pointer compiler
165
+ * resolves a ref, so each module passes its own `compileRefResolver`
166
+ * and gets the same fifteen compilers back.
167
+ *
168
+ * Every validator follows one lax contract: a data instance outside the
169
+ * keyword's type, an unresolvable ref, or a resolved constraint of the
170
+ * wrong type asserts nothing.
171
+ *
172
+ * @param {(ref: string) => (dataRoot: any, dataPath: string) => any} compileRefResolver
173
+ * @returns {Record<string, (schemaObj: object, ref: string) => ((data: any, dataPath: string, dataRoot: any) => boolean) | undefined>}
174
+ */
175
+ export function createDataRefCompilers(compileRefResolver) {
176
+ /**
177
+ * @param {string} keyword
178
+ * @param {(data: any) => boolean} accepts - Instance types the keyword constrains
179
+ * @param {(constraint: any) => boolean} expects - Resolved constraint types that assert
180
+ * @param {(data: any, constraint: any) => boolean} isValid
181
+ */
182
+ const constraint = (keyword, accepts, expects, isValid) =>
183
+ (schemaObj, ref) => {
184
+ const addError = schemaObj.createErrorHandler(ref, keyword);
185
+ const resolveRef = compileRefResolver(ref);
186
+
187
+ return function validateDataRefConstraint(data, dataPath, dataRoot) {
188
+ if (!accepts(data)) return true;
189
+
190
+ const value = resolveRef(dataRoot, dataPath);
191
+ if (value === JSONPOINTER_NOTHING || !expects(value)) return true;
192
+
193
+ return isValid(data, value) || addError(data, dataPath, value);
194
+ };
195
+ };
196
+
197
+ const compileFormat = (schemaObj, ref) => {
198
+ const formats = schemaObj.formats;
199
+ if (!formats) return undefined;
200
+
201
+ const addError = schemaObj.createErrorHandler(ref, 'format');
202
+ const resolveRef = compileRefResolver(ref);
203
+
204
+ // The registry holds format COMPILERS; compile (and cache) a validator
205
+ // per referenced format name at validation time.
206
+ const compiled = new Map();
207
+ const mockSchemaObj = {
208
+ createErrorHandler: () => () => false,
209
+ options: { skipErrors: true },
210
+ };
211
+
212
+ return function validateDataRefFormat(data, dataPath, dataRoot) {
213
+ if (typeof data !== 'string') return true;
214
+
215
+ const formatName = resolveRef(dataRoot, dataPath);
216
+ if (formatName === JSONPOINTER_NOTHING || !isStringType(formatName)) return true;
217
+
218
+ let validator = compiled.get(formatName);
219
+ if (validator === undefined) {
220
+ const formatCompiler = formats[formatName];
221
+ validator = null;
222
+ if (formatCompiler) {
223
+ try {
224
+ const candidate = formatCompiler(mockSchemaObj, { format: formatName });
225
+ if (typeof candidate === 'function') validator = candidate;
226
+ } catch (_e) {
227
+ // An uncompilable format asserts nothing
228
+ }
229
+ }
230
+ compiled.set(formatName, validator);
231
+ }
232
+ if (validator === null) return true;
233
+
234
+ return validator(data, dataPath) || addError(data, dataPath, formatName);
235
+ };
236
+ };
237
+
238
+ return {
239
+ __proto__: null,
240
+ minimum: constraint('minimum', isNumberType, isNumberType,
241
+ (data, min) => data >= min),
242
+ maximum: constraint('maximum', isNumberType, isNumberType,
243
+ (data, max) => data <= max),
244
+ exclusiveMinimum: constraint('exclusiveMinimum', isNumberType, isNumberType,
245
+ (data, min) => data > min),
246
+ exclusiveMaximum: constraint('exclusiveMaximum', isNumberType, isNumberType,
247
+ (data, max) => data < max),
248
+ multipleOf: constraint('multipleOf', isNumberType, isNumberType,
249
+ (data, multipleOf) => {
250
+ const q = data / multipleOf;
251
+ return Math.abs(q - Math.round(q)) < 1e-6;
252
+ }),
253
+ minLength: constraint('minLength', isJsonString, isNumberType,
254
+ (data, min) => data.length >= min),
255
+ maxLength: constraint('maxLength', isJsonString, isNumberType,
256
+ (data, max) => data.length <= max),
257
+ pattern: constraint('pattern', isJsonString, isStringType,
258
+ (data, pattern) => new RegExp(pattern, 'u').test(data)),
259
+ minItems: constraint('minItems', Array.isArray, isNumberType,
260
+ (data, min) => data.length >= min),
261
+ maxItems: constraint('maxItems', Array.isArray, isNumberType,
262
+ (data, max) => data.length <= max),
263
+ minProperties: constraint('minProperties', isJsonObject, isNumberType,
264
+ (data, min) => Object.keys(data).length >= min),
265
+ maxProperties: constraint('maxProperties', isJsonObject, isNumberType,
266
+ (data, max) => Object.keys(data).length <= max),
267
+ enum: constraint('enum', isDefined, Array.isArray,
268
+ (data, values) => values.includes(data)),
269
+ const: constraint('const', isDefined, isAnyValue,
270
+ (data, value) => data === value),
271
+ format: compileFormat,
272
+ };
273
+ }
274
+
275
+ //#endregion
276
+
277
+ /**
278
+ * Records which properties (string keys) and items (numeric indexes) of a
279
+ * data instance were successfully evaluated during validation, so that
280
+ * unevaluatedProperties/unevaluatedItems can be checked afterwards.
281
+ *
282
+ * Entries are (data reference, key) pairs appended in application order.
283
+ * Applicators that discard annotations (failed anyOf/oneOf branches, not,
284
+ * failed if) take a mark() before running and rollback(mark) afterwards.
285
+ * The numeric key -1 means "all items of this array were evaluated".
286
+ */
287
+ export class EvalLog {
288
+ #data = [];
289
+ #keys = [];
290
+ #len = 0;
291
+
292
+ /** Clears the log; called at the start of each root validation. */
293
+ reset() {
294
+ this.#data.length = 0;
295
+ this.#keys.length = 0;
296
+ this.#len = 0;
297
+ }
298
+
299
+ /** @returns {number} The current log position */
300
+ mark() {
301
+ return this.#len;
302
+ }
303
+
304
+ /** Discards all entries recorded after the given mark. */
305
+ rollback(mark) {
306
+ this.#len = mark;
307
+ }
308
+
309
+ /** Records that `key` of instance `data` was evaluated. */
310
+ add(data, key) {
311
+ this.#data[this.#len] = data;
312
+ this.#keys[this.#len] = key;
313
+ this.#len++;
314
+ }
315
+
316
+ /** @returns {boolean} True when property `key` of `data` was evaluated at or after `from` */
317
+ hasKey(data, key, from) {
318
+ const len = this.#len;
319
+ const datas = this.#data;
320
+ const keys = this.#keys;
321
+ for (let i = from; i < len; ++i) {
322
+ if (datas[i] === data && keys[i] === key) return true;
323
+ }
324
+ return false;
325
+ }
326
+
327
+ /** @returns {boolean} True when item `index` of `data` was evaluated at or after `from` (-1 entries cover all items) */
328
+ hasItem(data, index, from) {
329
+ const len = this.#len;
330
+ const datas = this.#data;
331
+ const keys = this.#keys;
332
+ for (let i = from; i < len; ++i) {
333
+ if (datas[i] === data) {
334
+ const k = keys[i];
335
+ if (k === index || k === -1) return true;
336
+ }
337
+ }
338
+ return false;
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Combine INDEPENDENT keyword validators without short-circuiting.
344
+ *
345
+ * `a(...) && b(...)` is the right composition in boolean mode: the answer is
346
+ * known at the first failure and nothing is gained by continuing. When errors
347
+ * are recorded it is wrong, because each validator is the only thing that can
348
+ * report its own fault, so the first failure hides every sibling's. This runs
349
+ * all of them and ANDs the results — the boolean answer is identical, the
350
+ * error list is complete.
351
+ *
352
+ * Only use it where the validators genuinely are independent. A precondition
353
+ * (a type guard before a length check) must keep its short-circuit: running
354
+ * past it is meaningless at best and throws at worst.
355
+ * @param {Function[]} validators - Independent validators, in report order
356
+ * @returns {Function} A validator that runs every one of them
357
+ */
358
+ export function combineIndependent(validators) {
359
+ return function validateIndependent(data, dataPath, dataRoot, dataKey) {
360
+ let valid = true;
361
+ for (let i = 0; i < validators.length; ++i) {
362
+ if (validators[i](data, dataPath, dataRoot, dataKey) === false)
363
+ valid = false;
364
+ }
365
+ return valid;
366
+ };
367
+ }
368
+
369
+ export class ValidationResult {
370
+ static undefThat() {
371
+ return new ValidationResult();
372
+ }
373
+
374
+ constructor(match = false, errors = 0) {
375
+ this.match = match;
376
+ this.errors = Number(errors);
377
+ }
378
+
379
+ addValid(valid = true) {
380
+ if (valid === false)// this.errors += valid|0
381
+ this.errors++;
382
+ return this;
383
+ }
384
+
385
+ addMatch(valid = true) {
386
+ this.match = true;
387
+ if (valid === false)// this.errors += valid|0
388
+ this.errors++;
389
+ return this;
390
+ }
391
+
392
+ addResult(result = new ValidationResult()) {
393
+ this.match = this.match || result.match;
394
+ this.errors += result.errors;
395
+ return this;
396
+ }
397
+ }