joi 17.3.0 → 17.4.3

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/lib/validator.js CHANGED
@@ -1,650 +1,650 @@
1
- 'use strict';
2
-
3
- const Assert = require('@hapi/hoek/lib/assert');
4
- const Clone = require('@hapi/hoek/lib/clone');
5
- const Ignore = require('@hapi/hoek/lib/ignore');
6
- const Reach = require('@hapi/hoek/lib/reach');
7
-
8
- const Common = require('./common');
9
- const Errors = require('./errors');
10
- const State = require('./state');
11
-
12
-
13
- const internals = {
14
- result: Symbol('result')
15
- };
16
-
17
-
18
- exports.entry = function (value, schema, prefs) {
19
-
20
- let settings = Common.defaults;
21
- if (prefs) {
22
- Assert(prefs.warnings === undefined, 'Cannot override warnings preference in synchronous validation');
23
- Assert(prefs.artifacts === undefined, 'Cannot override artifacts preference in synchronous validation');
24
- settings = Common.preferences(Common.defaults, prefs);
25
- }
26
-
27
- const result = internals.entry(value, schema, settings);
28
- Assert(!result.mainstay.externals.length, 'Schema with external rules must use validateAsync()');
29
- const outcome = { value: result.value };
30
-
31
- if (result.error) {
32
- outcome.error = result.error;
33
- }
34
-
35
- if (result.mainstay.warnings.length) {
36
- outcome.warning = Errors.details(result.mainstay.warnings);
37
- }
38
-
39
- if (result.mainstay.debug) {
40
- outcome.debug = result.mainstay.debug;
41
- }
42
-
43
- if (result.mainstay.artifacts) {
44
- outcome.artifacts = result.mainstay.artifacts;
45
- }
46
-
47
- return outcome;
48
- };
49
-
50
-
51
- exports.entryAsync = async function (value, schema, prefs) {
52
-
53
- let settings = Common.defaults;
54
- if (prefs) {
55
- settings = Common.preferences(Common.defaults, prefs);
56
- }
57
-
58
- const result = internals.entry(value, schema, settings);
59
- const mainstay = result.mainstay;
60
- if (result.error) {
61
- if (mainstay.debug) {
62
- result.error.debug = mainstay.debug;
63
- }
64
-
65
- throw result.error;
66
- }
67
-
68
- if (mainstay.externals.length) {
69
- let root = result.value;
70
- for (const { method, path, label } of mainstay.externals) {
71
- let node = root;
72
- let key;
73
- let parent;
74
-
75
- if (path.length) {
76
- key = path[path.length - 1];
77
- parent = Reach(root, path.slice(0, -1));
78
- node = parent[key];
79
- }
80
-
81
- try {
82
- const output = await method(node, { prefs });
83
- if (output === undefined ||
84
- output === node) {
85
-
86
- continue;
87
- }
88
-
89
- if (parent) {
90
- parent[key] = output;
91
- }
92
- else {
93
- root = output;
94
- }
95
- }
96
- catch (err) {
97
- err.message += ` (${label})`; // Change message to include path
98
- throw err;
99
- }
100
- }
101
-
102
- result.value = root;
103
- }
104
-
105
- if (!settings.warnings &&
106
- !settings.debug &&
107
- !settings.artifacts) {
108
-
109
- return result.value;
110
- }
111
-
112
- const outcome = { value: result.value };
113
- if (mainstay.warnings.length) {
114
- outcome.warning = Errors.details(mainstay.warnings);
115
- }
116
-
117
- if (mainstay.debug) {
118
- outcome.debug = mainstay.debug;
119
- }
120
-
121
- if (mainstay.artifacts) {
122
- outcome.artifacts = mainstay.artifacts;
123
- }
124
-
125
- return outcome;
126
- };
127
-
128
-
129
- internals.entry = function (value, schema, prefs) {
130
-
131
- // Prepare state
132
-
133
- const { tracer, cleanup } = internals.tracer(schema, prefs);
134
- const debug = prefs.debug ? [] : null;
135
- const links = schema._ids._schemaChain ? new Map() : null;
136
- const mainstay = { externals: [], warnings: [], tracer, debug, links };
137
- const schemas = schema._ids._schemaChain ? [{ schema }] : null;
138
- const state = new State([], [], { mainstay, schemas });
139
-
140
- // Validate value
141
-
142
- const result = exports.validate(value, schema, state, prefs);
143
-
144
- // Process value and errors
145
-
146
- if (cleanup) {
147
- schema.$_root.untrace();
148
- }
149
-
150
- const error = Errors.process(result.errors, value, prefs);
151
- return { value: result.value, error, mainstay };
152
- };
153
-
154
-
155
- internals.tracer = function (schema, prefs) {
156
-
157
- if (schema.$_root._tracer) {
158
- return { tracer: schema.$_root._tracer._register(schema) };
159
- }
160
-
161
- if (prefs.debug) {
162
- Assert(schema.$_root.trace, 'Debug mode not supported');
163
- return { tracer: schema.$_root.trace()._register(schema), cleanup: true };
164
- }
165
-
166
- return { tracer: internals.ignore };
167
- };
168
-
169
-
170
- exports.validate = function (value, schema, state, prefs, overrides = {}) {
171
-
172
- if (schema.$_terms.whens) {
173
- schema = schema._generate(value, state, prefs).schema;
174
- }
175
-
176
- // Setup state and settings
177
-
178
- if (schema._preferences) {
179
- prefs = internals.prefs(schema, prefs);
180
- }
181
-
182
- // Cache
183
-
184
- if (schema._cache &&
185
- prefs.cache) {
186
-
187
- const result = schema._cache.get(value);
188
- state.mainstay.tracer.debug(state, 'validate', 'cached', !!result);
189
- if (result) {
190
- return result;
191
- }
192
- }
193
-
194
- // Helpers
195
-
196
- const createError = (code, local, localState) => schema.$_createError(code, value, local, localState || state, prefs);
197
- const helpers = {
198
- original: value,
199
- prefs,
200
- schema,
201
- state,
202
- error: createError,
203
- errorsArray: internals.errorsArray,
204
- warn: (code, local, localState) => state.mainstay.warnings.push(createError(code, local, localState)),
205
- message: (messages, local) => schema.$_createError('custom', value, local, state, prefs, { messages })
206
- };
207
-
208
- // Prepare
209
-
210
- state.mainstay.tracer.entry(schema, state);
211
-
212
- const def = schema._definition;
213
- if (def.prepare &&
214
- value !== undefined &&
215
- prefs.convert) {
216
-
217
- const prepared = def.prepare(value, helpers);
218
- if (prepared) {
219
- state.mainstay.tracer.value(state, 'prepare', value, prepared.value);
220
- if (prepared.errors) {
221
- return internals.finalize(prepared.value, [].concat(prepared.errors), helpers); // Prepare error always aborts early
222
- }
223
-
224
- value = prepared.value;
225
- }
226
- }
227
-
228
- // Type coercion
229
-
230
- if (def.coerce &&
231
- value !== undefined &&
232
- prefs.convert &&
233
- (!def.coerce.from || def.coerce.from.includes(typeof value))) {
234
-
235
- const coerced = def.coerce.method(value, helpers);
236
- if (coerced) {
237
- state.mainstay.tracer.value(state, 'coerced', value, coerced.value);
238
- if (coerced.errors) {
239
- return internals.finalize(coerced.value, [].concat(coerced.errors), helpers); // Coerce error always aborts early
240
- }
241
-
242
- value = coerced.value;
243
- }
244
- }
245
-
246
- // Empty value
247
-
248
- const empty = schema._flags.empty;
249
- if (empty &&
250
- empty.$_match(internals.trim(value, schema), state.nest(empty), Common.defaults)) {
251
-
252
- state.mainstay.tracer.value(state, 'empty', value, undefined);
253
- value = undefined;
254
- }
255
-
256
- // Presence requirements (required, optional, forbidden)
257
-
258
- const presence = overrides.presence || schema._flags.presence || (schema._flags._endedSwitch ? null : prefs.presence);
259
- if (value === undefined) {
260
- if (presence === 'forbidden') {
261
- return internals.finalize(value, null, helpers);
262
- }
263
-
264
- if (presence === 'required') {
265
- return internals.finalize(value, [schema.$_createError('any.required', value, null, state, prefs)], helpers);
266
- }
267
-
268
- if (presence === 'optional') {
269
- if (schema._flags.default !== Common.symbols.deepDefault) {
270
- return internals.finalize(value, null, helpers);
271
- }
272
-
273
- state.mainstay.tracer.value(state, 'default', value, {});
274
- value = {};
275
- }
276
- }
277
- else if (presence === 'forbidden') {
278
- return internals.finalize(value, [schema.$_createError('any.unknown', value, null, state, prefs)], helpers);
279
- }
280
-
281
- // Allowed values
282
-
283
- const errors = [];
284
-
285
- if (schema._valids) {
286
- const match = schema._valids.get(value, state, prefs, schema._flags.insensitive);
287
- if (match) {
288
- if (prefs.convert) {
289
- state.mainstay.tracer.value(state, 'valids', value, match.value);
290
- value = match.value;
291
- }
292
-
293
- state.mainstay.tracer.filter(schema, state, 'valid', match);
294
- return internals.finalize(value, null, helpers);
295
- }
296
-
297
- if (schema._flags.only) {
298
- const report = schema.$_createError('any.only', value, { valids: schema._valids.values({ display: true }) }, state, prefs);
299
- if (prefs.abortEarly) {
300
- return internals.finalize(value, [report], helpers);
301
- }
302
-
303
- errors.push(report);
304
- }
305
- }
306
-
307
- // Denied values
308
-
309
- if (schema._invalids) {
310
- const match = schema._invalids.get(value, state, prefs, schema._flags.insensitive);
311
- if (match) {
312
- state.mainstay.tracer.filter(schema, state, 'invalid', match);
313
- const report = schema.$_createError('any.invalid', value, { invalids: schema._invalids.values({ display: true }) }, state, prefs);
314
- if (prefs.abortEarly) {
315
- return internals.finalize(value, [report], helpers);
316
- }
317
-
318
- errors.push(report);
319
- }
320
- }
321
-
322
- // Base type
323
-
324
- if (def.validate) {
325
- const base = def.validate(value, helpers);
326
- if (base) {
327
- state.mainstay.tracer.value(state, 'base', value, base.value);
328
- value = base.value;
329
-
330
- if (base.errors) {
331
- if (!Array.isArray(base.errors)) {
332
- errors.push(base.errors);
333
- return internals.finalize(value, errors, helpers); // Base error always aborts early
334
- }
335
-
336
- if (base.errors.length) {
337
- errors.push(...base.errors);
338
- return internals.finalize(value, errors, helpers); // Base error always aborts early
339
- }
340
- }
341
- }
342
- }
343
-
344
- // Validate tests
345
-
346
- if (!schema._rules.length) {
347
- return internals.finalize(value, errors, helpers);
348
- }
349
-
350
- return internals.rules(value, errors, helpers);
351
- };
352
-
353
-
354
- internals.rules = function (value, errors, helpers) {
355
-
356
- const { schema, state, prefs } = helpers;
357
-
358
- for (const rule of schema._rules) {
359
- const definition = schema._definition.rules[rule.method];
360
-
361
- // Skip rules that are also applied in coerce step
362
-
363
- if (definition.convert &&
364
- prefs.convert) {
365
-
366
- state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'full');
367
- continue;
368
- }
369
-
370
- // Resolve references
371
-
372
- let ret;
373
- let args = rule.args;
374
- if (rule._resolve.length) {
375
- args = Object.assign({}, args); // Shallow copy
376
- for (const key of rule._resolve) {
377
- const resolver = definition.argsByName.get(key);
378
-
379
- const resolved = args[key].resolve(value, state, prefs);
380
- const normalized = resolver.normalize ? resolver.normalize(resolved) : resolved;
381
-
382
- const invalid = Common.validateArg(normalized, null, resolver);
383
- if (invalid) {
384
- ret = schema.$_createError('any.ref', resolved, { arg: key, ref: args[key], reason: invalid }, state, prefs);
385
- break;
386
- }
387
-
388
- args[key] = normalized;
389
- }
390
- }
391
-
392
- // Test rule
393
-
394
- ret = ret || definition.validate(value, helpers, args, rule); // Use ret if already set to reference error
395
-
396
- const result = internals.rule(ret, rule);
397
- if (result.errors) {
398
- state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'error');
399
-
400
- if (rule.warn) {
401
- state.mainstay.warnings.push(...result.errors);
402
- continue;
403
- }
404
-
405
- if (prefs.abortEarly) {
406
- return internals.finalize(value, result.errors, helpers);
407
- }
408
-
409
- errors.push(...result.errors);
410
- }
411
- else {
412
- state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'pass');
413
- state.mainstay.tracer.value(state, 'rule', value, result.value, rule.name);
414
- value = result.value;
415
- }
416
- }
417
-
418
- return internals.finalize(value, errors, helpers);
419
- };
420
-
421
-
422
- internals.rule = function (ret, rule) {
423
-
424
- if (ret instanceof Errors.Report) {
425
- internals.error(ret, rule);
426
- return { errors: [ret], value: null };
427
- }
428
-
429
- if (Array.isArray(ret) &&
430
- ret[Common.symbols.errors]) {
431
-
432
- ret.forEach((report) => internals.error(report, rule));
433
- return { errors: ret, value: null };
434
- }
435
-
436
- return { errors: null, value: ret };
437
- };
438
-
439
-
440
- internals.error = function (report, rule) {
441
-
442
- if (rule.message) {
443
- report._setTemplate(rule.message);
444
- }
445
-
446
- return report;
447
- };
448
-
449
-
450
- internals.finalize = function (value, errors, helpers) {
451
-
452
- errors = errors || [];
453
- const { schema, state, prefs } = helpers;
454
-
455
- // Failover value
456
-
457
- if (errors.length) {
458
- const failover = internals.default('failover', undefined, errors, helpers);
459
- if (failover !== undefined) {
460
- state.mainstay.tracer.value(state, 'failover', value, failover);
461
- value = failover;
462
- errors = [];
463
- }
464
- }
465
-
466
- // Error override
467
-
468
- if (errors.length &&
469
- schema._flags.error) {
470
-
471
- if (typeof schema._flags.error === 'function') {
472
- errors = schema._flags.error(errors);
473
- if (!Array.isArray(errors)) {
474
- errors = [errors];
475
- }
476
-
477
- for (const error of errors) {
478
- Assert(error instanceof Error || error instanceof Errors.Report, 'error() must return an Error object');
479
- }
480
- }
481
- else {
482
- errors = [schema._flags.error];
483
- }
484
- }
485
-
486
- // Default
487
-
488
- if (value === undefined) {
489
- const defaulted = internals.default('default', value, errors, helpers);
490
- state.mainstay.tracer.value(state, 'default', value, defaulted);
491
- value = defaulted;
492
- }
493
-
494
- // Cast
495
-
496
- if (schema._flags.cast &&
497
- value !== undefined) {
498
-
499
- const caster = schema._definition.cast[schema._flags.cast];
500
- if (caster.from(value)) {
501
- const casted = caster.to(value, helpers);
502
- state.mainstay.tracer.value(state, 'cast', value, casted, schema._flags.cast);
503
- value = casted;
504
- }
505
- }
506
-
507
- // Externals
508
-
509
- if (schema.$_terms.externals &&
510
- prefs.externals &&
511
- prefs._externals !== false) { // Disabled for matching
512
-
513
- for (const { method } of schema.$_terms.externals) {
514
- state.mainstay.externals.push({ method, path: state.path, label: Errors.label(schema._flags, state, prefs) });
515
- }
516
- }
517
-
518
- // Result
519
-
520
- const result = { value, errors: errors.length ? errors : null };
521
-
522
- if (schema._flags.result) {
523
- result.value = schema._flags.result === 'strip' ? undefined : /* raw */ helpers.original;
524
- state.mainstay.tracer.value(state, schema._flags.result, value, result.value);
525
- state.shadow(value, schema._flags.result);
526
- }
527
-
528
- // Cache
529
-
530
- if (schema._cache &&
531
- prefs.cache !== false &&
532
- !schema._refs.length) {
533
-
534
- schema._cache.set(helpers.original, result);
535
- }
536
-
537
- // Artifacts
538
-
539
- if (value !== undefined &&
540
- !result.errors &&
541
- schema._flags.artifact !== undefined) {
542
-
543
- state.mainstay.artifacts = state.mainstay.artifacts || new Map();
544
- if (!state.mainstay.artifacts.has(schema._flags.artifact)) {
545
- state.mainstay.artifacts.set(schema._flags.artifact, []);
546
- }
547
-
548
- state.mainstay.artifacts.get(schema._flags.artifact).push(state.path);
549
- }
550
-
551
- return result;
552
- };
553
-
554
-
555
- internals.prefs = function (schema, prefs) {
556
-
557
- const isDefaultOptions = prefs === Common.defaults;
558
- if (isDefaultOptions &&
559
- schema._preferences[Common.symbols.prefs]) {
560
-
561
- return schema._preferences[Common.symbols.prefs];
562
- }
563
-
564
- prefs = Common.preferences(prefs, schema._preferences);
565
- if (isDefaultOptions) {
566
- schema._preferences[Common.symbols.prefs] = prefs;
567
- }
568
-
569
- return prefs;
570
- };
571
-
572
-
573
- internals.default = function (flag, value, errors, helpers) {
574
-
575
- const { schema, state, prefs } = helpers;
576
- const source = schema._flags[flag];
577
- if (prefs.noDefaults ||
578
- source === undefined) {
579
-
580
- return value;
581
- }
582
-
583
- state.mainstay.tracer.log(schema, state, 'rule', flag, 'full');
584
-
585
- if (!source) {
586
- return source;
587
- }
588
-
589
- if (typeof source === 'function') {
590
- const args = source.length ? [Clone(state.ancestors[0]), helpers] : [];
591
-
592
- try {
593
- return source(...args);
594
- }
595
- catch (err) {
596
- errors.push(schema.$_createError(`any.${flag}`, null, { error: err }, state, prefs));
597
- return;
598
- }
599
- }
600
-
601
- if (typeof source !== 'object') {
602
- return source;
603
- }
604
-
605
- if (source[Common.symbols.literal]) {
606
- return source.literal;
607
- }
608
-
609
- if (Common.isResolvable(source)) {
610
- return source.resolve(value, state, prefs);
611
- }
612
-
613
- return Clone(source);
614
- };
615
-
616
-
617
- internals.trim = function (value, schema) {
618
-
619
- if (typeof value !== 'string') {
620
- return value;
621
- }
622
-
623
- const trim = schema.$_getRule('trim');
624
- if (!trim ||
625
- !trim.args.enabled) {
626
-
627
- return value;
628
- }
629
-
630
- return value.trim();
631
- };
632
-
633
-
634
- internals.ignore = {
635
- active: false,
636
- debug: Ignore,
637
- entry: Ignore,
638
- filter: Ignore,
639
- log: Ignore,
640
- resolve: Ignore,
641
- value: Ignore
642
- };
643
-
644
-
645
- internals.errorsArray = function () {
646
-
647
- const errors = [];
648
- errors[Common.symbols.errors] = true;
649
- return errors;
650
- };
1
+ 'use strict';
2
+
3
+ const Assert = require('@hapi/hoek/lib/assert');
4
+ const Clone = require('@hapi/hoek/lib/clone');
5
+ const Ignore = require('@hapi/hoek/lib/ignore');
6
+ const Reach = require('@hapi/hoek/lib/reach');
7
+
8
+ const Common = require('./common');
9
+ const Errors = require('./errors');
10
+ const State = require('./state');
11
+
12
+
13
+ const internals = {
14
+ result: Symbol('result')
15
+ };
16
+
17
+
18
+ exports.entry = function (value, schema, prefs) {
19
+
20
+ let settings = Common.defaults;
21
+ if (prefs) {
22
+ Assert(prefs.warnings === undefined, 'Cannot override warnings preference in synchronous validation');
23
+ Assert(prefs.artifacts === undefined, 'Cannot override artifacts preference in synchronous validation');
24
+ settings = Common.preferences(Common.defaults, prefs);
25
+ }
26
+
27
+ const result = internals.entry(value, schema, settings);
28
+ Assert(!result.mainstay.externals.length, 'Schema with external rules must use validateAsync()');
29
+ const outcome = { value: result.value };
30
+
31
+ if (result.error) {
32
+ outcome.error = result.error;
33
+ }
34
+
35
+ if (result.mainstay.warnings.length) {
36
+ outcome.warning = Errors.details(result.mainstay.warnings);
37
+ }
38
+
39
+ if (result.mainstay.debug) {
40
+ outcome.debug = result.mainstay.debug;
41
+ }
42
+
43
+ if (result.mainstay.artifacts) {
44
+ outcome.artifacts = result.mainstay.artifacts;
45
+ }
46
+
47
+ return outcome;
48
+ };
49
+
50
+
51
+ exports.entryAsync = async function (value, schema, prefs) {
52
+
53
+ let settings = Common.defaults;
54
+ if (prefs) {
55
+ settings = Common.preferences(Common.defaults, prefs);
56
+ }
57
+
58
+ const result = internals.entry(value, schema, settings);
59
+ const mainstay = result.mainstay;
60
+ if (result.error) {
61
+ if (mainstay.debug) {
62
+ result.error.debug = mainstay.debug;
63
+ }
64
+
65
+ throw result.error;
66
+ }
67
+
68
+ if (mainstay.externals.length) {
69
+ let root = result.value;
70
+ for (const { method, path, label } of mainstay.externals) {
71
+ let node = root;
72
+ let key;
73
+ let parent;
74
+
75
+ if (path.length) {
76
+ key = path[path.length - 1];
77
+ parent = Reach(root, path.slice(0, -1));
78
+ node = parent[key];
79
+ }
80
+
81
+ try {
82
+ const output = await method(node, { prefs });
83
+ if (output === undefined ||
84
+ output === node) {
85
+
86
+ continue;
87
+ }
88
+
89
+ if (parent) {
90
+ parent[key] = output;
91
+ }
92
+ else {
93
+ root = output;
94
+ }
95
+ }
96
+ catch (err) {
97
+ err.message += ` (${label})`; // Change message to include path
98
+ throw err;
99
+ }
100
+ }
101
+
102
+ result.value = root;
103
+ }
104
+
105
+ if (!settings.warnings &&
106
+ !settings.debug &&
107
+ !settings.artifacts) {
108
+
109
+ return result.value;
110
+ }
111
+
112
+ const outcome = { value: result.value };
113
+ if (mainstay.warnings.length) {
114
+ outcome.warning = Errors.details(mainstay.warnings);
115
+ }
116
+
117
+ if (mainstay.debug) {
118
+ outcome.debug = mainstay.debug;
119
+ }
120
+
121
+ if (mainstay.artifacts) {
122
+ outcome.artifacts = mainstay.artifacts;
123
+ }
124
+
125
+ return outcome;
126
+ };
127
+
128
+
129
+ internals.entry = function (value, schema, prefs) {
130
+
131
+ // Prepare state
132
+
133
+ const { tracer, cleanup } = internals.tracer(schema, prefs);
134
+ const debug = prefs.debug ? [] : null;
135
+ const links = schema._ids._schemaChain ? new Map() : null;
136
+ const mainstay = { externals: [], warnings: [], tracer, debug, links };
137
+ const schemas = schema._ids._schemaChain ? [{ schema }] : null;
138
+ const state = new State([], [], { mainstay, schemas });
139
+
140
+ // Validate value
141
+
142
+ const result = exports.validate(value, schema, state, prefs);
143
+
144
+ // Process value and errors
145
+
146
+ if (cleanup) {
147
+ schema.$_root.untrace();
148
+ }
149
+
150
+ const error = Errors.process(result.errors, value, prefs);
151
+ return { value: result.value, error, mainstay };
152
+ };
153
+
154
+
155
+ internals.tracer = function (schema, prefs) {
156
+
157
+ if (schema.$_root._tracer) {
158
+ return { tracer: schema.$_root._tracer._register(schema) };
159
+ }
160
+
161
+ if (prefs.debug) {
162
+ Assert(schema.$_root.trace, 'Debug mode not supported');
163
+ return { tracer: schema.$_root.trace()._register(schema), cleanup: true };
164
+ }
165
+
166
+ return { tracer: internals.ignore };
167
+ };
168
+
169
+
170
+ exports.validate = function (value, schema, state, prefs, overrides = {}) {
171
+
172
+ if (schema.$_terms.whens) {
173
+ schema = schema._generate(value, state, prefs).schema;
174
+ }
175
+
176
+ // Setup state and settings
177
+
178
+ if (schema._preferences) {
179
+ prefs = internals.prefs(schema, prefs);
180
+ }
181
+
182
+ // Cache
183
+
184
+ if (schema._cache &&
185
+ prefs.cache) {
186
+
187
+ const result = schema._cache.get(value);
188
+ state.mainstay.tracer.debug(state, 'validate', 'cached', !!result);
189
+ if (result) {
190
+ return result;
191
+ }
192
+ }
193
+
194
+ // Helpers
195
+
196
+ const createError = (code, local, localState) => schema.$_createError(code, value, local, localState || state, prefs);
197
+ const helpers = {
198
+ original: value,
199
+ prefs,
200
+ schema,
201
+ state,
202
+ error: createError,
203
+ errorsArray: internals.errorsArray,
204
+ warn: (code, local, localState) => state.mainstay.warnings.push(createError(code, local, localState)),
205
+ message: (messages, local) => schema.$_createError('custom', value, local, state, prefs, { messages })
206
+ };
207
+
208
+ // Prepare
209
+
210
+ state.mainstay.tracer.entry(schema, state);
211
+
212
+ const def = schema._definition;
213
+ if (def.prepare &&
214
+ value !== undefined &&
215
+ prefs.convert) {
216
+
217
+ const prepared = def.prepare(value, helpers);
218
+ if (prepared) {
219
+ state.mainstay.tracer.value(state, 'prepare', value, prepared.value);
220
+ if (prepared.errors) {
221
+ return internals.finalize(prepared.value, [].concat(prepared.errors), helpers); // Prepare error always aborts early
222
+ }
223
+
224
+ value = prepared.value;
225
+ }
226
+ }
227
+
228
+ // Type coercion
229
+
230
+ if (def.coerce &&
231
+ value !== undefined &&
232
+ prefs.convert &&
233
+ (!def.coerce.from || def.coerce.from.includes(typeof value))) {
234
+
235
+ const coerced = def.coerce.method(value, helpers);
236
+ if (coerced) {
237
+ state.mainstay.tracer.value(state, 'coerced', value, coerced.value);
238
+ if (coerced.errors) {
239
+ return internals.finalize(coerced.value, [].concat(coerced.errors), helpers); // Coerce error always aborts early
240
+ }
241
+
242
+ value = coerced.value;
243
+ }
244
+ }
245
+
246
+ // Empty value
247
+
248
+ const empty = schema._flags.empty;
249
+ if (empty &&
250
+ empty.$_match(internals.trim(value, schema), state.nest(empty), Common.defaults)) {
251
+
252
+ state.mainstay.tracer.value(state, 'empty', value, undefined);
253
+ value = undefined;
254
+ }
255
+
256
+ // Presence requirements (required, optional, forbidden)
257
+
258
+ const presence = overrides.presence || schema._flags.presence || (schema._flags._endedSwitch ? null : prefs.presence);
259
+ if (value === undefined) {
260
+ if (presence === 'forbidden') {
261
+ return internals.finalize(value, null, helpers);
262
+ }
263
+
264
+ if (presence === 'required') {
265
+ return internals.finalize(value, [schema.$_createError('any.required', value, null, state, prefs)], helpers);
266
+ }
267
+
268
+ if (presence === 'optional') {
269
+ if (schema._flags.default !== Common.symbols.deepDefault) {
270
+ return internals.finalize(value, null, helpers);
271
+ }
272
+
273
+ state.mainstay.tracer.value(state, 'default', value, {});
274
+ value = {};
275
+ }
276
+ }
277
+ else if (presence === 'forbidden') {
278
+ return internals.finalize(value, [schema.$_createError('any.unknown', value, null, state, prefs)], helpers);
279
+ }
280
+
281
+ // Allowed values
282
+
283
+ const errors = [];
284
+
285
+ if (schema._valids) {
286
+ const match = schema._valids.get(value, state, prefs, schema._flags.insensitive);
287
+ if (match) {
288
+ if (prefs.convert) {
289
+ state.mainstay.tracer.value(state, 'valids', value, match.value);
290
+ value = match.value;
291
+ }
292
+
293
+ state.mainstay.tracer.filter(schema, state, 'valid', match);
294
+ return internals.finalize(value, null, helpers);
295
+ }
296
+
297
+ if (schema._flags.only) {
298
+ const report = schema.$_createError('any.only', value, { valids: schema._valids.values({ display: true }) }, state, prefs);
299
+ if (prefs.abortEarly) {
300
+ return internals.finalize(value, [report], helpers);
301
+ }
302
+
303
+ errors.push(report);
304
+ }
305
+ }
306
+
307
+ // Denied values
308
+
309
+ if (schema._invalids) {
310
+ const match = schema._invalids.get(value, state, prefs, schema._flags.insensitive);
311
+ if (match) {
312
+ state.mainstay.tracer.filter(schema, state, 'invalid', match);
313
+ const report = schema.$_createError('any.invalid', value, { invalids: schema._invalids.values({ display: true }) }, state, prefs);
314
+ if (prefs.abortEarly) {
315
+ return internals.finalize(value, [report], helpers);
316
+ }
317
+
318
+ errors.push(report);
319
+ }
320
+ }
321
+
322
+ // Base type
323
+
324
+ if (def.validate) {
325
+ const base = def.validate(value, helpers);
326
+ if (base) {
327
+ state.mainstay.tracer.value(state, 'base', value, base.value);
328
+ value = base.value;
329
+
330
+ if (base.errors) {
331
+ if (!Array.isArray(base.errors)) {
332
+ errors.push(base.errors);
333
+ return internals.finalize(value, errors, helpers); // Base error always aborts early
334
+ }
335
+
336
+ if (base.errors.length) {
337
+ errors.push(...base.errors);
338
+ return internals.finalize(value, errors, helpers); // Base error always aborts early
339
+ }
340
+ }
341
+ }
342
+ }
343
+
344
+ // Validate tests
345
+
346
+ if (!schema._rules.length) {
347
+ return internals.finalize(value, errors, helpers);
348
+ }
349
+
350
+ return internals.rules(value, errors, helpers);
351
+ };
352
+
353
+
354
+ internals.rules = function (value, errors, helpers) {
355
+
356
+ const { schema, state, prefs } = helpers;
357
+
358
+ for (const rule of schema._rules) {
359
+ const definition = schema._definition.rules[rule.method];
360
+
361
+ // Skip rules that are also applied in coerce step
362
+
363
+ if (definition.convert &&
364
+ prefs.convert) {
365
+
366
+ state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'full');
367
+ continue;
368
+ }
369
+
370
+ // Resolve references
371
+
372
+ let ret;
373
+ let args = rule.args;
374
+ if (rule._resolve.length) {
375
+ args = Object.assign({}, args); // Shallow copy
376
+ for (const key of rule._resolve) {
377
+ const resolver = definition.argsByName.get(key);
378
+
379
+ const resolved = args[key].resolve(value, state, prefs);
380
+ const normalized = resolver.normalize ? resolver.normalize(resolved) : resolved;
381
+
382
+ const invalid = Common.validateArg(normalized, null, resolver);
383
+ if (invalid) {
384
+ ret = schema.$_createError('any.ref', resolved, { arg: key, ref: args[key], reason: invalid }, state, prefs);
385
+ break;
386
+ }
387
+
388
+ args[key] = normalized;
389
+ }
390
+ }
391
+
392
+ // Test rule
393
+
394
+ ret = ret || definition.validate(value, helpers, args, rule); // Use ret if already set to reference error
395
+
396
+ const result = internals.rule(ret, rule);
397
+ if (result.errors) {
398
+ state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'error');
399
+
400
+ if (rule.warn) {
401
+ state.mainstay.warnings.push(...result.errors);
402
+ continue;
403
+ }
404
+
405
+ if (prefs.abortEarly) {
406
+ return internals.finalize(value, result.errors, helpers);
407
+ }
408
+
409
+ errors.push(...result.errors);
410
+ }
411
+ else {
412
+ state.mainstay.tracer.log(schema, state, 'rule', rule.name, 'pass');
413
+ state.mainstay.tracer.value(state, 'rule', value, result.value, rule.name);
414
+ value = result.value;
415
+ }
416
+ }
417
+
418
+ return internals.finalize(value, errors, helpers);
419
+ };
420
+
421
+
422
+ internals.rule = function (ret, rule) {
423
+
424
+ if (ret instanceof Errors.Report) {
425
+ internals.error(ret, rule);
426
+ return { errors: [ret], value: null };
427
+ }
428
+
429
+ if (Array.isArray(ret) &&
430
+ ret[Common.symbols.errors]) {
431
+
432
+ ret.forEach((report) => internals.error(report, rule));
433
+ return { errors: ret, value: null };
434
+ }
435
+
436
+ return { errors: null, value: ret };
437
+ };
438
+
439
+
440
+ internals.error = function (report, rule) {
441
+
442
+ if (rule.message) {
443
+ report._setTemplate(rule.message);
444
+ }
445
+
446
+ return report;
447
+ };
448
+
449
+
450
+ internals.finalize = function (value, errors, helpers) {
451
+
452
+ errors = errors || [];
453
+ const { schema, state, prefs } = helpers;
454
+
455
+ // Failover value
456
+
457
+ if (errors.length) {
458
+ const failover = internals.default('failover', undefined, errors, helpers);
459
+ if (failover !== undefined) {
460
+ state.mainstay.tracer.value(state, 'failover', value, failover);
461
+ value = failover;
462
+ errors = [];
463
+ }
464
+ }
465
+
466
+ // Error override
467
+
468
+ if (errors.length &&
469
+ schema._flags.error) {
470
+
471
+ if (typeof schema._flags.error === 'function') {
472
+ errors = schema._flags.error(errors);
473
+ if (!Array.isArray(errors)) {
474
+ errors = [errors];
475
+ }
476
+
477
+ for (const error of errors) {
478
+ Assert(error instanceof Error || error instanceof Errors.Report, 'error() must return an Error object');
479
+ }
480
+ }
481
+ else {
482
+ errors = [schema._flags.error];
483
+ }
484
+ }
485
+
486
+ // Default
487
+
488
+ if (value === undefined) {
489
+ const defaulted = internals.default('default', value, errors, helpers);
490
+ state.mainstay.tracer.value(state, 'default', value, defaulted);
491
+ value = defaulted;
492
+ }
493
+
494
+ // Cast
495
+
496
+ if (schema._flags.cast &&
497
+ value !== undefined) {
498
+
499
+ const caster = schema._definition.cast[schema._flags.cast];
500
+ if (caster.from(value)) {
501
+ const casted = caster.to(value, helpers);
502
+ state.mainstay.tracer.value(state, 'cast', value, casted, schema._flags.cast);
503
+ value = casted;
504
+ }
505
+ }
506
+
507
+ // Externals
508
+
509
+ if (schema.$_terms.externals &&
510
+ prefs.externals &&
511
+ prefs._externals !== false) { // Disabled for matching
512
+
513
+ for (const { method } of schema.$_terms.externals) {
514
+ state.mainstay.externals.push({ method, path: state.path, label: Errors.label(schema._flags, state, prefs) });
515
+ }
516
+ }
517
+
518
+ // Result
519
+
520
+ const result = { value, errors: errors.length ? errors : null };
521
+
522
+ if (schema._flags.result) {
523
+ result.value = schema._flags.result === 'strip' ? undefined : /* raw */ helpers.original;
524
+ state.mainstay.tracer.value(state, schema._flags.result, value, result.value);
525
+ state.shadow(value, schema._flags.result);
526
+ }
527
+
528
+ // Cache
529
+
530
+ if (schema._cache &&
531
+ prefs.cache !== false &&
532
+ !schema._refs.length) {
533
+
534
+ schema._cache.set(helpers.original, result);
535
+ }
536
+
537
+ // Artifacts
538
+
539
+ if (value !== undefined &&
540
+ !result.errors &&
541
+ schema._flags.artifact !== undefined) {
542
+
543
+ state.mainstay.artifacts = state.mainstay.artifacts || new Map();
544
+ if (!state.mainstay.artifacts.has(schema._flags.artifact)) {
545
+ state.mainstay.artifacts.set(schema._flags.artifact, []);
546
+ }
547
+
548
+ state.mainstay.artifacts.get(schema._flags.artifact).push(state.path);
549
+ }
550
+
551
+ return result;
552
+ };
553
+
554
+
555
+ internals.prefs = function (schema, prefs) {
556
+
557
+ const isDefaultOptions = prefs === Common.defaults;
558
+ if (isDefaultOptions &&
559
+ schema._preferences[Common.symbols.prefs]) {
560
+
561
+ return schema._preferences[Common.symbols.prefs];
562
+ }
563
+
564
+ prefs = Common.preferences(prefs, schema._preferences);
565
+ if (isDefaultOptions) {
566
+ schema._preferences[Common.symbols.prefs] = prefs;
567
+ }
568
+
569
+ return prefs;
570
+ };
571
+
572
+
573
+ internals.default = function (flag, value, errors, helpers) {
574
+
575
+ const { schema, state, prefs } = helpers;
576
+ const source = schema._flags[flag];
577
+ if (prefs.noDefaults ||
578
+ source === undefined) {
579
+
580
+ return value;
581
+ }
582
+
583
+ state.mainstay.tracer.log(schema, state, 'rule', flag, 'full');
584
+
585
+ if (!source) {
586
+ return source;
587
+ }
588
+
589
+ if (typeof source === 'function') {
590
+ const args = source.length ? [Clone(state.ancestors[0]), helpers] : [];
591
+
592
+ try {
593
+ return source(...args);
594
+ }
595
+ catch (err) {
596
+ errors.push(schema.$_createError(`any.${flag}`, null, { error: err }, state, prefs));
597
+ return;
598
+ }
599
+ }
600
+
601
+ if (typeof source !== 'object') {
602
+ return source;
603
+ }
604
+
605
+ if (source[Common.symbols.literal]) {
606
+ return source.literal;
607
+ }
608
+
609
+ if (Common.isResolvable(source)) {
610
+ return source.resolve(value, state, prefs);
611
+ }
612
+
613
+ return Clone(source);
614
+ };
615
+
616
+
617
+ internals.trim = function (value, schema) {
618
+
619
+ if (typeof value !== 'string') {
620
+ return value;
621
+ }
622
+
623
+ const trim = schema.$_getRule('trim');
624
+ if (!trim ||
625
+ !trim.args.enabled) {
626
+
627
+ return value;
628
+ }
629
+
630
+ return value.trim();
631
+ };
632
+
633
+
634
+ internals.ignore = {
635
+ active: false,
636
+ debug: Ignore,
637
+ entry: Ignore,
638
+ filter: Ignore,
639
+ log: Ignore,
640
+ resolve: Ignore,
641
+ value: Ignore
642
+ };
643
+
644
+
645
+ internals.errorsArray = function () {
646
+
647
+ const errors = [];
648
+ errors[Common.symbols.errors] = true;
649
+ return errors;
650
+ };