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