UNPKG

27.1 kBJavaScriptView Raw
1/**
2 * A `StructFailure` represents a single specific failure in validation.
3 */
4
5/**
6 * `StructError` objects are thrown (or returned) when validation fails.
7 *
8 * Validation logic is design to exit early for maximum performance. The error
9 * represents the first error encountered during validation. For more detail,
10 * the `error.failures` property is a generator function that can be run to
11 * continue validation and receive all the failures in the data.
12 */
13class StructError extends TypeError {
14 constructor(failure, failures) {
15 let cached;
16 const {
17 message,
18 ...rest
19 } = failure;
20 const {
21 path
22 } = failure;
23 const msg = path.length === 0 ? message : "At path: " + path.join('.') + " -- " + message;
24 super(msg);
25 this.value = void 0;
26 this.key = void 0;
27 this.type = void 0;
28 this.refinement = void 0;
29 this.path = void 0;
30 this.branch = void 0;
31 this.failures = void 0;
32 Object.assign(this, rest);
33 this.name = this.constructor.name;
34
35 this.failures = () => {
36 var _cached;
37
38 return (_cached = cached) != null ? _cached : cached = [failure, ...failures()];
39 };
40 }
41
42}
43
44/**
45 * Check if a value is an iterator.
46 */
47function isIterable(x) {
48 return isObject(x) && typeof x[Symbol.iterator] === 'function';
49}
50/**
51 * Check if a value is a plain object.
52 */
53
54
55function isObject(x) {
56 return typeof x === 'object' && x != null;
57}
58/**
59 * Check if a value is a plain object.
60 */
61
62function isPlainObject(x) {
63 if (Object.prototype.toString.call(x) !== '[object Object]') {
64 return false;
65 }
66
67 const prototype = Object.getPrototypeOf(x);
68 return prototype === null || prototype === Object.prototype;
69}
70/**
71 * Return a value as a printable string.
72 */
73
74function print(value) {
75 return typeof value === 'string' ? JSON.stringify(value) : "" + value;
76}
77/**
78 * Shifts (removes and returns) the first value from the `input` iterator.
79 * Like `Array.prototype.shift()` but for an `Iterator`.
80 */
81
82function shiftIterator(input) {
83 const {
84 done,
85 value
86 } = input.next();
87 return done ? undefined : value;
88}
89/**
90 * Convert a single validation result to a failure.
91 */
92
93function toFailure(result, context, struct, value) {
94 if (result === true) {
95 return;
96 } else if (result === false) {
97 result = {};
98 } else if (typeof result === 'string') {
99 result = {
100 message: result
101 };
102 }
103
104 const {
105 path,
106 branch
107 } = context;
108 const {
109 type
110 } = struct;
111 const {
112 refinement,
113 message = "Expected a value of type `" + type + "`" + (refinement ? " with refinement `" + refinement + "`" : '') + ", but received: `" + print(value) + "`"
114 } = result;
115 return {
116 value,
117 type,
118 refinement,
119 key: path[path.length - 1],
120 path,
121 branch,
122 ...result,
123 message
124 };
125}
126/**
127 * Convert a validation result to an iterable of failures.
128 */
129
130function* toFailures(result, context, struct, value) {
131 if (!isIterable(result)) {
132 result = [result];
133 }
134
135 for (const r of result) {
136 const failure = toFailure(r, context, struct, value);
137
138 if (failure) {
139 yield failure;
140 }
141 }
142}
143/**
144 * Check a value against a struct, traversing deeply into nested values, and
145 * returning an iterator of failures or success.
146 */
147
148function* run(value, struct, options = {}) {
149 const {
150 path = [],
151 branch = [value],
152 coerce = false,
153 mask = false
154 } = options;
155 const ctx = {
156 path,
157 branch
158 };
159
160 if (coerce) {
161 value = struct.coercer(value, ctx);
162
163 if (mask && struct.type !== 'type' && isObject(struct.schema) && isObject(value) && !Array.isArray(value)) {
164 for (const key in value) {
165 if (struct.schema[key] === undefined) {
166 delete value[key];
167 }
168 }
169 }
170 }
171
172 let valid = true;
173
174 for (const failure of struct.validator(value, ctx)) {
175 valid = false;
176 yield [failure, undefined];
177 }
178
179 for (let [k, v, s] of struct.entries(value, ctx)) {
180 const ts = run(v, s, {
181 path: k === undefined ? path : [...path, k],
182 branch: k === undefined ? branch : [...branch, v],
183 coerce,
184 mask
185 });
186
187 for (const t of ts) {
188 if (t[0]) {
189 valid = false;
190 yield [t[0], undefined];
191 } else if (coerce) {
192 v = t[1];
193
194 if (k === undefined) {
195 value = v;
196 } else if (value instanceof Map) {
197 value.set(k, v);
198 } else if (value instanceof Set) {
199 value.add(v);
200 } else if (isObject(value)) {
201 value[k] = v;
202 }
203 }
204 }
205 }
206
207 if (valid) {
208 for (const failure of struct.refiner(value, ctx)) {
209 valid = false;
210 yield [failure, undefined];
211 }
212 }
213
214 if (valid) {
215 yield [undefined, value];
216 }
217}
218
219/**
220 * `Struct` objects encapsulate the validation logic for a specific type of
221 * values. Once constructed, you use the `assert`, `is` or `validate` helpers to
222 * validate unknown input data against the struct.
223 */
224
225class Struct {
226 constructor(props) {
227 this.TYPE = void 0;
228 this.type = void 0;
229 this.schema = void 0;
230 this.coercer = void 0;
231 this.validator = void 0;
232 this.refiner = void 0;
233 this.entries = void 0;
234 const {
235 type,
236 schema,
237 validator,
238 refiner,
239 coercer = value => value,
240 entries = function* () {}
241 } = props;
242 this.type = type;
243 this.schema = schema;
244 this.entries = entries;
245 this.coercer = coercer;
246
247 if (validator) {
248 this.validator = (value, context) => {
249 const result = validator(value, context);
250 return toFailures(result, context, this, value);
251 };
252 } else {
253 this.validator = () => [];
254 }
255
256 if (refiner) {
257 this.refiner = (value, context) => {
258 const result = refiner(value, context);
259 return toFailures(result, context, this, value);
260 };
261 } else {
262 this.refiner = () => [];
263 }
264 }
265 /**
266 * Assert that a value passes the struct's validation, throwing if it doesn't.
267 */
268
269
270 assert(value) {
271 return assert(value, this);
272 }
273 /**
274 * Create a value with the struct's coercion logic, then validate it.
275 */
276
277
278 create(value) {
279 return create(value, this);
280 }
281 /**
282 * Check if a value passes the struct's validation.
283 */
284
285
286 is(value) {
287 return is(value, this);
288 }
289 /**
290 * Mask a value, coercing and validating it, but returning only the subset of
291 * properties defined by the struct's schema.
292 */
293
294
295 mask(value) {
296 return mask(value, this);
297 }
298 /**
299 * Validate a value with the struct's validation logic, returning a tuple
300 * representing the result.
301 *
302 * You may optionally pass `true` for the `withCoercion` argument to coerce
303 * the value before attempting to validate it. If you do, the result will
304 * contain the coerced result when successful.
305 */
306
307
308 validate(value, options = {}) {
309 return validate(value, this, options);
310 }
311
312}
313/**
314 * Assert that a value passes a struct, throwing if it doesn't.
315 */
316
317function assert(value, struct) {
318 const result = validate(value, struct);
319
320 if (result[0]) {
321 throw result[0];
322 }
323}
324/**
325 * Create a value with the coercion logic of struct and validate it.
326 */
327
328function create(value, struct) {
329 const result = validate(value, struct, {
330 coerce: true
331 });
332
333 if (result[0]) {
334 throw result[0];
335 } else {
336 return result[1];
337 }
338}
339/**
340 * Mask a value, returning only the subset of properties defined by a struct.
341 */
342
343function mask(value, struct) {
344 const result = validate(value, struct, {
345 coerce: true,
346 mask: true
347 });
348
349 if (result[0]) {
350 throw result[0];
351 } else {
352 return result[1];
353 }
354}
355/**
356 * Check if a value passes a struct.
357 */
358
359function is(value, struct) {
360 const result = validate(value, struct);
361 return !result[0];
362}
363/**
364 * Validate a value against a struct, returning an error if invalid, or the
365 * value (with potential coercion) if valid.
366 */
367
368function validate(value, struct, options = {}) {
369 const tuples = run(value, struct, options);
370 const tuple = shiftIterator(tuples);
371
372 if (tuple[0]) {
373 const error = new StructError(tuple[0], function* () {
374 for (const t of tuples) {
375 if (t[0]) {
376 yield t[0];
377 }
378 }
379 });
380 return [error, undefined];
381 } else {
382 const v = tuple[1];
383 return [undefined, v];
384 }
385}
386
387function assign(...Structs) {
388 const isType = Structs[0].type === 'type';
389 const schemas = Structs.map(s => s.schema);
390 const schema = Object.assign({}, ...schemas);
391 return isType ? type(schema) : object(schema);
392}
393/**
394 * Define a new struct type with a custom validation function.
395 */
396
397function define(name, validator) {
398 return new Struct({
399 type: name,
400 schema: null,
401 validator
402 });
403}
404/**
405 * Create a new struct based on an existing struct, but the value is allowed to
406 * be `undefined`. `log` will be called if the value is not `undefined`.
407 */
408
409function deprecated(struct, log) {
410 return new Struct({ ...struct,
411 refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx),
412
413 validator(value, ctx) {
414 if (value === undefined) {
415 return true;
416 } else {
417 log(value, ctx);
418 return struct.validator(value, ctx);
419 }
420 }
421
422 });
423}
424/**
425 * Create a struct with dynamic validation logic.
426 *
427 * The callback will receive the value currently being validated, and must
428 * return a struct object to validate it with. This can be useful to model
429 * validation logic that changes based on its input.
430 */
431
432function dynamic(fn) {
433 return new Struct({
434 type: 'dynamic',
435 schema: null,
436
437 *entries(value, ctx) {
438 const struct = fn(value, ctx);
439 yield* struct.entries(value, ctx);
440 },
441
442 validator(value, ctx) {
443 const struct = fn(value, ctx);
444 return struct.validator(value, ctx);
445 },
446
447 coercer(value, ctx) {
448 const struct = fn(value, ctx);
449 return struct.coercer(value, ctx);
450 }
451
452 });
453}
454/**
455 * Create a struct with lazily evaluated validation logic.
456 *
457 * The first time validation is run with the struct, the callback will be called
458 * and must return a struct object to use. This is useful for cases where you
459 * want to have self-referential structs for nested data structures to avoid a
460 * circular definition problem.
461 */
462
463function lazy(fn) {
464 let struct;
465 return new Struct({
466 type: 'lazy',
467 schema: null,
468
469 *entries(value, ctx) {
470 var _struct;
471
472 (_struct = struct) != null ? _struct : struct = fn();
473 yield* struct.entries(value, ctx);
474 },
475
476 validator(value, ctx) {
477 var _struct2;
478
479 (_struct2 = struct) != null ? _struct2 : struct = fn();
480 return struct.validator(value, ctx);
481 },
482
483 coercer(value, ctx) {
484 var _struct3;
485
486 (_struct3 = struct) != null ? _struct3 : struct = fn();
487 return struct.coercer(value, ctx);
488 }
489
490 });
491}
492/**
493 * Create a new struct based on an existing object struct, but excluding
494 * specific properties.
495 *
496 * Like TypeScript's `Omit` utility.
497 */
498
499function omit(struct, keys) {
500 const {
501 schema
502 } = struct;
503 const subschema = { ...schema
504 };
505
506 for (const key of keys) {
507 delete subschema[key];
508 }
509
510 return object(subschema);
511}
512/**
513 * Create a new struct based on an existing object struct, but with all of its
514 * properties allowed to be `undefined`.
515 *
516 * Like TypeScript's `Partial` utility.
517 */
518
519function partial(struct) {
520 const schema = struct instanceof Struct ? { ...struct.schema
521 } : { ...struct
522 };
523
524 for (const key in schema) {
525 schema[key] = optional(schema[key]);
526 }
527
528 return object(schema);
529}
530/**
531 * Create a new struct based on an existing object struct, but only including
532 * specific properties.
533 *
534 * Like TypeScript's `Pick` utility.
535 */
536
537function pick(struct, keys) {
538 const {
539 schema
540 } = struct;
541 const subschema = {};
542
543 for (const key of keys) {
544 subschema[key] = schema[key];
545 }
546
547 return object(subschema);
548}
549/**
550 * Define a new struct type with a custom validation function.
551 *
552 * @deprecated This function has been renamed to `define`.
553 */
554
555function struct(name, validator) {
556 console.warn('superstruct@0.11 - The `struct` helper has been renamed to `define`.');
557 return define(name, validator);
558}
559
560/**
561 * Ensure that any value passes validation.
562 */
563
564function any() {
565 return define('any', () => true);
566}
567function array(Element) {
568 return new Struct({
569 type: 'array',
570 schema: Element,
571
572 *entries(value) {
573 if (Element && Array.isArray(value)) {
574 for (const [i, v] of value.entries()) {
575 yield [i, v, Element];
576 }
577 }
578 },
579
580 coercer(value) {
581 return Array.isArray(value) ? value.slice() : value;
582 },
583
584 validator(value) {
585 return Array.isArray(value) || "Expected an array value, but received: " + print(value);
586 }
587
588 });
589}
590/**
591 * Ensure that a value is a bigint.
592 */
593
594function bigint() {
595 return define('bigint', value => {
596 return typeof value === 'bigint';
597 });
598}
599/**
600 * Ensure that a value is a boolean.
601 */
602
603function boolean() {
604 return define('boolean', value => {
605 return typeof value === 'boolean';
606 });
607}
608/**
609 * Ensure that a value is a valid `Date`.
610 *
611 * Note: this also ensures that the value is *not* an invalid `Date` object,
612 * which can occur when parsing a date fails but still returns a `Date`.
613 */
614
615function date() {
616 return define('date', value => {
617 return value instanceof Date && !isNaN(value.getTime()) || "Expected a valid `Date` object, but received: " + print(value);
618 });
619}
620function enums(values) {
621 const schema = {};
622 const description = values.map(v => print(v)).join();
623
624 for (const key of values) {
625 schema[key] = key;
626 }
627
628 return new Struct({
629 type: 'enums',
630 schema,
631
632 validator(value) {
633 return values.includes(value) || "Expected one of `" + description + "`, but received: " + print(value);
634 }
635
636 });
637}
638/**
639 * Ensure that a value is a function.
640 */
641
642function func() {
643 return define('func', value => {
644 return typeof value === 'function' || "Expected a function, but received: " + print(value);
645 });
646}
647/**
648 * Ensure that a value is an instance of a specific class.
649 */
650
651function instance(Class) {
652 return define('instance', value => {
653 return value instanceof Class || "Expected a `" + Class.name + "` instance, but received: " + print(value);
654 });
655}
656/**
657 * Ensure that a value is an integer.
658 */
659
660function integer() {
661 return define('integer', value => {
662 return typeof value === 'number' && !isNaN(value) && Number.isInteger(value) || "Expected an integer, but received: " + print(value);
663 });
664}
665/**
666 * Ensure that a value matches all of a set of types.
667 */
668
669function intersection(Structs) {
670 return new Struct({
671 type: 'intersection',
672 schema: null,
673
674 *entries(value, ctx) {
675 for (const S of Structs) {
676 yield* S.entries(value, ctx);
677 }
678 },
679
680 *validator(value, ctx) {
681 for (const S of Structs) {
682 yield* S.validator(value, ctx);
683 }
684 },
685
686 *refiner(value, ctx) {
687 for (const S of Structs) {
688 yield* S.refiner(value, ctx);
689 }
690 }
691
692 });
693}
694function literal(constant) {
695 const description = print(constant);
696 const t = typeof constant;
697 return new Struct({
698 type: 'literal',
699 schema: t === 'string' || t === 'number' || t === 'boolean' ? constant : null,
700
701 validator(value) {
702 return value === constant || "Expected the literal `" + description + "`, but received: " + print(value);
703 }
704
705 });
706}
707function map(Key, Value) {
708 return new Struct({
709 type: 'map',
710 schema: null,
711
712 *entries(value) {
713 if (Key && Value && value instanceof Map) {
714 for (const [k, v] of value.entries()) {
715 yield [k, k, Key];
716 yield [k, v, Value];
717 }
718 }
719 },
720
721 coercer(value) {
722 return value instanceof Map ? new Map(value) : value;
723 },
724
725 validator(value) {
726 return value instanceof Map || "Expected a `Map` object, but received: " + print(value);
727 }
728
729 });
730}
731/**
732 * Ensure that no value ever passes validation.
733 */
734
735function never() {
736 return define('never', () => false);
737}
738/**
739 * Augment an existing struct to allow `null` values.
740 */
741
742function nullable(struct) {
743 return new Struct({ ...struct,
744 validator: (value, ctx) => value === null || struct.validator(value, ctx),
745 refiner: (value, ctx) => value === null || struct.refiner(value, ctx)
746 });
747}
748/**
749 * Ensure that a value is a number.
750 */
751
752function number() {
753 return define('number', value => {
754 return typeof value === 'number' && !isNaN(value) || "Expected a number, but received: " + print(value);
755 });
756}
757function object(schema) {
758 const knowns = schema ? Object.keys(schema) : [];
759 const Never = never();
760 return new Struct({
761 type: 'object',
762 schema: schema ? schema : null,
763
764 *entries(value) {
765 if (schema && isObject(value)) {
766 const unknowns = new Set(Object.keys(value));
767
768 for (const key of knowns) {
769 unknowns.delete(key);
770 yield [key, value[key], schema[key]];
771 }
772
773 for (const key of unknowns) {
774 yield [key, value[key], Never];
775 }
776 }
777 },
778
779 validator(value) {
780 return isObject(value) || "Expected an object, but received: " + print(value);
781 },
782
783 coercer(value) {
784 return isObject(value) ? { ...value
785 } : value;
786 }
787
788 });
789}
790/**
791 * Augment a struct to allow `undefined` values.
792 */
793
794function optional(struct) {
795 return new Struct({ ...struct,
796 validator: (value, ctx) => value === undefined || struct.validator(value, ctx),
797 refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx)
798 });
799}
800/**
801 * Ensure that a value is an object with keys and values of specific types, but
802 * without ensuring any specific shape of properties.
803 *
804 * Like TypeScript's `Record` utility.
805 */
806
807function record(Key, Value) {
808 return new Struct({
809 type: 'record',
810 schema: null,
811
812 *entries(value) {
813 if (isObject(value)) {
814 for (const k in value) {
815 const v = value[k];
816 yield [k, k, Key];
817 yield [k, v, Value];
818 }
819 }
820 },
821
822 validator(value) {
823 return isObject(value) || "Expected an object, but received: " + print(value);
824 }
825
826 });
827}
828/**
829 * Ensure that a value is a `RegExp`.
830 *
831 * Note: this does not test the value against the regular expression! For that
832 * you need to use the `pattern()` refinement.
833 */
834
835function regexp() {
836 return define('regexp', value => {
837 return value instanceof RegExp;
838 });
839}
840function set(Element) {
841 return new Struct({
842 type: 'set',
843 schema: null,
844
845 *entries(value) {
846 if (Element && value instanceof Set) {
847 for (const v of value) {
848 yield [v, v, Element];
849 }
850 }
851 },
852
853 coercer(value) {
854 return value instanceof Set ? new Set(value) : value;
855 },
856
857 validator(value) {
858 return value instanceof Set || "Expected a `Set` object, but received: " + print(value);
859 }
860
861 });
862}
863/**
864 * Ensure that a value is a string.
865 */
866
867function string() {
868 return define('string', value => {
869 return typeof value === 'string' || "Expected a string, but received: " + print(value);
870 });
871}
872/**
873 * Ensure that a value is a tuple of a specific length, and that each of its
874 * elements is of a specific type.
875 */
876
877function tuple(Structs) {
878 const Never = never();
879 return new Struct({
880 type: 'tuple',
881 schema: null,
882
883 *entries(value) {
884 if (Array.isArray(value)) {
885 const length = Math.max(Structs.length, value.length);
886
887 for (let i = 0; i < length; i++) {
888 yield [i, value[i], Structs[i] || Never];
889 }
890 }
891 },
892
893 validator(value) {
894 return Array.isArray(value) || "Expected an array, but received: " + print(value);
895 }
896
897 });
898}
899/**
900 * Ensure that a value has a set of known properties of specific types.
901 *
902 * Note: Unrecognized properties are allowed and untouched. This is similar to
903 * how TypeScript's structural typing works.
904 */
905
906function type(schema) {
907 const keys = Object.keys(schema);
908 return new Struct({
909 type: 'type',
910 schema,
911
912 *entries(value) {
913 if (isObject(value)) {
914 for (const k of keys) {
915 yield [k, value[k], schema[k]];
916 }
917 }
918 },
919
920 validator(value) {
921 return isObject(value) || "Expected an object, but received: " + print(value);
922 }
923
924 });
925}
926/**
927 * Ensure that a value matches one of a set of types.
928 */
929
930function union(Structs) {
931 const description = Structs.map(s => s.type).join(' | ');
932 return new Struct({
933 type: 'union',
934 schema: null,
935
936 coercer(value, ctx) {
937 const firstMatch = Structs.find(s => {
938 const [e] = s.validate(value, {
939 coerce: true
940 });
941 return !e;
942 }) || unknown();
943 return firstMatch.coercer(value, ctx);
944 },
945
946 validator(value, ctx) {
947 const failures = [];
948
949 for (const S of Structs) {
950 const [...tuples] = run(value, S, ctx);
951 const [first] = tuples;
952
953 if (!first[0]) {
954 return [];
955 } else {
956 for (const [failure] of tuples) {
957 if (failure) {
958 failures.push(failure);
959 }
960 }
961 }
962 }
963
964 return ["Expected the value to satisfy a union of `" + description + "`, but received: " + print(value), ...failures];
965 }
966
967 });
968}
969/**
970 * Ensure that any value passes validation, without widening its type to `any`.
971 */
972
973function unknown() {
974 return define('unknown', () => true);
975}
976
977/**
978 * Augment a `Struct` to add an additional coercion step to its input.
979 *
980 * This allows you to transform input data before validating it, to increase the
981 * likelihood that it passes validation—for example for default values, parsing
982 * different formats, etc.
983 *
984 * Note: You must use `create(value, Struct)` on the value to have the coercion
985 * take effect! Using simply `assert()` or `is()` will not use coercion.
986 */
987
988function coerce(struct, condition, coercer) {
989 return new Struct({ ...struct,
990 coercer: (value, ctx) => {
991 return is(value, condition) ? struct.coercer(coercer(value, ctx), ctx) : struct.coercer(value, ctx);
992 }
993 });
994}
995/**
996 * Augment a struct to replace `undefined` values with a default.
997 *
998 * Note: You must use `create(value, Struct)` on the value to have the coercion
999 * take effect! Using simply `assert()` or `is()` will not use coercion.
1000 */
1001
1002function defaulted(struct, fallback, options = {}) {
1003 return coerce(struct, unknown(), x => {
1004 const f = typeof fallback === 'function' ? fallback() : fallback;
1005
1006 if (x === undefined) {
1007 return f;
1008 }
1009
1010 if (!options.strict && isPlainObject(x) && isPlainObject(f)) {
1011 const ret = { ...x
1012 };
1013 let changed = false;
1014
1015 for (const key in f) {
1016 if (ret[key] === undefined) {
1017 ret[key] = f[key];
1018 changed = true;
1019 }
1020 }
1021
1022 if (changed) {
1023 return ret;
1024 }
1025 }
1026
1027 return x;
1028 });
1029}
1030/**
1031 * Augment a struct to trim string inputs.
1032 *
1033 * Note: You must use `create(value, Struct)` on the value to have the coercion
1034 * take effect! Using simply `assert()` or `is()` will not use coercion.
1035 */
1036
1037function trimmed(struct) {
1038 return coerce(struct, string(), x => x.trim());
1039}
1040
1041/**
1042 * Ensure that a string, array, map, or set is empty.
1043 */
1044
1045function empty(struct) {
1046 return refine(struct, 'empty', value => {
1047 const size = getSize(value);
1048 return size === 0 || "Expected an empty " + struct.type + " but received one with a size of `" + size + "`";
1049 });
1050}
1051
1052function getSize(value) {
1053 if (value instanceof Map || value instanceof Set) {
1054 return value.size;
1055 } else {
1056 return value.length;
1057 }
1058}
1059/**
1060 * Ensure that a number or date is below a threshold.
1061 */
1062
1063
1064function max(struct, threshold, options = {}) {
1065 const {
1066 exclusive
1067 } = options;
1068 return refine(struct, 'max', value => {
1069 return exclusive ? value < threshold : value <= threshold || "Expected a " + struct.type + " less than " + (exclusive ? '' : 'or equal to ') + threshold + " but received `" + value + "`";
1070 });
1071}
1072/**
1073 * Ensure that a number or date is above a threshold.
1074 */
1075
1076function min(struct, threshold, options = {}) {
1077 const {
1078 exclusive
1079 } = options;
1080 return refine(struct, 'min', value => {
1081 return exclusive ? value > threshold : value >= threshold || "Expected a " + struct.type + " greater than " + (exclusive ? '' : 'or equal to ') + threshold + " but received `" + value + "`";
1082 });
1083}
1084/**
1085 * Ensure that a string, array, map or set is not empty.
1086 */
1087
1088function nonempty(struct) {
1089 return refine(struct, 'nonempty', value => {
1090 const size = getSize(value);
1091 return size > 0 || "Expected a nonempty " + struct.type + " but received an empty one";
1092 });
1093}
1094/**
1095 * Ensure that a string matches a regular expression.
1096 */
1097
1098function pattern(struct, regexp) {
1099 return refine(struct, 'pattern', value => {
1100 return regexp.test(value) || "Expected a " + struct.type + " matching `/" + regexp.source + "/` but received \"" + value + "\"";
1101 });
1102}
1103/**
1104 * Ensure that a string, array, number, date, map, or set has a size (or length, or time) between `min` and `max`.
1105 */
1106
1107function size(struct, min, max = min) {
1108 const expected = "Expected a " + struct.type;
1109 const of = min === max ? "of `" + min + "`" : "between `" + min + "` and `" + max + "`";
1110 return refine(struct, 'size', value => {
1111 if (typeof value === 'number' || value instanceof Date) {
1112 return min <= value && value <= max || expected + " " + of + " but received `" + value + "`";
1113 } else if (value instanceof Map || value instanceof Set) {
1114 const {
1115 size
1116 } = value;
1117 return min <= size && size <= max || expected + " with a size " + of + " but received one with a size of `" + size + "`";
1118 } else {
1119 const {
1120 length
1121 } = value;
1122 return min <= length && length <= max || expected + " with a length " + of + " but received one with a length of `" + length + "`";
1123 }
1124 });
1125}
1126/**
1127 * Augment a `Struct` to add an additional refinement to the validation.
1128 *
1129 * The refiner function is guaranteed to receive a value of the struct's type,
1130 * because the struct's existing validation will already have passed. This
1131 * allows you to layer additional validation on top of existing structs.
1132 */
1133
1134function refine(struct, name, refiner) {
1135 return new Struct({ ...struct,
1136
1137 *refiner(value, ctx) {
1138 yield* struct.refiner(value, ctx);
1139 const result = refiner(value, ctx);
1140 const failures = toFailures(result, ctx, struct, value);
1141
1142 for (const failure of failures) {
1143 yield { ...failure,
1144 refinement: name
1145 };
1146 }
1147 }
1148
1149 });
1150}
1151
1152export { Struct, StructError, any, array, assert, assign, bigint, boolean, coerce, create, date, defaulted, define, deprecated, dynamic, empty, enums, func, instance, integer, intersection, is, lazy, literal, map, mask, max, min, never, nonempty, nullable, number, object, omit, optional, partial, pattern, pick, record, refine, regexp, set, size, string, struct, trimmed, tuple, type, union, unknown, validate };
1153//# sourceMappingURL=index.es.js.map