(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, failures) {
            let cached;
            const { message, explanation, ...rest } = failure;
            const { path } = failure;
            const msg = path.length === 0 ? message : `At path: ${path.join('.')} -- ${message}`;
            super(explanation ?? msg);
            if (explanation != null)
                this.cause = msg;
            Object.assign(this, rest);
            this.name = this.constructor.name;
            this.failures = () => {
                return (cached ?? (cached = [failure, ...failures()]));
            };
        }
    }

    /**
     * Check if a value is an iterator.
     */
    function isIterable(x) {
        return isObject(x) && typeof x[Symbol.iterator] === 'function';
    }
    /**
     * Check if a value is a plain object.
     */
    function isObject(x) {
        return typeof x === 'object' && x != null;
    }
    /**
     * Check if a value is a non-array object.
     */
    function isNonArrayObject(x) {
        return isObject(x) && !Array.isArray(x);
    }
    /**
     * Check if a value is a plain object.
     */
    function isPlainObject(x) {
        if (Object.prototype.toString.call(x) !== '[object Object]') {
            return false;
        }
        const prototype = Object.getPrototypeOf(x);
        return prototype === null || prototype === Object.prototype;
    }
    /**
     * Return a value as a printable string.
     */
    function print(value) {
        if (typeof value === 'symbol') {
            return value.toString();
        }
        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 single validation result to a failure.
     */
    function toFailure(result, context, struct, value) {
        if (result === true) {
            return;
        }
        else if (result === false) {
            result = {};
        }
        else if (typeof result === 'string') {
            result = { message: result };
        }
        const { path, branch } = context;
        const { type } = struct;
        const { refinement, message = `Expected a value of type \`${type}\`${refinement ? ` with refinement \`${refinement}\`` : ''}, but received: \`${print(value)}\``, } = result;
        return {
            value,
            type,
            refinement,
            key: path[path.length - 1],
            path,
            branch,
            ...result,
            message,
        };
    }
    /**
     * Convert a validation result to an iterable of failures.
     */
    function* toFailures(result, context, struct, value) {
        if (!isIterable(result)) {
            result = [result];
        }
        for (const r of result) {
            const failure = toFailure(r, context, struct, value);
            if (failure) {
                yield failure;
            }
        }
    }
    /**
     * Check a value against a struct, traversing deeply into nested values, and
     * returning an iterator of failures or success.
     */
    function* run(value, struct, options = {}) {
        const { path = [], branch = [value], coerce = false, mask = false } = options;
        const ctx = { path, branch, mask };
        if (coerce) {
            value = struct.coercer(value, ctx);
        }
        let status = 'valid';
        for (const failure of struct.validator(value, ctx)) {
            failure.explanation = options.message;
            status = 'not_valid';
            yield [failure, undefined];
        }
        for (let [k, v, s] of struct.entries(value, ctx)) {
            const ts = run(v, s, {
                path: k === undefined ? path : [...path, k],
                branch: k === undefined ? branch : [...branch, v],
                coerce,
                mask,
                message: options.message,
            });
            for (const t of ts) {
                if (t[0]) {
                    status = t[0].refinement != null ? 'not_refined' : 'not_valid';
                    yield [t[0], undefined];
                }
                else if (coerce) {
                    v = t[1];
                    if (k === undefined) {
                        value = v;
                    }
                    else if (value instanceof Map) {
                        value.set(k, v);
                    }
                    else if (value instanceof Set) {
                        value.add(v);
                    }
                    else if (isObject(value)) {
                        if (v !== undefined || k in value)
                            value[k] = v;
                    }
                }
            }
        }
        if (status !== 'not_valid') {
            for (const failure of struct.refiner(value, ctx)) {
                failure.explanation = options.message;
                status = 'not_refined';
                yield [failure, undefined];
            }
        }
        if (status === 'valid') {
            yield [undefined, value];
        }
    }

    /**
     * `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) {
            const { type, schema, validator, refiner, coercer = (value) => value, entries = function* () { }, } = props;
            this.type = type;
            this.schema = schema;
            this.entries = entries;
            this.coercer = coercer;
            if (validator) {
                this.validator = (value, context) => {
                    const result = validator(value, context);
                    return toFailures(result, context, this, value);
                };
            }
            else {
                this.validator = () => [];
            }
            if (refiner) {
                this.refiner = (value, context) => {
                    const result = refiner(value, context);
                    return toFailures(result, context, this, value);
                };
            }
            else {
                this.refiner = () => [];
            }
        }
        /**
         * Assert that a value passes the struct's validation, throwing if it doesn't.
         */
        assert(value, message) {
            return assert(value, this, message);
        }
        /**
         * Create a value with the struct's coercion logic, then validate it.
         */
        create(value, message) {
            return create(value, this, message);
        }
        /**
         * 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. Masking applies recursively to
         * props of `object` structs only.
         */
        mask(value, message) {
            return mask(value, this, message);
        }
        /**
         * Validate a value with the struct's validation logic, returning a tuple
         * representing the result.
         *
         * You may optionally pass `true` for the `coerce` argument to coerce
         * the value before attempting to validate it. If you do, the result will
         * contain the coerced result when successful. Also, `mask` will turn on
         * masking of the unknown `object` props recursively if passed.
         */
        validate(value, options = {}) {
            return validate(value, this, options);
        }
    }
    /**
     * Assert that a value passes a struct, throwing if it doesn't.
     */
    function assert(value, struct, message) {
        const result = validate(value, struct, { message });
        if (result[0]) {
            throw result[0];
        }
    }
    /**
     * Create a value with the coercion logic of struct and validate it.
     */
    function create(value, struct, message) {
        const result = validate(value, struct, { coerce: true, message });
        if (result[0]) {
            throw result[0];
        }
        else {
            return result[1];
        }
    }
    /**
     * Mask a value, returning only the subset of properties defined by a struct.
     */
    function mask(value, struct, message) {
        const result = validate(value, struct, { coerce: true, mask: true, message });
        if (result[0]) {
            throw result[0];
        }
        else {
            return result[1];
        }
    }
    /**
     * 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, or the
     * value (with potential coercion) if valid.
     */
    function validate(value, struct, options = {}) {
        const tuples = run(value, struct, options);
        const tuple = shiftIterator(tuples);
        if (tuple[0]) {
            const error = new StructError(tuple[0], function* () {
                for (const t of tuples) {
                    if (t[0]) {
                        yield t[0];
                    }
                }
            });
            return [error, undefined];
        }
        else {
            const v = tuple[1];
            return [undefined, v];
        }
    }

    function assign(...Structs) {
        const isType = Structs[0].type === 'type';
        const schemas = Structs.map((s) => s.schema);
        const schema = Object.assign({}, ...schemas);
        return isType ? type(schema) : 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 new struct based on an existing struct, but the value is allowed to
     * be `undefined`. `log` will be called if the value is not `undefined`.
     */
    function deprecated(struct, log) {
        return new Struct({
            ...struct,
            refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx),
            validator(value, ctx) {
                if (value === undefined) {
                    return true;
                }
                else {
                    log(value, ctx);
                    return struct.validator(value, ctx);
                }
            },
        });
    }
    /**
     * Create a struct with dynamic validation logic.
     *
     * 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 new Struct({
            type: 'dynamic',
            schema: null,
            *entries(value, ctx) {
                const struct = fn(value, ctx);
                yield* struct.entries(value, ctx);
            },
            validator(value, ctx) {
                const struct = fn(value, ctx);
                return struct.validator(value, ctx);
            },
            coercer(value, ctx) {
                const struct = fn(value, ctx);
                return struct.coercer(value, ctx);
            },
            refiner(value, ctx) {
                const struct = fn(value, ctx);
                return struct.refiner(value, ctx);
            },
        });
    }
    /**
     * Create a struct with lazily evaluated validation logic.
     *
     * 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 struct;
        return new Struct({
            type: 'lazy',
            schema: null,
            *entries(value, ctx) {
                struct ?? (struct = fn());
                yield* struct.entries(value, ctx);
            },
            validator(value, ctx) {
                struct ?? (struct = fn());
                return struct.validator(value, ctx);
            },
            coercer(value, ctx) {
                struct ?? (struct = fn());
                return struct.coercer(value, ctx);
            },
            refiner(value, ctx) {
                struct ?? (struct = fn());
                return struct.refiner(value, ctx);
            },
        });
    }
    /**
     * 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];
        }
        switch (struct.type) {
            case 'type':
                return type(subschema);
            default:
                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 isStruct = struct instanceof Struct;
        const schema = isStruct ? { ...struct.schema } : { ...struct };
        for (const key in schema) {
            schema[key] = optional(schema[key]);
        }
        if (isStruct && struct.type === 'type') {
            return type(schema);
        }
        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];
        }
        switch (struct.type) {
            case 'type':
                return type(subschema);
            default:
                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,
            *entries(value) {
                if (Element && Array.isArray(value)) {
                    for (const [i, v] of value.entries()) {
                        yield [i, v, Element];
                    }
                }
            },
            coercer(value) {
                return Array.isArray(value) ? value.slice() : value;
            },
            validator(value) {
                return (Array.isArray(value) ||
                    `Expected an array value, but received: ${print(value)}`);
            },
        });
    }
    /**
     * Ensure that a value is a bigint.
     */
    function bigint() {
        return define('bigint', (value) => {
            return typeof value === 'bigint';
        });
    }
    /**
     * 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)}`);
        });
    }
    /**
     * Ensure that a value matches all of a set of types.
     */
    function intersection(Structs) {
        return new Struct({
            type: 'intersection',
            schema: null,
            *entries(value, ctx) {
                for (const S of Structs) {
                    yield* S.entries(value, ctx);
                }
            },
            *validator(value, ctx) {
                for (const S of Structs) {
                    yield* S.validator(value, ctx);
                }
            },
            *refiner(value, ctx) {
                for (const S of Structs) {
                    yield* S.refiner(value, ctx);
                }
            },
        });
    }
    function literal(constant) {
        const description = print(constant);
        const t = typeof constant;
        return new Struct({
            type: 'literal',
            schema: t === 'string' || t === 'number' || t === 'boolean' ? constant : null,
            validator(value) {
                return (value === constant ||
                    `Expected the literal \`${description}\`, but received: ${print(value)}`);
            },
        });
    }
    function map(Key, Value) {
        return new Struct({
            type: 'map',
            schema: null,
            *entries(value) {
                if (Key && Value && value instanceof Map) {
                    for (const [k, v] of value.entries()) {
                        yield [k, k, Key];
                        yield [k, v, Value];
                    }
                }
            },
            coercer(value) {
                return value instanceof Map ? new Map(value) : value;
            },
            validator(value) {
                return (value instanceof Map ||
                    `Expected a \`Map\` object, but received: ${print(value)}`);
            },
        });
    }
    /**
     * Ensure that no value ever passes validation.
     */
    function never() {
        return define('never', () => false);
    }
    /**
     * Augment an existing struct to allow `null` values.
     */
    function nullable(struct) {
        return new Struct({
            ...struct,
            validator: (value, ctx) => value === null || struct.validator(value, ctx),
            refiner: (value, ctx) => value === null || struct.refiner(value, ctx),
        });
    }
    /**
     * 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,
            *entries(value) {
                if (schema && isObject(value)) {
                    const unknowns = new Set(Object.keys(value));
                    for (const key of knowns) {
                        unknowns.delete(key);
                        yield [key, value[key], schema[key]];
                    }
                    for (const key of unknowns) {
                        yield [key, value[key], Never];
                    }
                }
            },
            validator(value) {
                return (isNonArrayObject(value) ||
                    `Expected an object, but received: ${print(value)}`);
            },
            coercer(value, ctx) {
                if (!isNonArrayObject(value)) {
                    return value;
                }
                const coerced = { ...value };
                // The `object` struct has special behaviour enabled by the mask flag.
                // When masking, properties that are not in the schema are deleted from
                // the coerced object instead of eventually failing validaiton.
                if (ctx.mask && schema) {
                    for (const key in coerced) {
                        if (schema[key] === undefined) {
                            delete coerced[key];
                        }
                    }
                }
                return coerced;
            },
        });
    }
    /**
     * Augment a struct to allow `undefined` values.
     */
    function optional(struct) {
        return new Struct({
            ...struct,
            validator: (value, ctx) => value === undefined || struct.validator(value, ctx),
            refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx),
        });
    }
    /**
     * 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 new Struct({
            type: 'record',
            schema: null,
            *entries(value) {
                if (isObject(value)) {
                    for (const k in value) {
                        const v = value[k];
                        yield [k, k, Key];
                        yield [k, v, Value];
                    }
                }
            },
            validator(value) {
                return (isNonArrayObject(value) ||
                    `Expected an object, but received: ${print(value)}`);
            },
            coercer(value) {
                return isNonArrayObject(value) ? { ...value } : value;
            },
        });
    }
    /**
     * 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 new Struct({
            type: 'set',
            schema: null,
            *entries(value) {
                if (Element && value instanceof Set) {
                    for (const v of value) {
                        yield [v, v, Element];
                    }
                }
            },
            coercer(value) {
                return value instanceof Set ? new Set(value) : value;
            },
            validator(value) {
                return (value instanceof Set ||
                    `Expected a \`Set\` object, but received: ${print(value)}`);
            },
        });
    }
    /**
     * Ensure that a value is a string.
     */
    function string() {
        return define('string', (value) => {
            return (typeof value === 'string' ||
                `Expected a string, but received: ${print(value)}`);
        });
    }
    /**
     * Ensure that a value is a tuple of a specific length, and that each of its
     * elements is of a specific type.
     */
    function tuple(Structs) {
        const Never = never();
        return new Struct({
            type: 'tuple',
            schema: null,
            *entries(value) {
                if (Array.isArray(value)) {
                    const length = Math.max(Structs.length, value.length);
                    for (let i = 0; i < length; i++) {
                        yield [i, value[i], Structs[i] || Never];
                    }
                }
            },
            validator(value) {
                return (Array.isArray(value) ||
                    `Expected an array, but received: ${print(value)}`);
            },
            coercer(value) {
                return Array.isArray(value) ? value.slice() : value;
            },
        });
    }
    /**
     * 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,
            *entries(value) {
                if (isObject(value)) {
                    for (const k of keys) {
                        yield [k, value[k], schema[k]];
                    }
                }
            },
            validator(value) {
                return (isNonArrayObject(value) ||
                    `Expected an object, but received: ${print(value)}`);
            },
            coercer(value) {
                return isNonArrayObject(value) ? { ...value } : value;
            },
        });
    }
    /**
     * Ensure that a value matches one of a set of types.
     */
    function union(Structs) {
        const description = Structs.map((s) => s.type).join(' | ');
        return new Struct({
            type: 'union',
            schema: null,
            coercer(value, ctx) {
                for (const S of Structs) {
                    const [error, coerced] = S.validate(value, {
                        coerce: true,
                        mask: ctx.mask,
                    });
                    if (!error) {
                        return coerced;
                    }
                }
                return value;
            },
            validator(value, ctx) {
                const failures = [];
                for (const S of Structs) {
                    const [...tuples] = run(value, S, ctx);
                    const [first] = tuples;
                    if (!first[0]) {
                        return [];
                    }
                    else {
                        for (const [failure] of tuples) {
                            if (failure) {
                                failures.push(failure);
                            }
                        }
                    }
                }
                return [
                    `Expected the value to satisfy a union of \`${description}\`, but received: ${print(value)}`,
                    ...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) {
        return new Struct({
            ...struct,
            coercer: (value, ctx) => {
                return is(value, condition)
                    ? struct.coercer(coercer(value, ctx), ctx)
                    : struct.coercer(value, ctx);
            },
        });
    }
    /**
     * 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(struct, fallback, options = {}) {
        return coerce(struct, unknown(), (x) => {
            const f = typeof fallback === 'function' ? fallback() : fallback;
            if (x === undefined) {
                return f;
            }
            if (!options.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 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());
    }

    /**
     * Ensure that a string, array, map, or set is empty.
     */
    function empty(struct) {
        return refine(struct, 'empty', (value) => {
            const size = getSize(value);
            return (size === 0 ||
                `Expected an empty ${struct.type} but received one with a size of \`${size}\``);
        });
    }
    function getSize(value) {
        if (value instanceof Map || value instanceof Set) {
            return value.size;
        }
        else {
            return value.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} less 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, array, map or set is not empty.
     */
    function nonempty(struct) {
        return refine(struct, 'nonempty', (value) => {
            const size = getSize(value);
            return (size > 0 || `Expected a nonempty ${struct.type} but received an empty one`);
        });
    }
    /**
     * 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) {
        return new Struct({
            ...struct,
            *refiner(value, ctx) {
                yield* struct.refiner(value, ctx);
                const result = refiner(value, ctx);
                const failures = toFailures(result, ctx, struct, value);
                for (const failure of failures) {
                    yield { ...failure, refinement: name };
                }
            },
        });
    }

    exports.Struct = Struct;
    exports.StructError = StructError;
    exports.any = any;
    exports.array = array;
    exports.assert = assert;
    exports.assign = assign;
    exports.bigint = bigint;
    exports.boolean = boolean;
    exports.coerce = coerce;
    exports.create = create;
    exports.date = date;
    exports.defaulted = defaulted;
    exports.define = define;
    exports.deprecated = deprecated;
    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.max = max;
    exports.min = min;
    exports.never = never;
    exports.nonempty = nonempty;
    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;

}));
//# sourceMappingURL=index.cjs.map
