@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,937 @@
1
+ 'use strict';
2
+
3
+ const { assert, deepEqual, reach } = require('@hapi/hoek');
4
+
5
+ const Any = require('./any');
6
+ const Common = require('../common');
7
+ const Compile = require('../compile');
8
+
9
+
10
+ const internals = {};
11
+
12
+
13
+ module.exports = Any.extend({
14
+
15
+ type: 'array',
16
+
17
+ flags: {
18
+
19
+ single: { default: false },
20
+ sparse: { default: false }
21
+ },
22
+
23
+ terms: {
24
+
25
+ items: { init: [], manifest: 'schema' },
26
+ ordered: { init: [], manifest: 'schema' },
27
+
28
+ _exclusions: { init: [] },
29
+ _inclusions: { init: [] },
30
+ _requireds: { init: [] }
31
+ },
32
+
33
+ coerce: {
34
+ from: 'object',
35
+ method(value, { schema, state, prefs }) {
36
+
37
+ if (!Array.isArray(value)) {
38
+ return;
39
+ }
40
+
41
+ const sort = schema.$_getRule('sort');
42
+ if (!sort) {
43
+ return;
44
+ }
45
+
46
+ return internals.sort(schema, value, sort.args.options, state, prefs);
47
+ }
48
+ },
49
+
50
+ validate(value, { schema, error }) {
51
+
52
+ if (!Array.isArray(value)) {
53
+ if (schema._flags.single) {
54
+ const single = [value];
55
+ single[Common.symbols.arraySingle] = true;
56
+ return { value: single };
57
+ }
58
+
59
+ return { errors: error('array.base') };
60
+ }
61
+
62
+ if (!schema.$_getRule('items') &&
63
+ !schema.$_terms.externals) {
64
+
65
+ return;
66
+ }
67
+
68
+ return { value: value.slice() }; // Clone the array so that we don't modify the original
69
+ },
70
+
71
+ jsonSchema(schema, res, mode, options) {
72
+
73
+ const ordered = schema.$_terms.ordered;
74
+
75
+ // Handle ordered items (tuple-like) using 'prefixItems'
76
+
77
+ if (ordered.length) {
78
+ res.prefixItems = ordered.map((item) => item.$_jsonSchema(mode, options));
79
+ }
80
+
81
+ if (schema.$_terms.items.length) {
82
+ let items;
83
+ if (schema.$_terms.items.length === 1) {
84
+ items = schema.$_terms.items[0].$_jsonSchema(mode, options);
85
+ }
86
+ else {
87
+ items = {
88
+ anyOf: schema.$_terms.items.map((item) => item.$_jsonSchema(mode, options))
89
+ };
90
+ }
91
+
92
+ // If there are ordered items, remaining items are 'unevaluatedItems'
93
+
94
+ if (ordered.length) {
95
+ res.unevaluatedItems = items;
96
+ res.minItems = ordered.length;
97
+ }
98
+ else {
99
+ res.items = items;
100
+ }
101
+ }
102
+ else if (ordered.length) {
103
+ // No additional items allowed beyond the ordered ones
104
+
105
+ res.unevaluatedItems = false;
106
+ res.minItems = ordered.length;
107
+ res.maxItems = ordered.length;
108
+ }
109
+
110
+ // Map 'has' rules to 'contains' in JSON Schema
111
+
112
+ const contains = [];
113
+ for (const rule of schema._rules) {
114
+ if (rule.name === 'has') {
115
+ contains.push(rule.args.schema.$_jsonSchema(mode, options));
116
+ }
117
+ }
118
+
119
+ if (contains.length) {
120
+ if (contains.length === 1) {
121
+ res.contains = contains[0];
122
+ }
123
+ else {
124
+ res.allOf = contains.map((item) => ({ contains: item }));
125
+ }
126
+ }
127
+
128
+ if (schema._flags.single &&
129
+ schema.$_terms.items.length) {
130
+
131
+ let items;
132
+ if (schema.$_terms.items.length === 1) {
133
+ items = schema.$_terms.items[0].$_jsonSchema(mode, options);
134
+ }
135
+ else {
136
+ items = {
137
+ anyOf: schema.$_terms.items.map((item) => item.$_jsonSchema(mode, options))
138
+ };
139
+ }
140
+
141
+ res = {
142
+ anyOf: [
143
+ res,
144
+ items
145
+ ]
146
+ };
147
+ }
148
+
149
+ return res;
150
+ },
151
+
152
+ rules: {
153
+
154
+ has: {
155
+ method(schema) {
156
+
157
+ schema = this.$_compile(schema, { appendPath: true });
158
+ const obj = this.$_addRule({ name: 'has', args: { schema } });
159
+ obj.$_mutateRegister(schema);
160
+ return obj;
161
+ },
162
+ validate(value, { state, prefs, error }, { schema: has }) {
163
+
164
+ const ancestors = [value, ...state.ancestors];
165
+ for (let i = 0; i < value.length; ++i) {
166
+ const localState = state.localize([...state.path, i], ancestors, has);
167
+ if (has.$_match(value[i], localState, prefs)) {
168
+ return value;
169
+ }
170
+ }
171
+
172
+ const patternLabel = has._flags.label;
173
+ if (patternLabel) {
174
+ return error('array.hasKnown', { patternLabel });
175
+ }
176
+
177
+ return error('array.hasUnknown', null);
178
+ },
179
+ multi: true
180
+ },
181
+
182
+ items: {
183
+ method(...schemas) {
184
+
185
+ Common.verifyFlat(schemas, 'items');
186
+
187
+ const obj = this.$_addRule('items');
188
+
189
+ for (let i = 0; i < schemas.length; ++i) {
190
+ const type = Common.tryWithPath(() => this.$_compile(schemas[i]), i, { append: true });
191
+ obj.$_terms.items.push(type);
192
+ }
193
+
194
+ return obj.$_mutateRebuild();
195
+ },
196
+ validate(value, { schema, error, state, prefs, errorsArray }) {
197
+
198
+ const requireds = schema.$_terms._requireds.slice();
199
+ const ordereds = schema.$_terms.ordered.slice();
200
+ const inclusions = [...schema.$_terms._inclusions, ...requireds];
201
+
202
+ const wasArray = !value[Common.symbols.arraySingle];
203
+ delete value[Common.symbols.arraySingle];
204
+
205
+ const errors = errorsArray();
206
+
207
+ let il = value.length;
208
+ for (let i = 0; i < il; ++i) {
209
+ const item = value[i];
210
+
211
+ let errored = false;
212
+ let isValid = false;
213
+
214
+ const key = wasArray ? i : new Number(i); // eslint-disable-line no-new-wrappers
215
+ const path = [...state.path, key];
216
+
217
+ // Sparse
218
+
219
+ if (!schema._flags.sparse &&
220
+ item === undefined) {
221
+
222
+ errors.push(error('array.sparse', { key, path, pos: i, value: undefined }, state.localize(path)));
223
+ if (prefs.abortEarly) {
224
+ return errors;
225
+ }
226
+
227
+ ordereds.shift();
228
+ continue;
229
+ }
230
+
231
+ // Exclusions
232
+
233
+ const ancestors = [value, ...state.ancestors];
234
+
235
+ for (const exclusion of schema.$_terms._exclusions) {
236
+ if (!exclusion.$_match(item, state.localize(path, ancestors, exclusion), prefs, { presence: 'ignore' })) {
237
+ continue;
238
+ }
239
+
240
+ errors.push(error('array.excludes', { pos: i, value: item }, state.localize(path)));
241
+ if (prefs.abortEarly) {
242
+ return errors;
243
+ }
244
+
245
+ errored = true;
246
+ ordereds.shift();
247
+ break;
248
+ }
249
+
250
+ if (errored) {
251
+ continue;
252
+ }
253
+
254
+ // Ordered
255
+
256
+ if (schema.$_terms.ordered.length) {
257
+ if (ordereds.length) {
258
+ const ordered = ordereds.shift();
259
+ const res = ordered.$_validate(item, state.localize(path, ancestors, ordered), prefs);
260
+ if (!res.errors) {
261
+ if (ordered._flags.result === 'strip') {
262
+ internals.fastSplice(value, i);
263
+ --i;
264
+ --il;
265
+ }
266
+ else if (!schema._flags.sparse && res.value === undefined) {
267
+ errors.push(error('array.sparse', { key, path, pos: i, value: undefined }, state.localize(path)));
268
+ if (prefs.abortEarly) {
269
+ return errors;
270
+ }
271
+
272
+ continue;
273
+ }
274
+ else {
275
+ value[i] = res.value;
276
+ }
277
+ }
278
+ else {
279
+ errors.push(...res.errors);
280
+ if (prefs.abortEarly) {
281
+ return errors;
282
+ }
283
+ }
284
+
285
+ continue;
286
+ }
287
+ else if (!schema.$_terms.items.length) {
288
+ errors.push(error('array.orderedLength', { pos: i, limit: schema.$_terms.ordered.length }));
289
+ if (prefs.abortEarly) {
290
+ return errors;
291
+ }
292
+
293
+ break; // No reason to continue since there are no other rules to validate other than array.orderedLength
294
+ }
295
+ }
296
+
297
+ // Requireds
298
+
299
+ const requiredChecks = [];
300
+ let jl = requireds.length;
301
+ for (let j = 0; j < jl; ++j) {
302
+ const localState = state.localize(path, ancestors, requireds[j]);
303
+ localState.snapshot();
304
+
305
+ const res = requireds[j].$_validate(item, localState, prefs);
306
+ requiredChecks[j] = res;
307
+
308
+ if (!res.errors) {
309
+ localState.commit();
310
+ value[i] = res.value;
311
+ isValid = true;
312
+ internals.fastSplice(requireds, j);
313
+ --j;
314
+ --jl;
315
+
316
+ if (!schema._flags.sparse &&
317
+ res.value === undefined) {
318
+
319
+ errors.push(error('array.sparse', { key, path, pos: i, value: undefined }, state.localize(path)));
320
+ if (prefs.abortEarly) {
321
+ return errors;
322
+ }
323
+ }
324
+
325
+ break;
326
+ }
327
+
328
+ localState.restore();
329
+ }
330
+
331
+ if (isValid) {
332
+ continue;
333
+ }
334
+
335
+ // Inclusions
336
+
337
+ const stripUnknown = prefs.stripUnknown && !!prefs.stripUnknown.arrays || false;
338
+
339
+ jl = inclusions.length;
340
+ for (const inclusion of inclusions) {
341
+
342
+ // Avoid re-running requireds that already didn't match in the previous loop
343
+
344
+ let res;
345
+ const previousCheck = requireds.indexOf(inclusion);
346
+ if (previousCheck !== -1) {
347
+ res = requiredChecks[previousCheck];
348
+ }
349
+ else {
350
+ const localState = state.localize(path, ancestors, inclusion);
351
+ localState.snapshot();
352
+
353
+ res = inclusion.$_validate(item, localState, prefs);
354
+ if (!res.errors) {
355
+ localState.commit();
356
+ if (inclusion._flags.result === 'strip') {
357
+ internals.fastSplice(value, i);
358
+ --i;
359
+ --il;
360
+ }
361
+ else if (!schema._flags.sparse &&
362
+ res.value === undefined) {
363
+
364
+ errors.push(error('array.sparse', { key, path, pos: i, value: undefined }, state.localize(path)));
365
+ errored = true;
366
+ }
367
+ else {
368
+ value[i] = res.value;
369
+ }
370
+
371
+ isValid = true;
372
+ break;
373
+ }
374
+
375
+ localState.restore();
376
+ }
377
+
378
+ // Return the actual error if only one inclusion defined
379
+
380
+ if (jl === 1) {
381
+ if (stripUnknown) {
382
+ internals.fastSplice(value, i);
383
+ --i;
384
+ --il;
385
+ isValid = true;
386
+ break;
387
+ }
388
+
389
+ errors.push(...res.errors);
390
+ if (prefs.abortEarly) {
391
+ return errors;
392
+ }
393
+
394
+ errored = true;
395
+ break;
396
+ }
397
+ }
398
+
399
+ if (errored) {
400
+ continue;
401
+ }
402
+
403
+ if ((schema.$_terms._inclusions.length || schema.$_terms._requireds.length) &&
404
+ !isValid) {
405
+
406
+ if (stripUnknown) {
407
+ internals.fastSplice(value, i);
408
+ --i;
409
+ --il;
410
+ continue;
411
+ }
412
+
413
+ errors.push(error('array.includes', { pos: i, value: item }, state.localize(path)));
414
+ if (prefs.abortEarly) {
415
+ return errors;
416
+ }
417
+ }
418
+ }
419
+
420
+ if (requireds.length) {
421
+ internals.fillMissedErrors(schema, errors, requireds, value, state, prefs);
422
+ }
423
+
424
+ if (ordereds.length) {
425
+ internals.fillOrderedErrors(schema, errors, ordereds, value, state, prefs);
426
+
427
+ if (!errors.length) {
428
+ internals.fillDefault(ordereds, value, state, prefs);
429
+ }
430
+ }
431
+
432
+ return errors.length ? errors : value;
433
+ },
434
+
435
+ priority: true,
436
+ manifest: false
437
+ },
438
+
439
+ length: {
440
+ method(limit) {
441
+
442
+ return this.$_addRule({ name: 'length', args: { limit }, operator: '=' });
443
+ },
444
+ validate(value, helpers, { limit }, { name, operator, args }) {
445
+
446
+ if (Common.compare(value.length, limit, operator)) {
447
+ return value;
448
+ }
449
+
450
+ return helpers.error('array.' + name, { limit: args.limit, value });
451
+ },
452
+ jsonSchema(rule, res) {
453
+
454
+ res.minItems = rule.args.limit;
455
+ res.maxItems = rule.args.limit;
456
+
457
+ return res;
458
+ },
459
+ args: [
460
+ {
461
+ name: 'limit',
462
+ ref: true,
463
+ assert: Common.limit,
464
+ message: 'must be a positive integer'
465
+ }
466
+ ]
467
+ },
468
+
469
+ max: {
470
+ method(limit) {
471
+
472
+ return this.$_addRule({ name: 'max', method: 'length', args: { limit }, operator: '<=' });
473
+ },
474
+ jsonSchema(rule, res) {
475
+
476
+ res.maxItems = rule.args.limit;
477
+
478
+ return res;
479
+ }
480
+ },
481
+
482
+ min: {
483
+ method(limit) {
484
+
485
+ return this.$_addRule({ name: 'min', method: 'length', args: { limit }, operator: '>=' });
486
+ },
487
+ jsonSchema(rule, res) {
488
+
489
+ res.minItems = rule.args.limit;
490
+
491
+ return res;
492
+ }
493
+ },
494
+
495
+ ordered: {
496
+ method(...schemas) {
497
+
498
+ Common.verifyFlat(schemas, 'ordered');
499
+
500
+ const obj = this.$_addRule('items');
501
+
502
+ for (let i = 0; i < schemas.length; ++i) {
503
+ const type = Common.tryWithPath(() => this.$_compile(schemas[i]), i, { append: true });
504
+ internals.validateSingle(type, obj);
505
+
506
+ obj.$_mutateRegister(type);
507
+ obj.$_terms.ordered.push(type);
508
+ }
509
+
510
+ return obj.$_mutateRebuild();
511
+ }
512
+ },
513
+
514
+ single: {
515
+ method(enabled) {
516
+
517
+ const value = enabled === undefined ? true : !!enabled;
518
+ assert(!value || !this._flags._arrayItems, 'Cannot specify single rule when array has array items');
519
+
520
+ return this.$_setFlag('single', value);
521
+ }
522
+ },
523
+
524
+ sort: {
525
+ method(options = {}) {
526
+
527
+ Common.assertOptions(options, ['by', 'order']);
528
+
529
+ const settings = {
530
+ order: options.order || 'ascending'
531
+ };
532
+
533
+ if (options.by) {
534
+ settings.by = Compile.ref(options.by, { ancestor: 0 });
535
+ assert(!settings.by.ancestor, 'Cannot sort by ancestor');
536
+ }
537
+
538
+ return this.$_addRule({ name: 'sort', args: { options: settings } });
539
+ },
540
+ validate(value, { error, state, prefs, schema }, { options }) {
541
+
542
+ const { value: sorted, errors } = internals.sort(schema, value, options, state, prefs);
543
+ if (errors) {
544
+ return errors;
545
+ }
546
+
547
+ for (let i = 0; i < value.length; ++i) {
548
+ if (value[i] !== sorted[i]) {
549
+ return error('array.sort', { order: options.order, by: options.by ? options.by.key : 'value' });
550
+ }
551
+ }
552
+
553
+ return value;
554
+ },
555
+ convert: true
556
+ },
557
+
558
+ sparse: {
559
+ method(enabled) {
560
+
561
+ const value = enabled === undefined ? true : !!enabled;
562
+
563
+ if (this._flags.sparse === value) {
564
+ return this;
565
+ }
566
+
567
+ const obj = value ? this.clone() : this.$_addRule('items');
568
+ return obj.$_setFlag('sparse', value, { clone: false });
569
+ }
570
+ },
571
+
572
+ unique: {
573
+ method(comparator, options = {}) {
574
+
575
+ assert(!comparator || typeof comparator === 'function' || typeof comparator === 'string', 'comparator must be a function or a string');
576
+ Common.assertOptions(options, ['ignoreUndefined', 'separator']);
577
+
578
+ const rule = { name: 'unique', args: { options, comparator } };
579
+
580
+ if (comparator) {
581
+ if (typeof comparator === 'string') {
582
+ const separator = Common.default(options.separator, '.');
583
+ rule.path = separator ? comparator.split(separator) : [comparator];
584
+ }
585
+ else {
586
+ rule.comparator = comparator;
587
+ }
588
+ }
589
+
590
+ return this.$_addRule(rule);
591
+ },
592
+ validate(value, { state, error, schema }, { comparator: raw, options }, { comparator, path }) {
593
+
594
+ const found = {
595
+ string: Object.create(null),
596
+ number: Object.create(null),
597
+ undefined: Object.create(null),
598
+ boolean: Object.create(null),
599
+ bigint: Object.create(null),
600
+ object: new Map(),
601
+ function: new Map(),
602
+ custom: new Map()
603
+ };
604
+
605
+ const compare = comparator || deepEqual;
606
+ const ignoreUndefined = options.ignoreUndefined;
607
+
608
+ for (let i = 0; i < value.length; ++i) {
609
+ const item = path ? reach(value[i], path) : value[i];
610
+ const records = comparator ? found.custom : found[typeof item];
611
+ assert(records, 'Failed to find unique map container for type', typeof item);
612
+
613
+ if (records instanceof Map) {
614
+ const entries = records.entries();
615
+ let current;
616
+ while (!(current = entries.next()).done) {
617
+ if (compare(current.value[0], item)) {
618
+ const localState = state.localize([...state.path, i], [value, ...state.ancestors]);
619
+ const context = {
620
+ pos: i,
621
+ value: value[i],
622
+ dupePos: current.value[1],
623
+ dupeValue: value[current.value[1]]
624
+ };
625
+
626
+ if (path) {
627
+ context.path = raw;
628
+ }
629
+
630
+ return error('array.unique', context, localState);
631
+ }
632
+ }
633
+
634
+ records.set(item, i);
635
+ }
636
+ else {
637
+ if ((!ignoreUndefined || item !== undefined) &&
638
+ records[item] !== undefined) {
639
+
640
+ const context = {
641
+ pos: i,
642
+ value: value[i],
643
+ dupePos: records[item],
644
+ dupeValue: value[records[item]]
645
+ };
646
+
647
+ if (path) {
648
+ context.path = raw;
649
+ }
650
+
651
+ const localState = state.localize([...state.path, i], [value, ...state.ancestors]);
652
+ return error('array.unique', context, localState);
653
+ }
654
+
655
+ records[item] = i;
656
+ }
657
+ }
658
+
659
+ return value;
660
+ },
661
+ jsonSchema(rule, res) {
662
+
663
+ res.uniqueItems = true;
664
+
665
+ return res;
666
+ },
667
+ args: ['comparator', 'options'],
668
+ multi: true
669
+ }
670
+ },
671
+
672
+ overrides: {
673
+
674
+ isAsync() {
675
+
676
+ if (this.$_terms.externals?.length) {
677
+ return true;
678
+ }
679
+
680
+ for (const item of this.$_terms.items) {
681
+ if (item.isAsync()) {
682
+ return true;
683
+ }
684
+ }
685
+
686
+ for (const item of this.$_terms.ordered) {
687
+ if (item.isAsync()) {
688
+ return true;
689
+ }
690
+ }
691
+
692
+ return false;
693
+ }
694
+ },
695
+
696
+ cast: {
697
+ set: {
698
+ from: Array.isArray,
699
+ to(value, helpers) {
700
+
701
+ return new Set(value);
702
+ }
703
+ }
704
+ },
705
+
706
+ rebuild(schema) {
707
+
708
+ schema.$_terms._inclusions = [];
709
+ schema.$_terms._exclusions = [];
710
+ schema.$_terms._requireds = [];
711
+
712
+ for (const type of schema.$_terms.items) {
713
+ internals.validateSingle(type, schema);
714
+
715
+ if (type._flags.presence === 'required') {
716
+ schema.$_terms._requireds.push(type);
717
+ }
718
+ else if (type._flags.presence === 'forbidden') {
719
+ schema.$_terms._exclusions.push(type);
720
+ }
721
+ else {
722
+ schema.$_terms._inclusions.push(type);
723
+ }
724
+ }
725
+
726
+ for (const type of schema.$_terms.ordered) {
727
+ internals.validateSingle(type, schema);
728
+ }
729
+ },
730
+
731
+ manifest: {
732
+
733
+ build(obj, desc) {
734
+
735
+ if (desc.items) {
736
+ obj = obj.items(...desc.items);
737
+ }
738
+
739
+ if (desc.ordered) {
740
+ obj = obj.ordered(...desc.ordered);
741
+ }
742
+
743
+ return obj;
744
+ }
745
+ },
746
+
747
+ messages: {
748
+ 'array.base': '{{#label}} must be an array',
749
+ 'array.excludes': '{{#label}} contains an excluded value',
750
+ 'array.hasKnown': '{{#label}} does not contain at least one required match for type {:#patternLabel}',
751
+ 'array.hasUnknown': '{{#label}} does not contain at least one required match',
752
+ 'array.includes': '{{#label}} does not match any of the allowed types',
753
+ 'array.includesRequiredBoth': '{{#label}} does not contain {{#knownMisses}} and {{#unknownMisses}} other required value(s)',
754
+ 'array.includesRequiredKnowns': '{{#label}} does not contain {{#knownMisses}}',
755
+ 'array.includesRequiredUnknowns': '{{#label}} does not contain {{#unknownMisses}} required value(s)',
756
+ 'array.length': '{{#label}} must contain {{#limit}} items',
757
+ 'array.max': '{{#label}} must contain less than or equal to {{#limit}} items',
758
+ 'array.min': '{{#label}} must contain at least {{#limit}} items',
759
+ 'array.orderedLength': '{{#label}} must contain at most {{#limit}} items',
760
+ 'array.sort': '{{#label}} must be sorted in {#order} order by {{#by}}',
761
+ 'array.sort.mismatching': '{{#label}} cannot be sorted due to mismatching types',
762
+ 'array.sort.unsupported': '{{#label}} cannot be sorted due to unsupported type {#type}',
763
+ 'array.sparse': '{{#label}} must not be a sparse array item',
764
+ 'array.unique': '{{#label}} contains a duplicate value'
765
+ }
766
+ });
767
+
768
+
769
+ // Helpers
770
+
771
+ internals.fillMissedErrors = function (schema, errors, requireds, value, state, prefs) {
772
+
773
+ const knownMisses = [];
774
+ let unknownMisses = 0;
775
+ for (const required of requireds) {
776
+ const label = required._flags.label;
777
+ if (label) {
778
+ knownMisses.push(label);
779
+ }
780
+ else {
781
+ ++unknownMisses;
782
+ }
783
+ }
784
+
785
+ if (knownMisses.length) {
786
+ if (unknownMisses) {
787
+ errors.push(schema.$_createError('array.includesRequiredBoth', value, { knownMisses, unknownMisses }, state, prefs));
788
+ }
789
+ else {
790
+ errors.push(schema.$_createError('array.includesRequiredKnowns', value, { knownMisses }, state, prefs));
791
+ }
792
+ }
793
+ else {
794
+ errors.push(schema.$_createError('array.includesRequiredUnknowns', value, { unknownMisses }, state, prefs));
795
+ }
796
+ };
797
+
798
+
799
+ internals.fillOrderedErrors = function (schema, errors, ordereds, value, state, prefs) {
800
+
801
+ const requiredOrdereds = [];
802
+
803
+ for (const ordered of ordereds) {
804
+ if (ordered._flags.presence === 'required') {
805
+ requiredOrdereds.push(ordered);
806
+ }
807
+ }
808
+
809
+ if (requiredOrdereds.length) {
810
+ internals.fillMissedErrors(schema, errors, requiredOrdereds, value, state, prefs);
811
+ }
812
+ };
813
+
814
+
815
+ internals.fillDefault = function (ordereds, value, state, prefs) {
816
+
817
+ const overrides = [];
818
+ let trailingUndefined = true;
819
+
820
+ for (let i = ordereds.length - 1; i >= 0; --i) {
821
+ const ordered = ordereds[i];
822
+ const ancestors = [value, ...state.ancestors];
823
+ const override = ordered.$_validate(undefined, state.localize(state.path, ancestors, ordered), prefs).value;
824
+
825
+ if (trailingUndefined) {
826
+ if (override === undefined) {
827
+ continue;
828
+ }
829
+
830
+ trailingUndefined = false;
831
+ }
832
+
833
+ overrides.unshift(override);
834
+ }
835
+
836
+ if (overrides.length) {
837
+ value.push(...overrides);
838
+ }
839
+ };
840
+
841
+
842
+ internals.fastSplice = function (arr, i) {
843
+
844
+ let pos = i;
845
+ while (pos < arr.length) {
846
+ arr[pos++] = arr[pos];
847
+ }
848
+
849
+ --arr.length;
850
+ };
851
+
852
+
853
+ internals.validateSingle = function (type, obj) {
854
+
855
+ if (type.type === 'array' ||
856
+ type._flags._arrayItems) {
857
+
858
+ assert(!obj._flags.single, 'Cannot specify array item with single rule enabled');
859
+ obj.$_setFlag('_arrayItems', true, { clone: false });
860
+ }
861
+ };
862
+
863
+
864
+ internals.sort = function (schema, value, settings, state, prefs) {
865
+
866
+ const order = settings.order === 'ascending' ? 1 : -1;
867
+ const aFirst = -1 * order;
868
+ const bFirst = order;
869
+
870
+ const sort = (a, b) => {
871
+
872
+ let compare = internals.compare(a, b, aFirst, bFirst);
873
+ if (compare !== null) {
874
+ return compare;
875
+ }
876
+
877
+ if (settings.by) {
878
+ a = settings.by.resolve(a, state, prefs);
879
+ b = settings.by.resolve(b, state, prefs);
880
+ }
881
+
882
+ compare = internals.compare(a, b, aFirst, bFirst);
883
+ if (compare !== null) {
884
+ return compare;
885
+ }
886
+
887
+ const type = typeof a;
888
+ if (type !== typeof b) {
889
+ throw schema.$_createError('array.sort.mismatching', value, null, state, prefs);
890
+ }
891
+
892
+ if (type !== 'number' &&
893
+ type !== 'string') {
894
+
895
+ throw schema.$_createError('array.sort.unsupported', value, { type }, state, prefs);
896
+ }
897
+
898
+ if (type === 'number') {
899
+ return (a - b) * order;
900
+ }
901
+
902
+ return a < b ? aFirst : bFirst;
903
+ };
904
+
905
+ try {
906
+ return { value: value.slice().sort(sort) };
907
+ }
908
+ catch (err) {
909
+ return { errors: err };
910
+ }
911
+ };
912
+
913
+
914
+ internals.compare = function (a, b, aFirst, bFirst) {
915
+
916
+ if (a === b) {
917
+ return 0;
918
+ }
919
+
920
+ if (a === undefined) {
921
+ return 1; // Always last regardless of sort order
922
+ }
923
+
924
+ if (b === undefined) {
925
+ return -1; // Always last regardless of sort order
926
+ }
927
+
928
+ if (a === null) {
929
+ return bFirst;
930
+ }
931
+
932
+ if (b === null) {
933
+ return aFirst;
934
+ }
935
+
936
+ return null;
937
+ };