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