UNPKG

17.1 kBJavaScriptView Raw
1function _defineProperty(obj, key, value) {
2 if (key in obj) {
3 Object.defineProperty(obj, key, {
4 value: value,
5 enumerable: true,
6 configurable: true,
7 writable: true
8 });
9 } else {
10 obj[key] = value;
11 }
12
13 return obj;
14}
15
16function ownKeys(object, enumerableOnly) {
17 var keys = Object.keys(object);
18
19 if (Object.getOwnPropertySymbols) {
20 var symbols = Object.getOwnPropertySymbols(object);
21 if (enumerableOnly) symbols = symbols.filter(function (sym) {
22 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
23 });
24 keys.push.apply(keys, symbols);
25 }
26
27 return keys;
28}
29
30function _objectSpread2(target) {
31 for (var i = 1; i < arguments.length; i++) {
32 var source = arguments[i] != null ? arguments[i] : {};
33
34 if (i % 2) {
35 ownKeys(Object(source), true).forEach(function (key) {
36 _defineProperty(target, key, source[key]);
37 });
38 } else if (Object.getOwnPropertyDescriptors) {
39 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
40 } else {
41 ownKeys(Object(source)).forEach(function (key) {
42 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
43 });
44 }
45 }
46
47 return target;
48}
49
50function _objectWithoutPropertiesLoose(source, excluded) {
51 if (source == null) return {};
52 var target = {};
53 var sourceKeys = Object.keys(source);
54 var key, i;
55
56 for (i = 0; i < sourceKeys.length; i++) {
57 key = sourceKeys[i];
58 if (excluded.indexOf(key) >= 0) continue;
59 target[key] = source[key];
60 }
61
62 return target;
63}
64
65function _objectWithoutProperties(source, excluded) {
66 if (source == null) return {};
67
68 var target = _objectWithoutPropertiesLoose(source, excluded);
69
70 var key, i;
71
72 if (Object.getOwnPropertySymbols) {
73 var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
74
75 for (i = 0; i < sourceSymbolKeys.length; i++) {
76 key = sourceSymbolKeys[i];
77 if (excluded.indexOf(key) >= 0) continue;
78 if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
79 target[key] = source[key];
80 }
81 }
82
83 return target;
84}
85
86/**
87 * Convert a validation result to an iterable of failures.
88 */
89function toFailures(result, context) {
90 if (result === true) {
91 return [];
92 } else if (result === false) {
93 return [context.fail()];
94 } else {
95 return result;
96 }
97}
98
99/**
100 * `Struct` objects encapsulate the schema for a specific data type (with
101 * optional coercion). You can then use the `assert`, `is` or `validate` helpers
102 * to validate unknown data against a struct.
103 */
104
105class Struct {
106 constructor(props) {
107 const {
108 type,
109 schema,
110 coercer = value => value,
111 validator = () => [],
112 refiner = () => []
113 } = props;
114 this.type = type;
115 this.schema = schema;
116 this.coercer = coercer;
117 this.validator = validator;
118 this.refiner = refiner;
119 }
120
121}
122/**
123 * `StructError` objects are thrown (or returned) by Superstruct when its
124 * validation fails. The error represents the first error encountered during
125 * validation. But they also have an `error.failures` property that holds
126 * information for all of the failures encountered.
127 */
128
129class StructError extends TypeError {
130 constructor(failure, iterable) {
131 const {
132 path,
133 value,
134 type,
135 branch
136 } = failure,
137 rest = _objectWithoutProperties(failure, ["path", "value", "type", "branch"]);
138
139 const message = `Expected a value of type \`${type}\`${path.length ? ` for \`${path.join('.')}\`` : ''} but received \`${JSON.stringify(value)}\`.`;
140
141 function* failures() {
142 yield failure;
143 yield* iterable;
144 }
145
146 super(message);
147 this.value = value;
148 Object.assign(this, rest);
149 this.type = type;
150 this.path = path;
151 this.branch = branch;
152 this.failures = failures;
153 this.stack = new Error().stack;
154 this.__proto__ = StructError.prototype;
155 }
156
157}
158/**
159 * Assert that a value passes a `Struct`, throwing if it doesn't.
160 */
161
162function assert(value, struct) {
163 const result = validate(value, struct);
164
165 if (result[0]) {
166 throw result[0];
167 }
168}
169/**
170 * Coerce a value with the coercion logic of `Struct` and validate it.
171 */
172
173function coerce(value, struct) {
174 const ret = struct.coercer(value);
175 assert(ret, struct);
176 return ret;
177}
178/**
179 * Check if a value passes a `Struct`.
180 */
181
182function is(value, struct) {
183 const result = validate(value, struct);
184 return !!result[0];
185}
186/**
187 * Validate a value against a `Struct`, returning an error if invalid.
188 */
189
190function validate(value, struct, coercing = false) {
191 if (coercing) {
192 value = struct.coercer(value);
193 }
194
195 const iterable = check(value, struct);
196 const [failure] = iterable;
197
198 if (failure) {
199 const error = new StructError(failure, iterable);
200 return [error, undefined];
201 } else {
202 return [undefined, value];
203 }
204}
205/**
206 * Check a value against a `Struct`, returning an iterable of failures.
207 */
208
209function* check(value, struct, path = [], branch = [value]) {
210 const {
211 type
212 } = struct;
213 const ctx = {
214 value,
215 type,
216 branch,
217 path,
218
219 fail(props = {}) {
220 return _objectSpread2({
221 value,
222 type,
223 path,
224 branch
225 }, props);
226 },
227
228 check(v, s, parent, key) {
229 const p = parent !== undefined ? path.concat(key) : path;
230 const b = parent !== undefined ? branch.concat(parent) : branch;
231 return check(v, s, p, b);
232 }
233
234 };
235 const failures = toFailures(struct.validator(value, ctx), ctx);
236 const [failure] = failures;
237
238 if (failure) {
239 yield failure;
240 yield* failures;
241 } else {
242 yield* toFailures(struct.refiner(value, ctx), ctx);
243 }
244}
245
246/**
247 * Augment a `Struct` to add an additional coercion step to its input.
248 */
249
250function coercion(struct, coercer) {
251 const fn = struct.coercer;
252 return new Struct(_objectSpread2(_objectSpread2({}, struct), {}, {
253 coercer: value => {
254 return fn(coercer(value));
255 }
256 }));
257}
258/**
259 * Augment a struct to coerce a default value for missing values.
260 *
261 * Note: You must use `coerce(value, Struct)` on the value before validating it
262 * to have the value defaulted!
263 */
264
265function defaulted(S, fallback, strict) {
266 return coercion(S, x => {
267 const f = typeof fallback === 'function' ? fallback() : fallback;
268
269 if (x === undefined) {
270 return f;
271 }
272
273 if (strict !== true && isPlainObject(x) && isPlainObject(f)) {
274 const ret = _objectSpread2({}, x);
275
276 let changed = false;
277
278 for (const key in f) {
279 if (ret[key] === undefined) {
280 ret[key] = f[key];
281 changed = true;
282 }
283 }
284
285 if (changed) {
286 return ret;
287 }
288 }
289
290 return x;
291 });
292}
293/**
294 * Coerce a value to mask its properties to only that defined in the struct.
295 */
296
297function masked(S) {
298 return coercion(S, x => {
299 if (!isPlainObject(x)) {
300 return x;
301 }
302
303 const ret = {};
304
305 for (const key in S.schema) {
306 ret[key] = x[key];
307 }
308
309 return ret;
310 });
311}
312/**
313 * Check if a value is a plain object.
314 */
315
316function isPlainObject(value) {
317 if (Object.prototype.toString.call(value) !== '[object Object]') {
318 return false;
319 }
320
321 const prototype = Object.getPrototypeOf(value);
322 return prototype === null || prototype === Object.prototype;
323}
324
325/**
326 * Augment a string or array struct to constrain its length to being between a
327 * minimum and maximum size.
328 */
329
330function length(S, min, max) {
331 return refinement(S, `${S.type} & Length<${min},${max}>`, value => {
332 return min < value.length && value.length < max;
333 });
334}
335/**
336 * Refine a string struct to match a specific regexp pattern.
337 */
338
339function pattern(S, regexp) {
340 return refinement(S, `${S.type} & Pattern<${regexp.source}>`, value => {
341 return regexp.test(value);
342 });
343}
344/**
345 * Augment a `Struct` to add an additional refinement to the validation.
346 */
347
348function refinement(struct, type, refiner) {
349 const fn = struct.refiner;
350 return new Struct(_objectSpread2(_objectSpread2({}, struct), {}, {
351 type,
352
353 *refiner(value, fail) {
354 yield* toFailures(fn(value, fail), fail);
355 yield* toFailures(refiner(value, fail), fail);
356 }
357
358 }));
359}
360
361/**
362 * Validate any value.
363 */
364
365function any() {
366 return struct('any', () => true);
367}
368/**
369 * Validate that an array of values of a specific type.
370 */
371
372function array(Element) {
373 return new Struct({
374 type: `Array<${Element.type}>`,
375 schema: Element,
376 coercer: value => {
377 return Array.isArray(value) ? value.map(v => coerce(v, Element)) : value;
378 },
379
380 *validator(value, ctx) {
381 if (!Array.isArray(value)) {
382 yield ctx.fail();
383 return;
384 }
385
386 for (const [i, v] of value.entries()) {
387 yield* ctx.check(v, Element, value, i);
388 }
389 }
390
391 });
392}
393/**
394 * Validate that boolean values.
395 */
396
397function boolean() {
398 return struct('boolean', value => {
399 return typeof value === 'boolean';
400 });
401}
402/**
403 * Validate that `Date` values.
404 *
405 * Note: this also ensures that the value is *not* an invalid `Date` object,
406 * which can occur when parsing a date fails but still returns a `Date`.
407 */
408
409function date() {
410 return struct('Date', value => {
411 return value instanceof Date && !isNaN(value.getTime());
412 });
413}
414/**
415 * Validate that a value dynamically, determing which struct to use at runtime.
416 */
417
418function dynamic(fn) {
419 return struct('Dynamic<...>', (value, ctx) => {
420 return ctx.check(value, fn(value, ctx));
421 });
422}
423/**
424 * Validate that a value against a set of potential values.
425 */
426
427function enums(values) {
428 return struct(`Enum<${values.map(toLiteralString)}>`, value => {
429 return values.includes(value);
430 });
431}
432/**
433 * Validate that a value is a function.
434 */
435
436function func() {
437 return struct('Function', value => {
438 return typeof value === 'function';
439 });
440}
441/**
442 * Validate that a value is an instance of a class.
443 */
444
445function instance(Class) {
446 return struct(`InstanceOf<${Class.name}>`, value => {
447 return value instanceof Class;
448 });
449}
450function intersection(Structs) {
451 return struct(Structs.map(s => s.type).join(' & '), function* (value, ctx) {
452 for (const S of Structs) {
453 yield* ctx.check(value, S);
454 }
455 });
456}
457/**
458 * Validate a value lazily, by constructing the struct right before the first
459 * validation. This is useful for cases where you want to have self-referential
460 * structs for nested data structures.
461 */
462
463function lazy(fn) {
464 let S;
465 return struct('Lazy<...>', (value, ctx) => {
466 if (!S) {
467 S = fn();
468 }
469
470 return ctx.check(value, S);
471 });
472}
473/**
474 * Validate that a value is a specific constant.
475 */
476
477function literal(constant) {
478 return struct(`Literal<${toLiteralString(constant)}>`, value => {
479 return value === constant;
480 });
481}
482/**
483 * Validate that a value is a map with specific key and value entries.
484 */
485
486function map(Key, Value) {
487 return struct(`Map<${Key.type},${Value.type}>`, function* (value, ctx) {
488 if (!(value instanceof Map)) {
489 yield ctx.fail();
490 return;
491 }
492
493 for (const [k, v] of value.entries()) {
494 yield* ctx.check(k, Key, value, k);
495 yield* ctx.check(v, Value, value, k);
496 }
497 });
498}
499/**
500 * Validate that a value always fails.
501 */
502
503function never() {
504 return struct('never', () => false);
505}
506/**
507 * Validate that a value is a number.
508 */
509
510function number() {
511 return struct(`number`, value => {
512 return typeof value === 'number' && !isNaN(value);
513 });
514}
515/**
516 * Validate that an object with specific entry values.
517 */
518
519function object(Structs) {
520 const knowns = Object.keys(Structs);
521 const Never = never();
522 return new Struct({
523 type: `Object<{${knowns.join(',')}}>`,
524 schema: Structs,
525 coercer: createObjectCoercer(Structs),
526
527 *validator(value, ctx) {
528 if (typeof value !== 'object' || value == null) {
529 yield ctx.fail();
530 return;
531 }
532
533 const unknowns = new Set(Object.keys(value));
534
535 for (const key of knowns) {
536 unknowns.delete(key);
537 const Value = Structs[key];
538 const v = value[key];
539 yield* ctx.check(v, Value, value, key);
540 }
541
542 for (const key of unknowns) {
543 const v = value[key];
544 yield* ctx.check(v, Never, value, key);
545 }
546 }
547
548 });
549}
550/**
551 * Augment a struct to make it accept optionally accept `undefined` values.
552 */
553
554function optional(S) {
555 return new Struct({
556 type: `${S.type}?`,
557 schema: S.schema,
558 validator: (value, ctx) => {
559 return value === undefined || ctx.check(value, S);
560 }
561 });
562}
563/**
564 * Validate that a partial object with specific entry values.
565 */
566
567function partial(Structs) {
568 if (Structs instanceof Struct) {
569 Structs = Structs.schema;
570 }
571
572 const knowns = Object.keys(Structs);
573 const Never = never();
574 return new Struct({
575 type: `Partial<{${knowns.join(',')}}>`,
576 schema: Structs,
577 coercer: createObjectCoercer(Structs),
578
579 *validator(value, ctx) {
580 if (typeof value !== 'object' || value == null) {
581 yield ctx.fail();
582 return;
583 }
584
585 const unknowns = new Set(Object.keys(value));
586
587 for (const key of knowns) {
588 unknowns.delete(key);
589
590 if (!(key in value)) {
591 continue;
592 }
593
594 const Value = Structs[key];
595 const v = value[key];
596 yield* ctx.check(v, Value, value, key);
597 }
598
599 for (const key of unknowns) {
600 const v = value[key];
601 yield* ctx.check(v, Never, value, key);
602 }
603 }
604
605 });
606}
607/**
608 * Validate that a value is a record with specific key and
609 * value entries.
610 */
611
612function record(Key, Value) {
613 return struct(`Record<${Key.type},${Value.type}>`, function* (value, ctx) {
614 if (typeof value !== 'object' || value == null) {
615 yield ctx.fail();
616 return;
617 }
618
619 for (const k in value) {
620 const v = value[k];
621 yield* ctx.check(k, Key, value, k);
622 yield* ctx.check(v, Value, value, k);
623 }
624 });
625}
626/**
627 * Validate that a set of values matches a specific type.
628 */
629
630function set(Element) {
631 return struct(`Set<${Element.type}>`, (value, ctx) => {
632 if (!(value instanceof Set)) {
633 return false;
634 }
635
636 for (const val of value) {
637 const [failure] = ctx.check(val, Element);
638
639 if (failure) {
640 return false;
641 }
642 }
643
644 return true;
645 });
646}
647/**
648 * Validate that a value is a string.
649 */
650
651function string() {
652 return struct('string', value => {
653 return typeof value === 'string';
654 });
655}
656/**
657 * Define a `Struct` instance with a type and validation function.
658 */
659
660function struct(name, validator) {
661 return new Struct({
662 type: name,
663 validator,
664 schema: null
665 });
666}
667function tuple(Elements) {
668 const Never = never();
669 return struct(`[${Elements.map(s => s.type).join(',')}]`, function* (value, ctx) {
670 if (!Array.isArray(value)) {
671 yield ctx.fail();
672 return;
673 }
674
675 for (const [index, Element] of Elements.entries()) {
676 const v = value[index];
677 yield* ctx.check(v, Element, value, index);
678 }
679
680 if (value.length > Elements.length) {
681 const index = Elements.length;
682 const v = value[index];
683 yield* ctx.check(v, Never, value, index);
684 }
685 });
686}
687/**
688 * Validate that a value matches a specific strutural interface, like the
689 * structural typing that TypeScript uses.
690 */
691
692function type(Structs) {
693 const keys = Object.keys(Structs);
694 return struct(`Type<{${keys.join(',')}}>`, function* (value, ctx) {
695 if (typeof value !== 'object' || value == null) {
696 yield ctx.fail();
697 return;
698 }
699
700 for (const key of keys) {
701 const Value = Structs[key];
702 const v = value[key];
703 yield* ctx.check(v, Value, value, key);
704 }
705 });
706}
707function union(Structs) {
708 return struct(`${Structs.map(s => s.type).join(' | ')}`, function* (value, ctx) {
709 for (const S of Structs) {
710 const [...failures] = ctx.check(value, S);
711
712 if (failures.length === 0) {
713 return;
714 }
715 }
716
717 yield ctx.fail();
718 });
719}
720/**
721 * Convert a value to a literal string.
722 */
723
724function toLiteralString(value) {
725 return typeof value === 'string' ? `"${value.replace(/"/g, '"')}"` : `${value}`;
726}
727/**
728 * Coerce the values of an object-like struct.
729 */
730
731
732function createObjectCoercer(Structs) {
733 const knowns = Object.keys(Structs);
734 return value => {
735 if (typeof value !== 'object' || value == null) {
736 return value;
737 }
738
739 const ret = {};
740 const unknowns = new Set(Object.keys(value));
741
742 for (const key of knowns) {
743 unknowns.delete(key);
744 const Value = Structs[key];
745 const v = value[key];
746 ret[key] = coerce(v, Value);
747 }
748
749 for (const key of unknowns) {
750 ret[key] = value[key];
751 }
752
753 return ret;
754 };
755}
756
757export { Struct, StructError, any, array, assert, boolean, coerce, coercion, date, defaulted, dynamic, enums, func, instance, intersection, is, lazy, length, literal, map, masked, never, number, object, optional, partial, pattern, record, refinement, set, string, struct, tuple, type, union, validate };
758//# sourceMappingURL=index.es.js.map