text: (function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Superstruct = {}));
  }(this, (function (exports) { 'use strict';
  
    /**
     * A `StructFailure` represents a single specific failure in validation.
     */
  
    /**
     * `StructError` objects are thrown (or returned) when validation fails.
     *
     * Validation logic is design to exit early for maximum performance. The error
     * represents the first error encountered during validation. For more detail,
     * the `error.failures` property is a generator function that can be run to
     * continue validation and receive all the failures in the data.
     */
    class StructError extends TypeError {
      constructor(failure, moreFailures) {
        const {
          message,
          ...rest
        } = failure;
        const {
          path
        } = failure;
        let failures;
        const msg = path.length === 0 ? message : "At path: " + path.join('.') + " -- " + message;
        super(msg);
        Object.assign(this, rest);
        this.name = this.constructor.name;
  
        this.failures = () => {
          if (!failures) {
            failures = [failure, ...moreFailures];
          }
  
          return failures;
        };
      }
  
    }
  
    /**
     * Check if a value is a plain object.
     */
    function isPlainObject(value) {
      if (Object.prototype.toString.call(value) !== '[object Object]') {
        return false;
      }
  
      const prototype = Object.getPrototypeOf(value);
      return prototype === null || prototype === Object.prototype;
    }
    /**
     * Return a value as a printable string.
     */
  
    function print(value) {
      return typeof value === 'string' ? JSON.stringify(value) : "" + value;
    }
    /**
     * Shifts (removes and returns) the first value from the `input` iterator.
     * Like `Array.prototype.shift()` but for an `Iterator`.
     */
  
    function shiftIterator(input) {
      const {
        done,
        value
      } = input.next();
      return done ? undefined : value;
    }
    /**
     * Convert a validation result to an iterable of failures.
     */
  
    function* toFailures(result, context) {
      if (typeof result === 'string') {
        yield context.fail({
          message: result
        });
      } else if (result === true) {
        return;
      } else if (result === false) {
        yield context.fail();
      } else {
        yield* result;
      }
    }
  
    function assign(...Structs) {
      const schemas = Structs.map(s => s.schema);
      const schema = Object.assign({}, ...schemas);
      return object(schema);
    }
    /**
     * Define a new struct type with a custom validation function.
     */
  
    function define(name, validator) {
      return new Struct({
        type: name,
        schema: null,
        validator
      });
    }
    /**
     * Create a struct with dynamic, runtime validation.
     *
     * The callback will receive the value currently being validated, and must
     * return a struct object to validate it with. This can be useful to model
     * validation logic that changes based on its input.
     */
  
    function dynamic(fn) {
      return define('dynamic', (value, ctx) => {
        return ctx.check(value, fn(value, ctx));
      });
    }
    /**
     * Create a struct with lazily evaluated validation.
     *
     * The first time validation is run with the struct, the callback will be called
     * and must return a struct object to use. This is useful for cases where you
     * want to have self-referential structs for nested data structures to avoid a
     * circular definition problem.
     */
  
    function lazy(fn) {
      let s;
      return define('lazy', (value, ctx) => {
        if (!s) {
          s = fn();
        }
  
        return ctx.check(value, s);
      });
    }
    /**
     * Create a new struct based on an existing object struct, but excluding
     * specific properties.
     *
     * Like TypeScript's `Omit` utility.
     */
  
    function omit(struct, keys) {
      const {
        schema
      } = struct;
      const subschema = { ...schema
      };
  
      for (const key of keys) {
        delete subschema[key];
      }
  
      return object(subschema);
    }
    /**
     * Create a new struct based on an existing object struct, but with all of its
     * properties allowed to be `undefined`.
     *
     * Like TypeScript's `Partial` utility.
     */
  
    function partial(struct) {
      const schema = struct instanceof Struct ? { ...struct.schema
      } : { ...struct
      };
  
      for (const key in schema) {
        schema[key] = optional(schema[key]);
      }
  
      return object(schema);
    }
    /**
     * Create a new struct based on an existing object struct, but only including
     * specific properties.
     *
     * Like TypeScript's `Pick` utility.
     */
  
    function pick(struct, keys) {
      const {
        schema
      } = struct;
      const subschema = {};
  
      for (const key of keys) {
        subschema[key] = schema[key];
      }
  
      return object(subschema);
    }
    /**
     * Define a new struct type with a custom validation function.
     *
     * @deprecated This function has been renamed to `define`.
     */
  
    function struct(name, validator) {
      console.warn('superstruct@0.11 - The `struct` helper has been renamed to `define`.');
      return define(name, validator);
    }
  
    /**
     * Ensure that any value passes validation.
     */
  
    function any() {
      return define('any', () => true);
    }
    function array(Element) {
      return new Struct({
        type: 'array',
        schema: Element,
        coercer: value => {
          return Element && Array.isArray(value) ? value.map(v => Element.coercer(v)) : value;
        },
  
        *validator(value, ctx) {
          if (!Array.isArray(value)) {
            yield ctx.fail("Expected an array value, but received: " + print(value));
          } else if (Element) {
            for (const [i, v] of value.entries()) {
              yield* ctx.check(v, Element, value, i);
            }
          }
        }
  
      });
    }
    /**
     * Ensure that a value is a boolean.
     */
  
    function boolean() {
      return define('boolean', value => {
        return typeof value === 'boolean';
      });
    }
    /**
     * Ensure that a value is a valid `Date`.
     *
     * Note: this also ensures that the value is *not* an invalid `Date` object,
     * which can occur when parsing a date fails but still returns a `Date`.
     */
  
    function date() {
      return define('date', value => {
        return value instanceof Date && !isNaN(value.getTime()) || "Expected a valid `Date` object, but received: " + print(value);
      });
    }
    function enums(values) {
      const schema = {};
      const description = values.map(v => print(v)).join();
  
      for (const key of values) {
        schema[key] = key;
      }
  
      return new Struct({
        type: 'enums',
        schema,
        validator: value => {
          return values.includes(value) || "Expected one of `" + description + "`, but received: " + print(value);
        }
      });
    }
    /**
     * Ensure that a value is a function.
     */
  
    function func() {
      return define('func', value => {
        return typeof value === 'function' || "Expected a function, but received: " + print(value);
      });
    }
    /**
     * Ensure that a value is an instance of a specific class.
     */
  
    function instance(Class) {
      return define('instance', value => {
        return value instanceof Class || "Expected a `" + Class.name + "` instance, but received: " + print(value);
      });
    }
    /**
     * Ensure that a value is an integer.
     */
  
    function integer() {
      return define('integer', value => {
        return typeof value === 'number' && !isNaN(value) && Number.isInteger(value) || "Expected an integer, but received: " + print(value);
      });
    }
    function intersection(Structs) {
      return define('intersection', function* (value, ctx) {
        for (const S of Structs) {
          yield* ctx.check(value, S);
        }
      });
    }
    function literal(constant) {
      const description = print(constant);
      return define('literal', value => {
        return value === constant || "Expected the literal `" + description + "`, but received: " + print(value);
      });
    }
    function map(Key, Value) {
      return define('map', function* (value, ctx) {
        if (!(value instanceof Map)) {
          yield ctx.fail("Expected a `Map` object, but received: " + print(value));
        } else if (Key && Value) {
          for (const [k, v] of value.entries()) {
            yield* ctx.check(k, Key, value, k);
            yield* ctx.check(v, Value, value, k);
          }
        }
      });
    }
    /**
     * Ensure that no value ever passes validation.
     */
  
    function never() {
      return define('never', () => false);
    }
    /**
     * Augment an existing struct to allow `null` values.
     */
  
    function nullable(struct) {
      const {
        refiner
      } = struct;
      return new Struct({ ...struct,
        validator: (value, ctx) => {
          return value === null || ctx.check(value, struct);
        },
        refiner: function* (value, ctx) {
          if (value != null) {
            const c = { ...ctx,
              struct
            };
            yield* toFailures(refiner(value, c), c);
          }
        }
      });
    }
    /**
     * Ensure that a value is a number.
     */
  
    function number() {
      return define('number', value => {
        return typeof value === 'number' && !isNaN(value) || "Expected a number, but received: " + print(value);
      });
    }
    function object(schema) {
      const knowns = schema ? Object.keys(schema) : [];
      const Never = never();
      return new Struct({
        type: 'object',
        schema: schema ? schema : null,
  
        *validator(value, ctx) {
          if (typeof value !== 'object' || value == null) {
            yield ctx.fail("Expected an object, but received: " + print(value));
          } else if (schema) {
            const unknowns = new Set(Object.keys(value));
  
            for (const key of knowns) {
              unknowns.delete(key);
              const Value = schema[key];
              const v = value[key];
              yield* ctx.check(v, Value, value, key);
            }
  
            for (const key of unknowns) {
              const v = value[key];
              yield* ctx.check(v, Never, value, key);
            }
          }
        },
  
        coercer: value => {
          if (!schema || typeof value !== 'object' || value == null) {
            return value;
          }
  
          const ret = {};
          const unknowns = new Set(Object.keys(value));
  
          for (const key of knowns) {
            unknowns.delete(key);
            const Value = schema[key];
            const v = value[key];
            ret[key] = Value.coercer(v);
          }
  
          for (const key of unknowns) {
            ret[key] = value[key];
          }
  
          return ret;
        }
      });
    }
    /**
     * Augment a struct to allow `undefined` values.
     */
  
    function optional(struct) {
      const {
        refiner
      } = struct;
      return new Struct({ ...struct,
        validator: (value, ctx) => {
          return value === undefined || ctx.check(value, struct);
        },
        refiner: function* (value, ctx) {
          if (value != null) {
            const c = { ...ctx,
              struct
            };
            yield* toFailures(refiner(value, c), c);
          }
        }
      });
    }
    /**
     * Ensure that a value is an object with keys and values of specific types, but
     * without ensuring any specific shape of properties.
     *
     * Like TypeScript's `Record` utility.
     */
  
    function record(Key, Value) {
      return define('record', function* (value, ctx) {
        if (typeof value !== 'object' || value == null) {
          yield ctx.fail("Expected an object, but received: " + print(value));
        } else {
          for (const k in value) {
            const v = value[k];
            yield* ctx.check(k, Key, value, k);
            yield* ctx.check(v, Value, value, k);
          }
        }
      });
    }
    /**
     * Ensure that a value is a `RegExp`.
     *
     * Note: this does not test the value against the regular expression! For that
     * you need to use the `pattern()` refinement.
     */
  
    function regexp() {
      return define('regexp', value => {
        return value instanceof RegExp;
      });
    }
    function set(Element) {
      return define('set', function* (value, ctx) {
        if (!(value instanceof Set)) {
          yield ctx.fail("Expected a `Set` object, but received: " + print(value));
        } else if (Element) {
          for (const val of value) {
            yield* ctx.check(val, Element, value, val);
          }
        }
      });
    }
    /**
     * Ensure that a value is a string.
     */
  
    function string() {
      return define('string', value => {
        return typeof value === 'string' || "Expected a string, but received: " + print(value);
      });
    }
    function tuple(Elements) {
      const Never = never();
      return define('tuple', function* (value, ctx) {
        if (!Array.isArray(value)) {
          yield ctx.fail("Expected an array, but received: " + print(value));
        } else {
          for (const [index, Element] of Elements.entries()) {
            const v = value[index];
            yield* ctx.check(v, Element, value, index);
          }
  
          if (value.length > Elements.length) {
            const index = Elements.length;
            const v = value[index];
            yield* ctx.check(v, Never, value, index);
          }
        }
      });
    }
    /**
     * Ensure that a value has a set of known properties of specific types.
     *
     * Note: Unrecognized properties are allowed and untouched. This is similar to
     * how TypeScript's structural typing works.
     */
  
    function type(schema) {
      const keys = Object.keys(schema);
      return new Struct({
        type: 'type',
        schema,
        validator: function* (value, ctx) {
          if (typeof value !== 'object' || value == null) {
            yield ctx.fail("Expected an object, but received: " + print(value));
          } else {
            for (const key of keys) {
              const Value = schema[key];
              const v = value[key];
              yield* ctx.check(v, Value, value, key);
            }
          }
        }
      });
    }
    function union(Structs) {
      const description = Structs.map(s => s.type).join(' | ');
      return define('union', function* (value, ctx) {
        const failures = [];
  
        for (const S of Structs) {
          const [...array] = ctx.check(value, S);
  
          if (array.length === 0) {
            return;
          } else {
            failures.push(...array);
          }
        }
  
        yield ctx.fail("Expected the value to satisfy a union of `" + description + "`, but received: " + print(value));
        yield* failures;
      });
    }
    /**
     * Ensure that any value passes validation, without widening its type to `any`.
     */
  
    function unknown() {
      return define('unknown', () => true);
    }
  
    /**
     * Augment a `Struct` to add an additional coercion step to its input.
     *
     * This allows you to transform input data before validating it, to increase the
     * likelihood that it passes validation—for example for default values, parsing
     * different formats, etc.
     *
     * Note: You must use `create(value, Struct)` on the value to have the coercion
     * take effect! Using simply `assert()` or `is()` will not use coercion.
     */
  
    function coerce(struct, condition, coercer) {
      const fn = struct.coercer;
      return new Struct({ ...struct,
        coercer: value => {
          if (is(value, condition)) {
            return fn(coercer(value));
          } else {
            return fn(value);
          }
        }
      });
    }
    /**
     * Augment a struct to replace `undefined` values with a default.
     *
     * Note: You must use `create(value, Struct)` on the value to have the coercion
     * take effect! Using simply `assert()` or `is()` will not use coercion.
     */
  
    function defaulted(S, fallback, options = {}) {
      const {
        strict
      } = options;
      return coerce(S, unknown(), x => {
        const f = typeof fallback === 'function' ? fallback() : fallback;
  
        if (x === undefined) {
          return f;
        }
  
        if (!strict && isPlainObject(x) && isPlainObject(f)) {
          const ret = { ...x
          };
          let changed = false;
  
          for (const key in f) {
            if (ret[key] === undefined) {
              ret[key] = f[key];
              changed = true;
            }
          }
  
          if (changed) {
            return ret;
          }
        }
  
        return x;
      });
    }
    /**
     * Augment a struct to mask its input to only properties defined in the struct.
     *
     * Note: You must use `create(value, Struct)` on the value to have the coercion
     * take effect! Using simply `assert()` or `is()` will not use coercion.
     */
  
    function masked(struct) {
      return coerce(struct, unknown(), x => {
        if (typeof struct.schema !== 'object' || struct.schema == null || typeof x !== 'object' || x == null) {
          return x;
        } else {
          const ret = {};
  
          for (const key in struct.schema) {
            if (key in x) {
              ret[key] = x[key];
            }
          }
  
          return ret;
        }
      });
    }
    /**
     * Augment a struct to trim string inputs.
     *
     * Note: You must use `create(value, Struct)` on the value to have the coercion
     * take effect! Using simply `assert()` or `is()` will not use coercion.
     */
  
    function trimmed(struct) {
      return coerce(struct, string(), x => x.trim());
    }
  
    /**
     * `Struct` objects encapsulate the validation logic for a specific type of
     * values. Once constructed, you use the `assert`, `is` or `validate` helpers to
     * validate unknown input data against the struct.
     */
  
    class Struct {
      constructor(props) {
        this.type = props.type;
        this.schema = props.schema;
  
        this.coercer = props.coercer || (value => value);
  
        this.validator = props.validator || (() => []);
  
        this.refiner = props.refiner || (() => []);
      }
      /**
       * Assert that a value passes the struct's validation, throwing if it doesn't.
       */
  
  
      assert(value) {
        return assert(value, this);
      }
      /**
       * Create a value with the struct's coercion logic, then validate it.
       */
  
  
      create(value) {
        return create(value, this);
      }
      /**
       * Check if a value passes the struct's validation.
       */
  
  
      is(value) {
        return is(value, this);
      }
      /**
       * Mask a value, coercing and validating it, but returning only the subset of
       * properties defined by the struct's schema.
       */
  
  
      mask(value) {
        return mask(value, this);
      }
      /**
       * Validate a value with the struct's validation logic, returning a tuple
       * representing the result.
       *
       * You may optionally pass `true` for the `withCoercion` argument to coerce
       * the value before attempting to validate it. If you do, the result will
       * contain the coerced result when successful.
       */
  
  
      validate(value, options = {}) {
        return validate(value, this, options);
      }
  
    }
    /**
     * Assert that a value passes a `Struct`, throwing if it doesn't.
     */
  
    function assert(value, struct) {
      const result = validate(value, struct);
  
      if (result[0]) {
        throw result[0];
      }
    }
    /**
     * Create a value with the coercion logic of `Struct` and validate it.
     */
  
    function create(value, struct) {
      const ret = struct.coercer(value);
      assert(ret, struct);
      return ret;
    }
    /**
     * Mask a value, returning only the subset of properties defined by a Struct.
     */
  
    function mask(value, struct) {
      const M = masked(struct);
      const ret = create(value, M);
      return ret;
    }
    /**
     * Check if a value passes a `Struct`.
     */
  
    function is(value, struct) {
      const result = validate(value, struct);
      return !result[0];
    }
    /**
     * Validate a value against a `Struct`, returning an error if invalid.
     */
  
    function validate(value, struct, options = {}) {
      if (options.coerce) {
        value = struct.coercer(value);
      }
  
      const failures = check(value, struct);
      const failure = shiftIterator(failures);
  
      if (failure) {
        const error = new StructError(failure, failures);
        return [error, undefined];
      } else {
        return [undefined, value];
      }
    }
    /**
     * Check a value against a `Struct`, returning an iterable of failures.
     */
  
    function* check(value, struct, path = [], branch = []) {
      const ctx = {
        value,
        struct,
        branch,
        path,
  
        check(v, s, parent, key) {
          const p = parent !== undefined ? [...path, key] : path;
          const b = parent !== undefined ? [...branch, parent] : branch;
          return check(v, s, p, b);
        },
  
        fail(props = {}) {
          if (typeof props === 'string') {
            props = {
              message: props
            };
          }
  
          const {
            type
          } = struct;
          let {
            message,
            refinement
          } = props;
  
          if (!message) {
            message = "Expected a value of type `" + type + "`" + (refinement ? " with refinement `" + refinement + "`" : '') + (path.length ? " for `" + path.join('.') + "`" : '') + ", but received: `" + print(value) + "`";
          }
  
          return { ...props,
            value,
            type,
            refinement,
            message,
            key: path[path.length - 1],
            path,
            branch: [...branch, value]
          };
        }
  
      };
      const failures = toFailures(struct.validator(value, ctx), ctx);
      const failure = shiftIterator(failures);
  
      if (failure) {
        yield failure;
        yield* failures;
      } else {
        yield* toFailures(struct.refiner(value, ctx), ctx);
      }
    }
  
    /**
     * Ensure that a string, array, map, or set is empty.
     */
  
    function empty(struct) {
      const expected = "Expected an empty " + struct.type;
      return refine(struct, 'empty', value => {
        if (value instanceof Map || value instanceof Set) {
          const {
            size
          } = value;
          return size === 0 || expected + " but received one with a size of `" + size + "`";
        } else {
          const {
            length
          } = value;
          return length === 0 || expected + " but received one with a length of `" + length + "`";
        }
      });
    }
    /**
     * Ensure that a number or date is below a threshold.
     */
  
    function max(struct, threshold, options = {}) {
      const {
        exclusive
      } = options;
      return refine(struct, 'max', value => {
        return exclusive ? value < threshold : value <= threshold || "Expected a " + struct.type + " greater than " + (exclusive ? '' : 'or equal to ') + threshold + " but received `" + value + "`";
      });
    }
    /**
     * Ensure that a number or date is above a threshold.
     */
  
    function min(struct, threshold, options = {}) {
      const {
        exclusive
      } = options;
      return refine(struct, 'min', value => {
        return exclusive ? value > threshold : value >= threshold || "Expected a " + struct.type + " greater than " + (exclusive ? '' : 'or equal to ') + threshold + " but received `" + value + "`";
      });
    }
    /**
     * Ensure that a string matches a regular expression.
     */
  
    function pattern(struct, regexp) {
      return refine(struct, 'pattern', value => {
        return regexp.test(value) || "Expected a " + struct.type + " matching `/" + regexp.source + "/` but received \"" + value + "\"";
      });
    }
    /**
     * Ensure that a string, array, number, date, map, or set has a size (or length, or time) between `min` and `max`.
     */
  
    function size(struct, min, max = min) {
      const expected = "Expected a " + struct.type;
      const of = min === max ? "of `" + min + "`" : "between `" + min + "` and `" + max + "`";
      return refine(struct, 'size', value => {
        if (typeof value === 'number' || value instanceof Date) {
          return min <= value && value <= max || expected + " " + of + " but received `" + value + "`";
        } else if (value instanceof Map || value instanceof Set) {
          const {
            size
          } = value;
          return min <= size && size <= max || expected + " with a size " + of + " but received one with a size of `" + size + "`";
        } else {
          const {
            length
          } = value;
          return min <= length && length <= max || expected + " with a length " + of + " but received one with a length of `" + length + "`";
        }
      });
    }
    /**
     * Augment a `Struct` to add an additional refinement to the validation.
     *
     * The refiner function is guaranteed to receive a value of the struct's type,
     * because the struct's existing validation will already have passed. This
     * allows you to layer additional validation on top of existing structs.
     */
  
    function refine(struct, name, refiner) {
      const fn = struct.refiner;
      return new Struct({ ...struct,
  
        *refiner(value, ctx) {
          yield* toFailures(fn(value, ctx), ctx);
  
          for (const failure of toFailures(refiner(value, ctx), ctx)) {
            yield { ...failure,
              refinement: name
            };
          }
        }
  
      });
    }
  
    exports.Struct = Struct;
    exports.StructError = StructError;
    exports.any = any;
    exports.array = array;
    exports.assert = assert;
    exports.assign = assign;
    exports.boolean = boolean;
    exports.coerce = coerce;
    exports.create = create;
    exports.date = date;
    exports.defaulted = defaulted;
    exports.define = define;
    exports.dynamic = dynamic;
    exports.empty = empty;
    exports.enums = enums;
    exports.func = func;
    exports.instance = instance;
    exports.integer = integer;
    exports.intersection = intersection;
    exports.is = is;
    exports.lazy = lazy;
    exports.literal = literal;
    exports.map = map;
    exports.mask = mask;
    exports.masked = masked;
    exports.max = max;
    exports.min = min;
    exports.never = never;
    exports.nullable = nullable;
    exports.number = number;
    exports.object = object;
    exports.omit = omit;
    exports.optional = optional;
    exports.partial = partial;
    exports.pattern = pattern;
    exports.pick = pick;
    exports.record = record;
    exports.refine = refine;
    exports.regexp = regexp;
    exports.set = set;
    exports.size = size;
    exports.string = string;
    exports.struct = struct;
    exports.trimmed = trimmed;
    exports.tuple = tuple;
    exports.type = type;
    exports.union = union;
    exports.unknown = unknown;
    exports.validate = validate;
  
    Object.defineProperty(exports, '__esModule', { value: true });
  
  })));
  